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