Work on new API. Locking system re-implemented
[idzebra-moved-to-github.git] / index / zsets.c
1 /*
2  * Copyright (C) 1994-2000, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zsets.c,v $
7  * Revision 1.32  2002-02-20 17:30:01  adam
8  * Work on new API. Locking system re-implemented
9  *
10  * Revision 1.31  2001/11/19 23:05:22  adam
11  * Added a few prototypes.
12  *
13  * Revision 1.30  2001/10/15 19:53:43  adam
14  * POSIX thread updates. First work on term sets.
15  *
16  * Revision 1.29  2001/01/22 10:42:56  adam
17  * Added numerical sort.
18  *
19  * Revision 1.28  2000/07/07 12:49:20  adam
20  * Optimized resultSetInsert{Rank,Sort}.
21  *
22  * Revision 1.27  2000/04/05 09:49:36  adam
23  * On Unix, zebra/z'mbol uses automake.
24  *
25  * Revision 1.26  2000/03/20 19:08:36  adam
26  * Added remote record import using Z39.50 extended services and Segment
27  * Requests.
28  *
29  * Revision 1.25  2000/03/15 15:00:31  adam
30  * First work on threaded version.
31  *
32  * Revision 1.24  1999/11/04 15:00:45  adam
33  * Implemented delete result set(s).
34  *
35  * Revision 1.23  1999/05/26 07:49:13  adam
36  * C++ compilation.
37  *
38  * Revision 1.22  1999/02/02 14:51:15  adam
39  * Updated WIN32 code specific sections. Changed header.
40  *
41  * Revision 1.21  1998/11/16 16:03:46  adam
42  * Moved loggin utilities to Yaz. Was implemented in file zlogs.c.
43  *
44  * Revision 1.20  1998/11/16 10:10:53  adam
45  * Fixed problem with zebraPosSetCreate that occurred when positions were
46  * less than 1.
47  *
48  * Revision 1.19  1998/09/22 10:48:22  adam
49  * Minor changes in search API.
50  *
51  * Revision 1.18  1998/09/22 10:03:45  adam
52  * Changed result sets to be persistent in the sense that they can
53  * be re-searched if needed.
54  * Fixed memory leak in rsm_or.
55  *
56  * Revision 1.17  1998/06/23 15:33:36  adam
57  * Added feature to specify sort criteria in query (type 7 specifies
58  * sort flags).
59  *
60  * Revision 1.16  1998/05/20 10:12:24  adam
61  * Implemented automatic EXPLAIN database maintenance.
62  * Modified Zebra to work with ASN.1 compiled version of YAZ.
63  *
64  * Revision 1.15  1998/03/05 08:45:14  adam
65  * New result set model and modular ranking system. Moved towards
66  * descent server API. System information stored as "SGML" records.
67  *
68  * Revision 1.14  1998/02/10 16:39:15  adam
69  * Minor change.
70  *
71  * Revision 1.13  1998/02/10 12:03:06  adam
72  * Implemented Sort.
73  *
74  * Revision 1.12  1997/09/25 14:57:36  adam
75  * Windows NT port.
76  *
77  * Revision 1.11  1996/12/23 15:30:46  adam
78  * Work on truncation.
79  * Bug fix: result sets weren't deleted after server shut down.
80  *
81  * Revision 1.10  1995/10/30 15:08:08  adam
82  * Bug fixes.
83  *
84  * Revision 1.9  1995/10/17  18:02:14  adam
85  * New feature: databases. Implemented as prefix to words in dictionary.
86  *
87  * Revision 1.8  1995/10/10  13:59:25  adam
88  * Function rset_open changed its wflag parameter to general flags.
89  *
90  * Revision 1.7  1995/10/06  14:38:01  adam
91  * New result set method: r_score.
92  * Local no (sysno) and score is transferred to retrieveCtrl.
93  *
94  * Revision 1.6  1995/09/28  09:19:49  adam
95  * xfree/xmalloc used everywhere.
96  * Extract/retrieve method seems to work for text records.
97  *
98  * Revision 1.5  1995/09/27  16:17:32  adam
99  * More work on retrieve.
100  *
101  * Revision 1.4  1995/09/07  13:58:36  adam
102  * New parameter: result-set file descriptor (RSFD) to support multiple
103  * positions within the same result-set.
104  * Boolean operators: and, or, not implemented.
105  * Result-set references.
106  *
107  * Revision 1.3  1995/09/06  16:11:19  adam
108  * Option: only one word key per file.
109  *
110  * Revision 1.2  1995/09/06  10:33:04  adam
111  * More work on present. Some log messages removed.
112  *
113  * Revision 1.1  1995/09/05  15:28:40  adam
114  * More work on search engine.
115  *
116  */
117 #include <stdio.h>
118 #include <assert.h>
119 #ifdef WIN32
120 #include <io.h>
121 #else
122 #include <unistd.h>
123 #endif
124
125 #include "zserver.h"
126 #include <rstemp.h>
127
128 #define SORT_IDX_ENTRYSIZE 64
129 #define ZSET_SORT_MAX_LEVEL 3
130
131 struct zebra_set_term_entry {
132     int reg_type;
133     char *db;
134     int set;
135     int use;
136     char *term;
137 };
138 struct zebra_set {
139     char *name;
140     RSET rset;
141     NMEM nmem;
142     int hits;
143     int num_bases;
144     char **basenames;
145     Z_RPNQuery *rpn;
146     struct zset_sort_info *sort_info;
147     struct zebra_set_term_entry *term_entries;
148     int term_entries_max;
149     struct zebra_set *next;
150     int locked;
151 };
152
153 struct zset_sort_entry {
154     int sysno;
155     int score;
156     char buf[ZSET_SORT_MAX_LEVEL][SORT_IDX_ENTRYSIZE];
157 };
158
159 struct zset_sort_info {
160     int max_entries;
161     int num_entries;
162     struct zset_sort_entry *all_entries;
163     struct zset_sort_entry **entries;
164 };
165
166 ZebraSet resultSetAddRPN (ZebraHandle zh, ODR input, ODR output,
167                           Z_RPNQuery *rpn, int num_bases, char **basenames, 
168                           const char *setname)
169 {
170     ZebraSet zebraSet;
171
172     zh->errCode = 0;
173     zh->errString = NULL;
174     zh->hits = 0;
175
176     zebraSet = resultSetAdd (zh, setname, 1);
177     if (!zebraSet)
178         return 0;
179     zebraSet->locked = 1;
180     zebraSet->rpn = 0;
181     zebraSet->num_bases = num_bases;
182     zebraSet->basenames = basenames;
183     zebraSet->nmem = odr_extract_mem (input);
184
185     zebraSet->rset = rpn_search (zh, output->mem, rpn,
186                                  zebraSet->num_bases,
187                                  zebraSet->basenames, zebraSet->name,
188                                  zebraSet);
189     zh->hits = zebraSet->hits;
190     if (zebraSet->rset)
191         zebraSet->rpn = rpn;
192     zebraSet->locked = 0;
193     return zebraSet;
194 }
195
196 void resultSetAddTerm (ZebraHandle zh, ZebraSet s, int reg_type,
197                        const char *db, int set,
198                        int use, const char *term)
199 {
200     if (!s->nmem)
201         s->nmem = nmem_create ();
202     if (!s->term_entries)
203     {
204         int i;
205         s->term_entries_max = 1000;
206         s->term_entries =
207             nmem_malloc (s->nmem, s->term_entries_max * 
208                          sizeof(*s->term_entries));
209         for (i = 0; i < s->term_entries_max; i++)
210             s->term_entries[i].term = 0;
211     }
212     if (s->hits < s->term_entries_max)
213     {
214         s->term_entries[s->hits].reg_type = reg_type;
215         s->term_entries[s->hits].db = nmem_strdup (s->nmem, db);
216         s->term_entries[s->hits].set = set;
217         s->term_entries[s->hits].use = use;
218         s->term_entries[s->hits].term = nmem_strdup (s->nmem, term);
219     }
220     (s->hits)++;
221 }
222
223 ZebraSet resultSetAdd (ZebraHandle zh, const char *name, int ov)
224 {
225     ZebraSet s;
226     int i;
227
228     for (s = zh->sets; s; s = s->next)
229         if (!strcmp (s->name, name))
230             break;
231     if (s)
232     {
233         logf (LOG_DEBUG, "updating result set %s", name);
234         if (!ov || s->locked)
235             return NULL;
236         if (s->rset)
237             rset_delete (s->rset);
238         if (s->nmem)
239             nmem_destroy (s->nmem);
240     }
241     else
242     {
243         logf (LOG_DEBUG, "adding result set %s", name);
244         s = (ZebraSet) xmalloc (sizeof(*s));
245         s->next = zh->sets;
246         zh->sets = s;
247         s->name = (char *) xmalloc (strlen(name)+1);
248         strcpy (s->name, name);
249
250         s->sort_info = (struct zset_sort_info *)
251             xmalloc (sizeof(*s->sort_info));
252         s->sort_info->max_entries = 1000;
253         s->sort_info->entries = (struct zset_sort_entry **)
254             xmalloc (sizeof(*s->sort_info->entries) *
255                      s->sort_info->max_entries);
256         s->sort_info->all_entries = (struct zset_sort_entry *)
257             xmalloc (sizeof(*s->sort_info->all_entries) *
258                      s->sort_info->max_entries);
259         for (i = 0; i < s->sort_info->max_entries; i++)
260             s->sort_info->entries[i] = s->sort_info->all_entries + i;
261     }
262     s->locked = 0;
263     s->term_entries = 0;
264     s->hits = 0;
265     s->rset = 0;
266     s->nmem = 0;
267     s->rpn = 0;
268     return s;
269 }
270
271 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
272 {
273     ZebraSet s;
274
275     for (s = zh->sets; s; s = s->next)
276         if (!strcmp (s->name, name))
277         {
278             if (!s->term_entries && !s->rset && s->rpn)
279             {
280                 NMEM nmem = nmem_create ();
281                 yaz_log (LOG_LOG, "research %s", name);
282                 s->rset =
283                     rpn_search (zh, nmem, s->rpn, s->num_bases,
284                                 s->basenames, s->name, s);
285                 nmem_destroy (nmem);
286             }
287             return s;
288         }
289     return NULL;
290 }
291
292 void resultSetInvalidate (ZebraHandle zh)
293 {
294     ZebraSet s = zh->sets;
295     
296     for (; s; s = s->next)
297     {
298         if (s->rset)
299             rset_delete (s->rset);
300         s->rset = 0;
301     }
302 }
303
304 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
305 {
306     ZebraSet * ss = &zh->sets;
307     int i;
308     
309     if (statuses)
310         for (i = 0; i<num; i++)
311             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
312     zh->errCode = 0;
313     zh->errString = NULL;
314     while (*ss)
315     {
316         int i = -1;
317         ZebraSet s = *ss;
318         if (num >= 0)
319         {
320             for (i = 0; i<num; i++)
321                 if (!strcmp (s->name, names[i]))
322                 {
323                     if (statuses)
324                         statuses[i] = Z_DeleteStatus_success;
325                     i = -1;
326                     break;
327                 }
328         }
329         if (i < 0)
330         {
331             *ss = s->next;
332             
333             xfree (s->sort_info->all_entries);
334             xfree (s->sort_info->entries);
335             xfree (s->sort_info);
336             
337             if (s->nmem)
338                 nmem_destroy (s->nmem);
339             if (s->rset)
340                 rset_delete (s->rset);
341             xfree (s->name);
342             xfree (s);
343         }
344         else
345             ss = &s->next;
346     }
347 }
348
349 ZebraPosSet zebraPosSetCreate (ZebraHandle zh, const char *name, 
350                                int num, int *positions)
351 {
352     ZebraSet sset;
353     ZebraPosSet sr = 0;
354     RSET rset;
355     int i;
356     struct zset_sort_info *sort_info;
357
358     if (!(sset = resultSetGet (zh, name)))
359         return NULL;
360     if (!(rset = sset->rset))
361     {
362         if (!sset->term_entries)
363             return 0;
364         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
365         for (i = 0; i<num; i++)
366         {
367             struct zebra_set_term_entry *entry = sset->term_entries;
368
369             sr[i].sysno = 0;
370             sr[i].score = -1;
371             sr[i].term = 0;
372             sr[i].db = 0;
373
374             if (positions[i] <= sset->term_entries_max)
375             {
376                 sr[i].term = sset->term_entries[positions[i]-1].term;
377                 sr[i].db = sset->term_entries[positions[i]-1].db;
378             }
379         }
380     }
381     else
382     {
383         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
384         for (i = 0; i<num; i++)
385         {
386             sr[i].sysno = 0;
387             sr[i].score = -1;
388             sr[i].term = 0;
389             sr[i].db = 0;
390         }
391         sort_info = sset->sort_info;
392         if (sort_info)
393         {
394             int position;
395             
396             for (i = 0; i<num; i++)
397             {
398                 position = positions[i];
399                 if (position > 0 && position <= sort_info->num_entries)
400                 {
401                     logf (LOG_DEBUG, "got pos=%d (sorted)", position);
402                     sr[i].sysno = sort_info->entries[position-1]->sysno;
403                     sr[i].score = sort_info->entries[position-1]->score;
404                 }
405             }
406         }
407         /* did we really get all entries using sort ? */
408         for (i = 0; i<num; i++)
409         {
410             if (!sr[i].sysno)
411                 break;
412         }
413         if (i < num) /* nope, get the rest, unsorted - sorry */
414         {
415             int position = 0;
416             int num_i = 0;
417             int psysno = 0;
418             int term_index;
419             RSFD rfd;
420             struct it_key key;
421             
422             if (sort_info)
423                 position = sort_info->num_entries;
424             while (num_i < num && positions[num_i] < position)
425                 num_i++;
426             rfd = rset_open (rset, RSETF_READ);
427             while (num_i < num && rset_read (rset, rfd, &key, &term_index))
428             {
429                 if (key.sysno != psysno)
430                 {
431                     psysno = key.sysno;
432                     if (sort_info)
433                     {
434                         /* determine we alreay have this in our set */
435                         for (i = sort_info->num_entries; --i >= 0; )
436                             if (psysno == sort_info->entries[i]->sysno)
437                                 break;
438                         if (i >= 0)
439                             continue;
440                     }
441                     position++;
442                     assert (num_i < num);
443                     if (position == positions[num_i])
444                     {
445                         sr[num_i].sysno = psysno;
446                         logf (LOG_DEBUG, "got pos=%d (unsorted)", position);
447                         sr[num_i].score = -1;
448                         num_i++;
449                     }
450                 }
451             }
452             rset_close (rset, rfd);
453         }
454     }
455     return sr;
456 }
457
458 void zebraPosSetDestroy (ZebraHandle zh, ZebraPosSet records, int num)
459 {
460     xfree (records);
461 }
462
463 struct sortKeyInfo {
464     int relation;
465     int attrUse;
466     int numerical;
467 };
468
469 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
470                           struct sortKeyInfo *criteria, int num_criteria,
471                           int sysno)
472 {
473     struct zset_sort_entry this_entry;
474     struct zset_sort_entry *new_entry = NULL;
475     struct zset_sort_info *sort_info = sset->sort_info;
476     int i, j;
477
478     sortIdx_sysno (zh->service->sortIdx, sysno);
479     for (i = 0; i<num_criteria; i++)
480     {
481         sortIdx_type (zh->service->sortIdx, criteria[i].attrUse);
482         sortIdx_read (zh->service->sortIdx, this_entry.buf[i]);
483     }
484     i = sort_info->num_entries;
485     while (--i >= 0)
486     {
487         int rel = 0;
488         for (j = 0; j<num_criteria; j++)
489         {
490             if (criteria[j].numerical)
491             {
492                 double diff = atof(this_entry.buf[j]) -
493                               atof(sort_info->entries[i]->buf[j]);
494                 rel = 0;
495                 if (diff > 0.0)
496                     rel = 1;
497                 else if (diff < 0.0)
498                     rel = -1;
499             }
500             else
501             {
502                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
503                           SORT_IDX_ENTRYSIZE);
504             }
505             if (rel)
506                 break;
507         }       
508         if (!rel)
509             break;
510         if (criteria[j].relation == 'A')
511         {
512             if (rel > 0)
513                 break;
514         }
515         else if (criteria[j].relation == 'D')
516         {
517             if (rel < 0)
518                 break;
519         }
520     }
521     ++i;
522     j = sort_info->max_entries;
523     if (i == j)
524         return;
525
526     if (sort_info->num_entries == j)
527         --j;
528     else
529         j = (sort_info->num_entries)++;
530     new_entry = sort_info->entries[j];
531     while (j != i)
532     {
533         sort_info->entries[j] = sort_info->entries[j-1];
534         --j;
535     }
536     sort_info->entries[i] = new_entry;
537     assert (new_entry);
538     for (i = 0; i<num_criteria; i++)
539         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
540     new_entry->sysno = sysno;
541     new_entry->score = -1;
542 }
543
544 void resultSetInsertRank (ZebraHandle zh, struct zset_sort_info *sort_info,
545                           int sysno, int score, int relation)
546 {
547     struct zset_sort_entry *new_entry = NULL;
548     int i, j;
549
550     i = sort_info->num_entries;
551     while (--i >= 0)
552     {
553         int rel = 0;
554
555         rel = score - sort_info->entries[i]->score;
556
557         if (relation == 'D')
558         {
559             if (rel >= 0)
560                 break;
561         }
562         else if (relation == 'A')
563         {
564             if (rel <= 0)
565                 break;
566         }
567     }
568     ++i;
569     j = sort_info->max_entries;
570     if (i == j)
571         return;
572
573     if (sort_info->num_entries == j)
574         --j;
575     else
576         j = (sort_info->num_entries)++;
577     
578     new_entry = sort_info->entries[j];
579     while (j != i)
580     {
581         sort_info->entries[j] = sort_info->entries[j-1];
582         --j;
583     }
584     sort_info->entries[i] = new_entry;
585     assert (new_entry);
586     new_entry->sysno = sysno;
587     new_entry->score = score;
588 }
589
590 void resultSetSort (ZebraHandle zh, NMEM nmem,
591                     int num_input_setnames, const char **input_setnames,
592                     const char *output_setname,
593                     Z_SortKeySpecList *sort_sequence, int *sort_status)
594 {
595     ZebraSet sset;
596     RSET rset;
597
598     if (num_input_setnames == 0)
599     {
600         zh->errCode = 208;
601         return ;
602     }
603     if (num_input_setnames > 1)
604     {
605         zh->errCode = 230;
606         return;
607     }
608     logf (LOG_DEBUG, "result set sort input=%s output=%s",
609           *input_setnames, output_setname);
610     sset = resultSetGet (zh, input_setnames[0]);
611     if (!sset)
612     {
613         zh->errCode = 30;
614         zh->errString = nmem_strdup (nmem, input_setnames[0]);
615         return;
616     }
617     if (!(rset = sset->rset))
618     {
619         zh->errCode = 30;
620         zh->errString = nmem_strdup (nmem, input_setnames[0]);
621         return;
622     }
623     if (strcmp (output_setname, input_setnames[0]))
624     {
625         rset = rset_dup (rset);
626         sset = resultSetAdd (zh, output_setname, 1);
627         sset->rset = rset;
628     }
629     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
630 }
631
632 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
633                           ZebraSet sset, RSET rset,
634                           Z_SortKeySpecList *sort_sequence, int *sort_status)
635 {
636     int i, psysno = 0;
637     struct it_key key;
638     struct sortKeyInfo sort_criteria[3];
639     int num_criteria;
640     int term_index;
641     RSFD rfd;
642
643     logf (LOG_DEBUG, "resultSetSortSingle start");
644     sset->sort_info->num_entries = 0;
645
646     sset->hits = 0;
647     num_criteria = sort_sequence->num_specs;
648     if (num_criteria > 3)
649         num_criteria = 3;
650     for (i = 0; i < num_criteria; i++)
651     {
652         Z_SortKeySpec *sks = sort_sequence->specs[i];
653         Z_SortKey *sk;
654
655         if (*sks->sortRelation == Z_SortRelation_ascending)
656             sort_criteria[i].relation = 'A';
657         else if (*sks->sortRelation == Z_SortRelation_descending)
658             sort_criteria[i].relation = 'D';
659         else
660         {
661             zh->errCode = 214;
662             return;
663         }
664         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
665         {
666             zh->errCode = 210;
667             return;
668         }
669         else if (sks->sortElement->which != Z_SortElement_generic)
670         {
671             zh->errCode = 237;
672             return;
673         }       
674         sk = sks->sortElement->u.generic;
675         switch (sk->which)
676         {
677         case Z_SortKey_sortField:
678             logf (LOG_DEBUG, "Sort: key %d is of type sortField", i+1);
679             zh->errCode = 207;
680             return;
681         case Z_SortKey_elementSpec:
682             logf (LOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
683             zh->errCode = 207;
684             return;
685         case Z_SortKey_sortAttributes:
686             logf (LOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
687             sort_criteria[i].attrUse =
688                 zebra_maps_sort (zh->service->zebra_maps,
689                                  sk->u.sortAttributes,
690                                  &sort_criteria[i].numerical);
691             logf (LOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
692             if (sort_criteria[i].attrUse == -1)
693             {
694                 zh->errCode = 116;
695                 return;
696             }
697             if (sortIdx_type (zh->service->sortIdx, sort_criteria[i].attrUse))
698             {
699                 zh->errCode = 207;
700                 return;
701             }
702             break;
703         }
704     }
705     rfd = rset_open (rset, RSETF_READ);
706     while (rset_read (rset, rfd, &key, &term_index))
707     {
708         if (key.sysno != psysno)
709         {
710             (sset->hits)++;
711             psysno = key.sysno;
712             resultSetInsertSort (zh, sset,
713                                  sort_criteria, num_criteria, psysno);
714         }
715     }
716     rset_close (rset, rfd);
717
718     *sort_status = Z_SortStatus_success;
719     logf (LOG_DEBUG, "resultSetSortSingle end");
720 }
721
722 RSET resultSetRef (ZebraHandle zh, Z_ResultSetId *resultSetId)
723 {
724     ZebraSet s;
725
726     if ((s = resultSetGet (zh, resultSetId)))
727         return s->rset;
728     return NULL;
729 }
730
731 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset)
732 {
733     int kno = 0;
734     struct it_key key;
735     RSFD rfd;
736     int term_index, i;
737     ZebraRankClass rank_class;
738     struct rank_control *rc;
739     struct zset_sort_info *sort_info;
740
741     sort_info = zebraSet->sort_info;
742     sort_info->num_entries = 0;
743     zebraSet->hits = 0;
744     rfd = rset_open (rset, RSETF_READ);
745
746     logf (LOG_DEBUG, "resultSetRank");
747     for (i = 0; i < rset->no_rset_terms; i++)
748         logf (LOG_DEBUG, "term=\"%s\" cnt=%d type=%s",
749               rset->rset_terms[i]->name,
750               rset->rset_terms[i]->nn,
751               rset->rset_terms[i]->flags);
752
753     rank_class = zebraRankLookup (zh, "rank-1");
754     rc = rank_class->control;
755
756     if (rset_read (rset, rfd, &key, &term_index))
757     {
758         int psysno = key.sysno;
759         int score;
760         void *handle =
761             (*rc->begin) (zh, rank_class->class_handle, rset);
762         (zebraSet->hits)++;
763         do
764         {
765             kno++;
766             if (key.sysno != psysno)
767             {
768                 score = (*rc->calc) (handle, psysno);
769
770                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
771                 (zebraSet->hits)++;
772                 psysno = key.sysno;
773             }
774             (*rc->add) (handle, key.seqno, term_index);
775         }
776         while (rset_read (rset, rfd, &key, &term_index));
777         score = (*rc->calc) (handle, psysno);
778         resultSetInsertRank (zh, sort_info, psysno, score, 'A');
779         (*rc->end) (zh, handle);
780     }
781     rset_close (rset, rfd);
782     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, zebraSet->hits);
783 }
784
785 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
786 {
787     ZebraRankClass p = zh->service->rank_classes;
788     while (p && strcmp (p->control->name, name))
789         p = p->next;
790     if (p && !p->init_flag)
791     {
792         if (p->control->create)
793             p->class_handle = (*p->control->create)(zh->service);
794         p->init_flag = 1;
795     }
796     return p;
797 }
798
799 void zebraRankInstall (ZebraService zh, struct rank_control *ctrl)
800 {
801     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
802     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
803     memcpy (p->control, ctrl, sizeof(*p->control));
804     p->control->name = xstrdup (ctrl->name);
805     p->init_flag = 0;
806     p->next = zh->rank_classes;
807     zh->rank_classes = p;
808 }
809
810 void zebraRankDestroy (ZebraService zh)
811 {
812     ZebraRankClass p = zh->rank_classes;
813     while (p)
814     {
815         ZebraRankClass p_next = p->next;
816         if (p->init_flag && p->control->destroy)
817             (*p->control->destroy)(zh, p->class_handle);
818         xfree (p->control->name);
819         xfree (p->control);
820         xfree (p);
821         p = p_next;
822     }
823     zh->rank_classes = NULL;
824 }