Add support for section/chapter indexing. Add safari filter.
[idzebra-moved-to-github.git] / index / zsets.c
1 /* $Id: zsets.c,v 1.72 2004-11-29 21:55:28 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #ifdef WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31
32 #include "index.h"
33 #include <rset.h>
34
35 #define SORT_IDX_ENTRYSIZE 64
36 #define ZSET_SORT_MAX_LEVEL 3
37
38 struct zebra_set_term_entry {
39     int reg_type;
40     char *db;
41     int set;
42     int use;
43     char *term;
44 };
45 struct zebra_set {
46     char *name;
47     RSET rset;
48     NMEM nmem;
49     NMEM rset_nmem; /* for creating the rsets in */
50     zint hits;
51     int num_bases;
52     char **basenames;
53     Z_RPNQuery *rpn;
54     struct zset_sort_info *sort_info;
55     struct zebra_set_term_entry *term_entries;
56     int term_entries_max;
57     struct zebra_set *next;
58     int locked;
59 };
60
61 struct zset_sort_entry {
62     zint sysno;
63     int score;
64     char buf[ZSET_SORT_MAX_LEVEL][SORT_IDX_ENTRYSIZE];
65 };
66
67 struct zset_sort_info {
68     int max_entries;
69     int num_entries;
70     struct zset_sort_entry *all_entries;
71     struct zset_sort_entry **entries;
72 };
73
74 ZebraSet resultSetAddRPN (ZebraHandle zh, NMEM m,
75                           Z_RPNQuery *rpn, int num_bases,
76                           char **basenames, 
77                           const char *setname)
78 {
79     ZebraSet zebraSet;
80     int i;
81
82     zh->errCode = 0;
83     zh->errString = NULL;
84     zh->hits = 0;
85
86     zebraSet = resultSetAdd (zh, setname, 1);
87     if (!zebraSet)
88         return 0;
89     zebraSet->locked = 1;
90     zebraSet->rpn = 0;
91     zebraSet->nmem = m;
92     zebraSet->rset_nmem=nmem_create(); 
93
94     zebraSet->num_bases = num_bases;
95     zebraSet->basenames = 
96         nmem_malloc (zebraSet->nmem, num_bases * sizeof(*zebraSet->basenames));
97     for (i = 0; i<num_bases; i++)
98         zebraSet->basenames[i] = nmem_strdup (zebraSet->nmem, basenames[i]);
99
100
101     zebraSet->rset = rpn_search (zh, zebraSet->nmem, zebraSet->rset_nmem,
102                                  rpn, zebraSet->num_bases,
103                                  zebraSet->basenames, zebraSet->name,
104                                  zebraSet);
105     zh->hits = zebraSet->hits;
106     if (zebraSet->rset)
107         zebraSet->rpn = rpn;
108     zebraSet->locked = 0;
109     return zebraSet;
110 }
111
112 void resultSetAddTerm (ZebraHandle zh, ZebraSet s, int reg_type,
113                        const char *db, int set,
114                        int use, const char *term)
115 {
116     assert(zh); /* compiler shut up */
117     if (!s->nmem)
118         s->nmem = nmem_create ();
119     if (!s->term_entries)
120     {
121         int i;
122         s->term_entries_max = 1000;
123         s->term_entries =
124             nmem_malloc (s->nmem, s->term_entries_max * 
125                          sizeof(*s->term_entries));
126         for (i = 0; i < s->term_entries_max; i++)
127             s->term_entries[i].term = 0;
128     }
129     if (s->hits < s->term_entries_max)
130     {
131         s->term_entries[s->hits].reg_type = reg_type;
132         s->term_entries[s->hits].db = nmem_strdup (s->nmem, db);
133         s->term_entries[s->hits].set = set;
134         s->term_entries[s->hits].use = use;
135         s->term_entries[s->hits].term = nmem_strdup (s->nmem, term);
136     }
137     (s->hits)++;
138 }
139
140 ZebraSet resultSetAdd (ZebraHandle zh, const char *name, int ov)
141 {
142     ZebraSet s;
143     int i;
144
145     for (s = zh->sets; s; s = s->next)
146         if (!strcmp (s->name, name))
147             break;
148     if (s)
149     {
150         yaz_log(YLOG_DEBUG, "updating result set %s", name);
151         if (!ov || s->locked)
152             return NULL;
153         if (s->rset)
154             rset_delete (s->rset);
155         if (s->rset_nmem)
156             nmem_destroy (s->rset_nmem);
157         if (s->nmem)
158             nmem_destroy (s->nmem);
159     }
160     else
161     {
162         const char *sort_max_str = zebra_get_resource(zh, "sortmax", "1000");
163
164         yaz_log(YLOG_DEBUG, "adding result set %s", name);
165         s = (ZebraSet) xmalloc (sizeof(*s));
166         s->next = zh->sets;
167         zh->sets = s;
168         s->name = (char *) xmalloc (strlen(name)+1);
169         strcpy (s->name, name);
170
171         s->sort_info = (struct zset_sort_info *)
172             xmalloc (sizeof(*s->sort_info));
173         s->sort_info->max_entries = atoi(sort_max_str);
174         if (s->sort_info->max_entries < 2)
175             s->sort_info->max_entries = 2;
176
177         s->sort_info->entries = (struct zset_sort_entry **)
178             xmalloc (sizeof(*s->sort_info->entries) *
179                      s->sort_info->max_entries);
180         s->sort_info->all_entries = (struct zset_sort_entry *)
181             xmalloc (sizeof(*s->sort_info->all_entries) *
182                      s->sort_info->max_entries);
183         for (i = 0; i < s->sort_info->max_entries; i++)
184             s->sort_info->entries[i] = s->sort_info->all_entries + i;
185     }
186     s->locked = 0;
187     s->term_entries = 0;
188     s->hits = 0;
189     s->rset = 0;
190     s->rset_nmem=0;
191     s->nmem = 0;
192     s->rpn = 0;
193     return s;
194 }
195
196 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
197 {
198     ZebraSet s;
199
200     for (s = zh->sets; s; s = s->next)
201         if (!strcmp (s->name, name))
202         {
203             if (!s->term_entries && !s->rset && s->rpn)
204             {
205                 NMEM nmem = nmem_create ();
206                 yaz_log(YLOG_LOG, "research %s", name);
207                 if (!s->rset_nmem)
208                     s->rset_nmem=nmem_create();
209                 s->rset =
210                     rpn_search (zh, nmem, s->rset_nmem, s->rpn, s->num_bases,
211                                 s->basenames, s->name, s);
212                 nmem_destroy (nmem);
213             }
214             return s;
215         }
216     return NULL;
217 }
218
219 void resultSetInvalidate (ZebraHandle zh)
220 {
221     ZebraSet s = zh->sets;
222     
223     for (; s; s = s->next)
224     {
225         if (s->rset)
226             rset_delete (s->rset);
227         s->rset = 0;
228         if (s->rset_nmem)
229             nmem_destroy(s->rset_nmem);
230         s->rset_nmem=0;
231     }
232 }
233
234 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
235 {
236     ZebraSet * ss = &zh->sets;
237     int i;
238     
239     if (statuses)
240         for (i = 0; i<num; i++)
241             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
242     zh->errCode = 0;
243     zh->errString = NULL;
244     while (*ss)
245     {
246         int i = -1;
247         ZebraSet s = *ss;
248         if (num >= 0)
249         {
250             for (i = 0; i<num; i++)
251                 if (!strcmp (s->name, names[i]))
252                 {
253                     if (statuses)
254                         statuses[i] = Z_DeleteStatus_success;
255                     i = -1;
256                     break;
257                 }
258         }
259         if (i < 0)
260         {
261             *ss = s->next;
262             
263             xfree (s->sort_info->all_entries);
264             xfree (s->sort_info->entries);
265             xfree (s->sort_info);
266             
267             if (s->nmem)
268                 nmem_destroy (s->nmem);
269             if (s->rset)
270                 rset_delete (s->rset);
271             if (s->rset_nmem)
272                 nmem_destroy(s->rset_nmem);
273             xfree (s->name);
274             xfree (s);
275         }
276         else
277             ss = &s->next;
278     }
279 }
280
281 ZebraMetaRecord *zebra_meta_records_create (ZebraHandle zh, const char *name, 
282                                             int num, zint *positions)
283 {
284     ZebraSet sset;
285     ZebraMetaRecord *sr = 0;
286     RSET rset;
287     int i;
288     struct zset_sort_info *sort_info;
289
290     if (!(sset = resultSetGet (zh, name)))
291         return NULL;
292     if (!(rset = sset->rset))
293     {
294         if (!sset->term_entries)
295             return 0;
296         sr = (ZebraMetaRecord *) xmalloc (sizeof(*sr) * num);
297         for (i = 0; i<num; i++)
298         {
299             sr[i].sysno = 0;
300             sr[i].score = -1;
301             sr[i].term = 0;
302             sr[i].db = 0;
303
304             if (positions[i] <= sset->term_entries_max)
305             {
306                 sr[i].term = sset->term_entries[positions[i]-1].term;
307                 sr[i].db = sset->term_entries[positions[i]-1].db;
308             }
309         }
310     }
311     else
312     {
313         sr = (ZebraMetaRecord *) xmalloc (sizeof(*sr) * num);
314         for (i = 0; i<num; i++)
315         {
316             sr[i].sysno = 0;
317             sr[i].score = -1;
318             sr[i].term = 0;
319             sr[i].db = 0;
320         }
321         sort_info = sset->sort_info;
322         if (sort_info)
323         {
324             int position;
325             
326             for (i = 0; i<num; i++)
327             {
328                 position = positions[i];
329                 if (position > 0 && position <= sort_info->num_entries)
330                 {
331                     yaz_log(YLOG_DEBUG, "got pos=%d (sorted)", position);
332                     sr[i].sysno = sort_info->entries[position-1]->sysno;
333                     sr[i].score = sort_info->entries[position-1]->score;
334                 }
335             }
336         }
337         /* did we really get all entries using sort ? */
338         for (i = 0; i<num; i++)
339         {
340             if (!sr[i].sysno)
341                 break;
342         }
343         if (i < num) /* nope, get the rest, unsorted - sorry */
344         {
345             int position = 0;
346             int num_i = 0;
347             zint psysno = 0;
348             RSFD rfd;
349             struct it_key key;
350             
351             if (sort_info)
352                 position = sort_info->num_entries;
353             while (num_i < num && positions[num_i] < position)
354                 num_i++;
355             rfd = rset_open (rset, RSETF_READ);
356             while (num_i < num && rset_read (rfd, &key, 0))
357             {
358                 zint this_sys = key.mem[0];
359                 yaz_log(YLOG_LOG, "RSET READ " ZINT_FORMAT, this_sys);
360                 if (this_sys != psysno)
361                 {
362                     psysno = this_sys;
363                     if (sort_info)
364                     {
365                         /* determine we alreay have this in our set */
366                         for (i = sort_info->num_entries; --i >= 0; )
367                             if (psysno == sort_info->entries[i]->sysno)
368                                 break;
369                         if (i >= 0)
370                             continue;
371                     }
372                     position++;
373                     assert (num_i < num);
374                     if (position == positions[num_i])
375                     {
376                         sr[num_i].sysno = psysno;
377                         yaz_log(YLOG_DEBUG, "got pos=%d (unsorted)", position);
378                         sr[num_i].score = -1;
379                         num_i++;
380                     }
381                 }
382             }
383             rset_close (rfd);
384         }
385     }
386     return sr;
387 }
388
389 void zebra_meta_records_destroy (ZebraHandle zh, ZebraMetaRecord *records,
390                                  int num)
391 {
392     assert(zh); /* compiler shut up about unused arg */
393     xfree (records);
394 }
395
396 struct sortKeyInfo {
397     int relation;
398     int attrUse;
399     int numerical;
400 };
401
402 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
403                           struct sortKeyInfo *criteria, int num_criteria,
404                           zint sysno)
405 {
406     struct zset_sort_entry this_entry;
407     struct zset_sort_entry *new_entry = NULL;
408     struct zset_sort_info *sort_info = sset->sort_info;
409     int i, j;
410
411     sortIdx_sysno (zh->reg->sortIdx, sysno);
412     for (i = 0; i<num_criteria; i++)
413     {
414         sortIdx_type (zh->reg->sortIdx, criteria[i].attrUse);
415         sortIdx_read (zh->reg->sortIdx, this_entry.buf[i]);
416     }
417     i = sort_info->num_entries;
418     while (--i >= 0)
419     {
420         int rel = 0;
421         for (j = 0; j<num_criteria; j++)
422         {
423             if (criteria[j].numerical)
424             {
425                 double diff = atof(this_entry.buf[j]) -
426                               atof(sort_info->entries[i]->buf[j]);
427                 rel = 0;
428                 if (diff > 0.0)
429                     rel = 1;
430                 else if (diff < 0.0)
431                     rel = -1;
432             }
433             else
434             {
435                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
436                           SORT_IDX_ENTRYSIZE);
437             }
438             if (rel)
439                 break;
440         }       
441         if (!rel)
442             break;
443         if (criteria[j].relation == 'A')
444         {
445             if (rel > 0)
446                 break;
447         }
448         else if (criteria[j].relation == 'D')
449         {
450             if (rel < 0)
451                 break;
452         }
453     }
454     ++i;
455     j = sort_info->max_entries;
456     if (i == j)
457         return;
458
459     if (sort_info->num_entries == j)
460         --j;
461     else
462         j = (sort_info->num_entries)++;
463     new_entry = sort_info->entries[j];
464     while (j != i)
465     {
466         sort_info->entries[j] = sort_info->entries[j-1];
467         --j;
468     }
469     sort_info->entries[i] = new_entry;
470     assert (new_entry);
471     for (i = 0; i<num_criteria; i++)
472         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
473     new_entry->sysno = sysno;
474     new_entry->score = -1;
475 }
476
477 void resultSetInsertRank (ZebraHandle zh, struct zset_sort_info *sort_info,
478                           zint sysno, int score, int relation)
479 {
480     struct zset_sort_entry *new_entry = NULL;
481     int i, j;
482     assert(zh); /* compiler shut up about unused arg */
483
484     i = sort_info->num_entries;
485     while (--i >= 0)
486     {
487         int rel = 0;
488
489         rel = score - sort_info->entries[i]->score;
490
491         if (relation == 'D')
492         {
493             if (rel >= 0)
494                 break;
495         }
496         else if (relation == 'A')
497         {
498             if (rel <= 0)
499                 break;
500         }
501     }
502     ++i;
503     j = sort_info->max_entries;
504     if (i == j)
505         return;
506
507     if (sort_info->num_entries == j)
508         --j;
509     else
510         j = (sort_info->num_entries)++;
511     
512     new_entry = sort_info->entries[j];
513     while (j != i)
514     {
515         sort_info->entries[j] = sort_info->entries[j-1];
516         --j;
517     }
518     sort_info->entries[i] = new_entry;
519     assert (new_entry);
520     new_entry->sysno = sysno;
521     new_entry->score = score;
522 }
523
524 void resultSetSort (ZebraHandle zh, NMEM nmem,
525                     int num_input_setnames, const char **input_setnames,
526                     const char *output_setname,
527                     Z_SortKeySpecList *sort_sequence, int *sort_status)
528 {
529     ZebraSet sset;
530     RSET rset;
531
532     if (num_input_setnames == 0)
533     {
534         zh->errCode = 208;
535         return ;
536     }
537     if (num_input_setnames > 1)
538     {
539         zh->errCode = 230;
540         return;
541     }
542     yaz_log(YLOG_DEBUG, "result set sort input=%s output=%s",
543           *input_setnames, output_setname);
544     sset = resultSetGet (zh, input_setnames[0]);
545     if (!sset)
546     {
547         zh->errCode = 30;
548         zh->errString = nmem_strdup (nmem, input_setnames[0]);
549         return;
550     }
551     if (!(rset = sset->rset))
552     {
553         zh->errCode = 30;
554         zh->errString = nmem_strdup (nmem, input_setnames[0]);
555         return;
556     }
557     if (strcmp (output_setname, input_setnames[0]))
558     {
559         rset = rset_dup (rset);
560         sset = resultSetAdd (zh, output_setname, 1);
561         sset->rset = rset;
562     }
563     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
564 }
565
566 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
567                           ZebraSet sset, RSET rset,
568                           Z_SortKeySpecList *sort_sequence, int *sort_status)
569 {
570     int i;
571     int n = 0;
572     zint kno = 0;
573     zint psysno = 0;
574     struct it_key key;
575     struct sortKeyInfo sort_criteria[3];
576     int num_criteria;
577     RSFD rfd;
578     TERMID termid;
579     TERMID *terms;
580     int numTerms = 0;
581
582     assert(nmem); /* compiler shut up about unused param */
583     sset->sort_info->num_entries = 0;
584
585     rset_getterms(rset, 0, 0, &n);
586     terms = (TERMID *) nmem_malloc(nmem, sizeof(*terms)*n);
587     rset_getterms(rset, terms, n, &numTerms);
588
589     sset->hits = 0;
590     num_criteria = sort_sequence->num_specs;
591     if (num_criteria > 3)
592         num_criteria = 3;
593     for (i = 0; i < num_criteria; i++)
594     {
595         Z_SortKeySpec *sks = sort_sequence->specs[i];
596         Z_SortKey *sk;
597
598         if (*sks->sortRelation == Z_SortKeySpec_ascending)
599             sort_criteria[i].relation = 'A';
600         else if (*sks->sortRelation == Z_SortKeySpec_descending)
601             sort_criteria[i].relation = 'D';
602         else
603         {
604             zh->errCode = 214;
605             return;
606         }
607         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
608         {
609             zh->errCode = 210;
610             return;
611         }
612         else if (sks->sortElement->which != Z_SortElement_generic)
613         {
614             zh->errCode = 237;
615             return;
616         }       
617         sk = sks->sortElement->u.generic;
618         switch (sk->which)
619         {
620         case Z_SortKey_sortField:
621             yaz_log(YLOG_DEBUG, "Sort: key %d is of type sortField", i+1);
622             zh->errCode = 207;
623             return;
624         case Z_SortKey_elementSpec:
625             yaz_log(YLOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
626             zh->errCode = 207;
627             return;
628         case Z_SortKey_sortAttributes:
629             yaz_log(YLOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
630             sort_criteria[i].attrUse =
631                 zebra_maps_sort (zh->reg->zebra_maps,
632                                  sk->u.sortAttributes,
633                                  &sort_criteria[i].numerical);
634             yaz_log(YLOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
635             if (sort_criteria[i].attrUse == -1)
636             {
637                 zh->errCode = 116;
638                 return;
639             }
640             if (sortIdx_type (zh->reg->sortIdx, sort_criteria[i].attrUse))
641             {
642                 zh->errCode = 207;
643                 return;
644             }
645             break;
646         }
647     }
648     rfd = rset_open (rset, RSETF_READ);
649     while (rset_read (rfd, &key, &termid))
650       /* FIXME - pass a TERMID *, and use it for something below !! */
651     {
652         zint this_sys = key.mem[0];
653         kno++;
654         if (this_sys != psysno)
655         {
656             (sset->hits)++;
657             psysno = this_sys;
658             resultSetInsertSort (zh, sset,
659                                  sort_criteria, num_criteria, psysno);
660         }
661     }
662     rset_close (rfd);
663     yaz_log(YLOG_DEBUG, ZINT_FORMAT " keys, " ZINT_FORMAT " sysnos, sort",
664                     kno, sset->hits);   
665     for (i = 0; i < numTerms; i++)
666         yaz_log(YLOG_LOG, "term=\"%s\" type=%s count=" ZINT_FORMAT,
667                  terms[i]->name, terms[i]->flags, rset_count(terms[i]->rset));
668     *sort_status = Z_SortResponse_success;
669 }
670
671 RSET resultSetRef (ZebraHandle zh, const char *resultSetId)
672 {
673     ZebraSet s;
674
675     if ((s = resultSetGet (zh, resultSetId)))
676         return s->rset;
677     return NULL;
678 }
679
680 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset, NMEM nmem)
681 {
682     zint kno = 0;
683     struct it_key key;
684     RSFD rfd;
685     TERMID termid;
686     TERMID *terms;
687     int numTerms = 0;
688     int n = 0;
689     int i;
690     ZebraRankClass rank_class;
691     struct rank_control *rc;
692     struct zset_sort_info *sort_info;
693     const char *rank_handler_name = res_get_def(zh->res, "rank", "rank-1");
694     double cur,tot; 
695     zint est=-2; /* -2 not done, -1 can't do, >0 actual estimate*/
696     zint esthits;
697     double ratio;
698
699     sort_info = zebraSet->sort_info;
700     sort_info->num_entries = 0;
701     zebraSet->hits = 0;
702     rset_getterms(rset, 0, 0, &n);
703     terms = (TERMID *) nmem_malloc(nmem, sizeof(*terms)*n);
704     rset_getterms(rset, terms, n, &numTerms);
705
706     rfd = rset_open (rset, RSETF_READ);
707
708     rank_class = zebraRankLookup (zh, rank_handler_name);
709     if (!rank_class)
710     {
711         yaz_log(YLOG_WARN, "No such rank handler: %s", rank_handler_name);
712         return;
713     }
714     rc = rank_class->control;
715
716     if (rset_read (rfd, &key, &termid))
717     {
718         zint psysno = key.mem[0];
719         int score;
720         void *handle =
721             (*rc->begin) (zh->reg, rank_class->class_handle, rset, nmem,
722                           terms, numTerms);
723         (zebraSet->hits)++;
724         esthits = atoi(res_get_def(zh->res, "estimatehits","0"));
725         if (!esthits) 
726             est = -1; /* can not do */
727         do
728         {
729             zint this_sys = key.mem[0]; /* FIXME - assumes scope==2 */
730             zint seqno = key.mem[key.len-1]; /* FIXME - assumes scope==2 */
731             kno++;
732             yaz_log(YLOG_LOG, "got sysno=%lld", this_sys);
733             if (this_sys != psysno)
734             {
735                 score = (*rc->calc) (handle, psysno);
736
737                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
738                 (zebraSet->hits)++;
739                 psysno = this_sys;
740             }
741             (*rc->add) (handle, seqno, termid);
742             
743             if ((est==-2) && (zebraSet->hits==esthits))
744             { /* time to estimate the hits */
745                 rset_pos(rfd,&cur,&tot); 
746                 if (tot>0) {
747                     ratio = cur/tot;
748                     est = (zint)(0.5+zebraSet->hits/ratio);
749                     yaz_log(YLOG_LOG, "Estimating hits (%s) "
750                                   "%0.1f->" ZINT_FORMAT
751                                   "; %0.1f->" ZINT_FORMAT,
752                                   rset->control->desc,
753                                   cur, zebraSet->hits,
754                                   tot, est);
755                     i = 0; /* round to 3 significant digits */
756                     while (est>1000) {
757                         est /= 10;
758                         i++;
759                     }
760                     while (i--)
761                         est *= 10;
762                     zebraSet->hits = est;
763                 }
764             }
765         }
766         while (rset_read (rfd, &key,&termid) && (est<0) );
767         score = (*rc->calc)(handle, psysno);
768         resultSetInsertRank(zh, sort_info, psysno, score, 'A');
769         (*rc->end) (zh->reg, handle);
770     }
771     rset_close (rfd);
772
773     yaz_log(YLOG_DEBUG, ZINT_FORMAT " keys, " ZINT_FORMAT " sysnos, rank",
774             kno, zebraSet->hits);
775     for (i = 0; i < numTerms; i++)
776     {
777         yaz_log(YLOG_LOG, "term=\"%s\" type=%s count=" ZINT_FORMAT,
778                  terms[i]->name, terms[i]->flags, rset_count(terms[i]->rset));
779     }
780 }
781
782 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
783 {
784     ZebraRankClass p = zh->reg->rank_classes;
785     while (p && strcmp (p->control->name, name))
786         p = p->next;
787     if (p && !p->init_flag)
788     {
789         if (p->control->create)
790             p->class_handle = (*p->control->create)(zh);
791         p->init_flag = 1;
792     }
793     return p;
794 }
795
796 void zebraRankInstall (struct zebra_register *reg, struct rank_control *ctrl)
797 {
798     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
799     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
800     memcpy (p->control, ctrl, sizeof(*p->control));
801     p->control->name = xstrdup (ctrl->name);
802     p->init_flag = 0;
803     p->next = reg->rank_classes;
804     reg->rank_classes = p;
805 }
806
807 void zebraRankDestroy (struct zebra_register *reg)
808 {
809     ZebraRankClass p = reg->rank_classes;
810     while (p)
811     {
812         ZebraRankClass p_next = p->next;
813         if (p->init_flag && p->control->destroy)
814             (*p->control->destroy)(reg, p->class_handle);
815         xfree(p->control->name);
816         xfree(p->control);
817         xfree(p);
818         p = p_next;
819     }
820     reg->rank_classes = NULL;
821 }