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