Do not use sync(2) after commit. The fsync on individual files suffices.
[idzebra-moved-to-github.git] / index / zsets.c
1 /* $Id: zsets.c,v 1.49.2.7 2006-08-14 10:39:02 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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 <rstemp.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     int hits;
50     int num_bases;
51     char **basenames;
52     Z_RPNQuery *rpn;
53     Z_SortKeySpecList *sortSpec;
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     int cache_position;  /* last position */
61     RSFD cache_rfd;      /* rfd (NULL if not existing) */
62     int cache_psysno;
63 };
64
65 struct zset_sort_entry {
66     int 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 ZebraSet resultSetAddRPN (ZebraHandle zh, NMEM m,
79                           Z_RPNQuery *rpn, int num_bases,
80                           char **basenames, 
81                           const char *setname)
82 {
83     ZebraSet zebraSet;
84     int i;
85
86     zh->errCode = 0;
87     zh->errString = NULL;
88     zh->hits = 0;
89
90     zebraSet = resultSetAdd (zh, setname, 1);
91     if (!zebraSet)
92         return 0;
93     zebraSet->locked = 1;
94     zebraSet->rpn = 0;
95     zebraSet->nmem = m;
96     
97     zebraSet->num_bases = num_bases;
98     zebraSet->basenames = 
99         nmem_malloc (zebraSet->nmem, num_bases * sizeof(*zebraSet->basenames));
100     for (i = 0; i<num_bases; i++)
101         zebraSet->basenames[i] = nmem_strdup (zebraSet->nmem, basenames[i]);
102
103
104     zebraSet->rset = rpn_search (zh, zebraSet->nmem, rpn,
105                                  zebraSet->num_bases,
106                                  zebraSet->basenames, zebraSet->name,
107                                  zebraSet);
108     zh->hits = zebraSet->hits;
109     if (zebraSet->rset)
110         zebraSet->rpn = rpn;
111     zebraSet->locked = 0;
112     return zebraSet;
113 }
114
115 void resultSetAddTerm (ZebraHandle zh, ZebraSet s, int reg_type,
116                        const char *db, int set,
117                        int use, const char *term)
118 {
119     if (!s->nmem)
120         s->nmem = nmem_create ();
121     if (!s->term_entries)
122     {
123         int i;
124         s->term_entries_max = 1000;
125         s->term_entries =
126             nmem_malloc (s->nmem, s->term_entries_max * 
127                          sizeof(*s->term_entries));
128         for (i = 0; i < s->term_entries_max; i++)
129             s->term_entries[i].term = 0;
130     }
131     if (s->hits < s->term_entries_max)
132     {
133         s->term_entries[s->hits].reg_type = reg_type;
134         s->term_entries[s->hits].db = nmem_strdup (s->nmem, db);
135         s->term_entries[s->hits].set = set;
136         s->term_entries[s->hits].use = use;
137         s->term_entries[s->hits].term = nmem_strdup (s->nmem, term);
138     }
139     (s->hits)++;
140 }
141
142
143 int zebra_resultSetTerms (ZebraHandle zh, const char *setname, 
144                           int no, int *count, 
145                           int *type, char *out, size_t *len)
146 {
147     ZebraSet s = resultSetGet (zh, setname);
148     int no_max = 0;
149
150     if (count)
151         *count = 0;
152     if (!s || !s->rset)
153         return 0;
154     no_max = s->rset->no_rset_terms;
155     if (no < 0 || no >= no_max)
156         return 0;
157     if (count)
158         *count = s->rset->rset_terms[no]->count;
159     if (type)
160         *type = s->rset->rset_terms[no]->type;
161     
162     if (out)
163     {
164         char *inbuf = s->rset->rset_terms[no]->name;
165         size_t inleft = strlen(inbuf);
166         size_t outleft = *len - 1;
167         int converted = 0;
168
169         if (zh->iconv_from_utf8 != 0)
170         {
171             char *outbuf = out;
172             size_t ret;
173             
174             ret = yaz_iconv(zh->iconv_from_utf8, &inbuf, &inleft,
175                         &outbuf, &outleft);
176             if (ret == (size_t)(-1))
177                 *len = 0;
178             else
179                 *len = outbuf - out;
180             converted = 1;
181         }
182         if (!converted)
183         {
184             if (inleft > outleft)
185                 inleft = outleft;
186             *len = inleft;
187             memcpy (out, inbuf, *len);
188         }
189         out[*len] = 0;
190     }
191     return no_max;
192 }
193
194
195 ZebraSet resultSetAdd (ZebraHandle zh, const char *name, int ov)
196 {
197     ZebraSet s;
198     int i;
199
200     for (s = zh->sets; s; s = s->next)
201         if (!strcmp (s->name, name))
202             break;
203     if (s)
204     {
205         yaz_log (LOG_DEBUG, "updating result set %s", name);
206         if (!ov || s->locked)
207             return NULL;
208         if (s->rset)
209         {
210             if (s->cache_rfd)
211                 rset_close(s->rset, s->cache_rfd);
212             rset_delete (s->rset);
213         }
214         if (s->nmem)
215             nmem_destroy (s->nmem);
216     }
217     else
218     {
219         const char *sort_max_str = zebra_get_resource(zh, "sortmax", "1000");
220
221         yaz_log (LOG_DEBUG, "adding result set %s", name);
222         s = (ZebraSet) xmalloc (sizeof(*s));
223         s->next = zh->sets;
224         zh->sets = s;
225         s->name = (char *) xmalloc (strlen(name)+1);
226         strcpy (s->name, name);
227
228         s->sort_info = (struct zset_sort_info *)
229             xmalloc (sizeof(*s->sort_info));
230         s->sort_info->max_entries = atoi(sort_max_str);
231         if (s->sort_info->max_entries < 2)
232             s->sort_info->max_entries = 2;
233
234         s->sort_info->entries = (struct zset_sort_entry **)
235             xmalloc (sizeof(*s->sort_info->entries) *
236                      s->sort_info->max_entries);
237         s->sort_info->all_entries = (struct zset_sort_entry *)
238             xmalloc (sizeof(*s->sort_info->all_entries) *
239                      s->sort_info->max_entries);
240         for (i = 0; i < s->sort_info->max_entries; i++)
241             s->sort_info->entries[i] = s->sort_info->all_entries + i;
242     }
243     s->locked = 0;
244     s->term_entries = 0;
245     s->hits = 0;
246     s->rset = 0;
247     s->nmem = 0;
248     s->rpn = 0;
249     s->sortSpec = 0;
250     s->cache_position = 0;
251     s->cache_rfd = 0;
252     return s;
253 }
254
255 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
256 {
257     ZebraSet s;
258
259     for (s = zh->sets; s; s = s->next)
260         if (!strcmp (s->name, name))
261         {
262             if (!s->term_entries && !s->rset && s->rpn)
263             {
264                 NMEM nmem = nmem_create ();
265                 yaz_log (LOG_LOG, "research %s", name);
266                 s->rset =
267                     rpn_search (zh, nmem, s->rpn, s->num_bases,
268                                 s->basenames, s->name, s);
269                 if (s->rset && s->sortSpec)
270                 {
271                     int sort_status;
272                     yaz_log (LOG_LOG, "resort %s", name);
273                     resultSetSortSingle (zh, nmem, s, s->rset, s->sortSpec,
274                                          &sort_status);
275                 }
276                 nmem_destroy (nmem);
277             }
278             return s;
279         }
280     return NULL;
281 }
282
283 void resultSetInvalidate (ZebraHandle zh)
284 {
285     ZebraSet s = zh->sets;
286     
287     for (; s; s = s->next)
288     {
289         if (s->rset)
290         {
291             if (s->cache_rfd)
292                 rset_close(s->rset, s->cache_rfd);
293             rset_delete (s->rset);
294         }
295         s->rset = 0;
296         s->cache_rfd = 0;
297         s->cache_position = 0;
298     }
299 }
300
301 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
302 {
303     ZebraSet * ss = &zh->sets;
304     int i;
305     
306     if (statuses)
307         for (i = 0; i<num; i++)
308             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
309     zh->errCode = 0;
310     zh->errString = NULL;
311     while (*ss)
312     {
313         int i = -1;
314         ZebraSet s = *ss;
315         if (num >= 0)
316         {
317             for (i = 0; i<num; i++)
318                 if (!strcmp (s->name, names[i]))
319                 {
320                     if (statuses)
321                         statuses[i] = Z_DeleteStatus_success;
322                     i = -1;
323                     break;
324                 }
325         }
326         if (i < 0)
327         {
328             *ss = s->next;
329             
330             xfree (s->sort_info->all_entries);
331             xfree (s->sort_info->entries);
332             xfree (s->sort_info);
333             
334             if (s->nmem)
335                 nmem_destroy (s->nmem);
336             if (s->rset)
337             {
338                 if (s->cache_rfd)
339                     rset_close (s->rset, s->cache_rfd);
340                 rset_delete (s->rset);
341             }
342             xfree (s->name);
343             xfree (s);
344         }
345         else
346             ss = &s->next;
347     }
348 }
349
350 ZebraPosSet zebraPosSetCreate (ZebraHandle zh, const char *name, 
351                                int num, int *positions)
352 {
353     ZebraSet sset;
354     ZebraPosSet sr = 0;
355     RSET rset;
356     int i;
357     struct zset_sort_info *sort_info;
358
359     if (!(sset = resultSetGet (zh, name)))
360         return NULL;
361     if (!(rset = sset->rset))
362     {
363         if (!sset->term_entries)
364             return 0;
365         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
366         for (i = 0; i<num; i++)
367         {
368             sr[i].sysno = 0;
369             sr[i].score = -1;
370             sr[i].term = 0;
371             sr[i].db = 0;
372
373             if (positions[i] <= sset->term_entries_max)
374             {
375                 sr[i].term = sset->term_entries[positions[i]-1].term;
376                 sr[i].db = sset->term_entries[positions[i]-1].db;
377             }
378         }
379     }
380     else
381     {
382         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
383         for (i = 0; i<num; i++)
384         {
385             sr[i].sysno = 0;
386             sr[i].score = -1;
387             sr[i].term = 0;
388             sr[i].db = 0;
389         }
390         sort_info = sset->sort_info;
391         if (sort_info)
392         {
393             int position;
394             
395             for (i = 0; i<num; i++)
396             {
397                 position = positions[i];
398                 if (position > 0 && position <= sort_info->num_entries)
399                 {
400                     yaz_log (LOG_DEBUG, "got pos=%d (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             int position = 0;
415             int num_i = 0;
416             int psysno = 0;
417             int term_index;
418             RSFD rfd;
419             struct it_key key;
420             
421             if (sort_info)
422                 position = sort_info->num_entries;
423             while (num_i < num && positions[num_i] < position)
424                 num_i++;
425             
426             if (sset->cache_rfd && 
427                 num_i < num && positions[num_i] > sset->cache_position)
428             {
429                 position = sset->cache_position;
430                 rfd = sset->cache_rfd;
431                 psysno = sset->cache_psysno;
432             } 
433             else
434             {
435                 if (sset->cache_rfd)
436                     rset_close(rset, sset->cache_rfd);
437                 rfd = rset_open (rset, RSETF_READ);
438             }
439             while (num_i < num && rset_read (rset, rfd, &key, &term_index))
440             {
441                 if (key.sysno != psysno)
442                 {
443                     psysno = key.sysno;
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_DEBUG, "got pos=%d (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 zebraPosSetDestroy (ZebraHandle zh, ZebraPosSet records, int num)
473 {
474     xfree (records);
475 }
476
477 struct sortKeyInfo {
478     int relation;
479     int attrUse;
480     int numerical;
481 };
482
483 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
484                           struct sortKeyInfo *criteria, int num_criteria,
485                           int sysno)
486 {
487     struct zset_sort_entry this_entry;
488     struct zset_sort_entry *new_entry = NULL;
489     struct zset_sort_info *sort_info = sset->sort_info;
490     int i, j;
491
492     sortIdx_sysno (zh->reg->sortIdx, sysno);
493     for (i = 0; i<num_criteria; i++)
494     {
495         memset(this_entry.buf[i], '\0', SORT_IDX_ENTRYSIZE);
496         if (criteria[i].attrUse != -1)
497         {
498             sortIdx_type (zh->reg->sortIdx, criteria[i].attrUse);
499             sortIdx_read (zh->reg->sortIdx, this_entry.buf[i]);
500         }
501     }
502     i = sort_info->num_entries;
503     while (--i >= 0)
504     {
505         int rel = 0;
506         for (j = 0; j<num_criteria; j++)
507         {
508             if (criteria[j].numerical)
509             {
510                 double diff = atof(this_entry.buf[j]) -
511                               atof(sort_info->entries[i]->buf[j]);
512                 rel = 0;
513                 if (diff > 0.0)
514                     rel = 1;
515                 else if (diff < 0.0)
516                     rel = -1;
517             }
518             else
519             {
520                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
521                           SORT_IDX_ENTRYSIZE);
522             }
523             if (rel)
524                 break;
525         }       
526         if (!rel)
527             break;
528         if (criteria[j].relation == 'A')
529         {
530             if (rel > 0)
531                 break;
532         }
533         else if (criteria[j].relation == 'D')
534         {
535             if (rel < 0)
536                 break;
537         }
538     }
539     ++i;
540     j = sort_info->max_entries;
541     if (i == j)
542         return;
543
544     if (sort_info->num_entries == j)
545         --j;
546     else
547         j = (sort_info->num_entries)++;
548     new_entry = sort_info->entries[j];
549     while (j != i)
550     {
551         sort_info->entries[j] = sort_info->entries[j-1];
552         --j;
553     }
554     sort_info->entries[i] = new_entry;
555     assert (new_entry);
556     for (i = 0; i<num_criteria; i++)
557         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
558     new_entry->sysno = sysno;
559     new_entry->score = -1;
560 }
561
562 void resultSetInsertRank(ZebraHandle zh, struct zset_sort_info *sort_info,
563                          int sysno, int score, int relation)
564 {
565     struct zset_sort_entry *new_entry = NULL;
566     int i, j;
567
568     i = sort_info->num_entries;
569     while (--i >= 0)
570     {
571         int rel = 0;
572
573         rel = score - sort_info->entries[i]->score;
574
575         if (relation == 'D')
576         {
577             if (rel >= 0)
578                 break;
579         }
580         else if (relation == 'A')
581         {
582             if (rel <= 0)
583                 break;
584         }
585     }
586     ++i;
587     j = sort_info->max_entries;
588     if (i == j)
589         return;
590
591     if (sort_info->num_entries == j)
592         --j;
593     else
594         j = (sort_info->num_entries)++;
595     
596     new_entry = sort_info->entries[j];
597     while (j != i)
598     {
599         sort_info->entries[j] = sort_info->entries[j-1];
600         --j;
601     }
602     sort_info->entries[i] = new_entry;
603     assert (new_entry);
604     new_entry->sysno = sysno;
605     new_entry->score = score;
606 }
607
608
609 static Z_RPNQuery *copy_RPNQuery(Z_RPNQuery *src, NMEM nmem)
610 {
611     Z_RPNQuery *dst = 0;
612     ODR encode = odr_createmem(ODR_ENCODE);
613     ODR decode = odr_createmem(ODR_DECODE);
614
615     if (z_RPNQuery(encode, &src, 0, 0))
616     {
617         int len;
618         char *buf = odr_getbuf(encode, &len, 0);
619
620         if (buf)
621         {
622             odr_setbuf(decode, buf, len, 0);
623             z_RPNQuery(decode, &dst, 0, 0);
624         }
625     }
626     nmem_transfer(nmem, decode->mem);
627     odr_destroy(encode);
628     odr_destroy(decode);
629     return dst;
630 }
631
632 static Z_SortKeySpecList *copy_SortKeySpecList(Z_SortKeySpecList *src, NMEM nmem)
633 {
634     Z_SortKeySpecList *dst = 0;
635     ODR encode = odr_createmem(ODR_ENCODE);
636     ODR decode = odr_createmem(ODR_DECODE);
637
638     if (z_SortKeySpecList(encode, &src, 0, 0))
639     {
640         int len;
641         char *buf = odr_getbuf(encode, &len, 0);
642
643         if (buf)
644         {
645             odr_setbuf(decode, buf, len, 0);
646             z_SortKeySpecList(decode, &dst, 0, 0);
647         }
648     }
649     nmem_transfer(nmem, decode->mem);
650     odr_destroy(encode);
651     odr_destroy(decode);
652     return dst;
653 }
654
655 ZebraSet resultSetClone(ZebraHandle zh, const char *setname,
656                         ZebraSet rset)
657 {
658     ZebraSet nset;
659     int i;
660
661     nset = resultSetAdd(zh, setname, 1);
662     if (!nset)
663         return 0;
664
665     nset->nmem = nmem_create();
666
667     nset->num_bases = rset->num_bases;
668     nset->basenames = 
669         nmem_malloc (nset->nmem, nset->num_bases * sizeof(*rset->basenames));
670     for (i = 0; i<rset->num_bases; i++)
671         nset->basenames[i] = nmem_strdup(nset->nmem, rset->basenames[i]);
672
673     if (rset->rset)
674         nset->rset = rset_dup(rset->rset);
675     if (rset->rpn)
676         nset->rpn = copy_RPNQuery(rset->rpn, nset->nmem);
677     return nset;
678 }
679
680 void resultSetSort (ZebraHandle zh, NMEM nmem,
681                     int num_input_setnames, const char **input_setnames,
682                     const char *output_setname,
683                     Z_SortKeySpecList *sort_sequence, int *sort_status)
684 {
685     ZebraSet sset;
686     RSET rset;
687
688     if (num_input_setnames == 0)
689     {
690         zh->errCode = 208;
691         return ;
692     }
693     if (num_input_setnames > 1)
694     {
695         zh->errCode = 230;
696         return;
697     }
698     yaz_log (LOG_DEBUG, "result set sort input=%s output=%s",
699           *input_setnames, output_setname);
700     sset = resultSetGet (zh, input_setnames[0]);
701     if (!sset)
702     {
703         zh->errCode = 30;
704         zh->errString = nmem_strdup (nmem, input_setnames[0]);
705         return;
706     }
707     if (!(rset = sset->rset))
708     {
709         zh->errCode = 30;
710         zh->errString = nmem_strdup (nmem, input_setnames[0]);
711         return;
712     }
713     if (strcmp (output_setname, input_setnames[0]))
714         sset = resultSetClone(zh, output_setname, sset);
715     sset->sortSpec = copy_SortKeySpecList(sort_sequence, sset->nmem);
716     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
717 }
718
719 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
720                           ZebraSet sset, RSET rset,
721                           Z_SortKeySpecList *sort_sequence, int *sort_status)
722 {
723     int kno = 0;
724     int i, psysno = 0;
725     struct it_key key;
726     struct sortKeyInfo sort_criteria[3];
727     int num_criteria;
728     int term_index;
729     RSFD rfd;
730
731     sset->sort_info->num_entries = 0;
732
733     sset->hits = 0;
734     num_criteria = sort_sequence->num_specs;
735     if (num_criteria > 3)
736         num_criteria = 3;
737     for (i = 0; i < num_criteria; i++)
738     {
739         Z_SortKeySpec *sks = sort_sequence->specs[i];
740         Z_SortKey *sk;
741
742         if (*sks->sortRelation == Z_SortKeySpec_ascending)
743             sort_criteria[i].relation = 'A';
744         else if (*sks->sortRelation == Z_SortKeySpec_descending)
745             sort_criteria[i].relation = 'D';
746         else
747         {
748             zh->errCode = 214;
749             return;
750         }
751         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
752         {
753             zh->errCode = 210;
754             return;
755         }
756         else if (sks->sortElement->which != Z_SortElement_generic)
757         {
758             zh->errCode = 237;
759             return;
760         }
761
762         if (sks->which == Z_SortKeySpec_missingValueData)
763         {
764             zh->errCode = 213;
765             return;
766         }
767
768         sk = sks->sortElement->u.generic;
769         switch (sk->which)
770         {
771         case Z_SortKey_sortField:
772             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortField", i+1);
773             zh->errCode = 207;
774             return;
775         case Z_SortKey_elementSpec:
776             yaz_log (LOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
777             zh->errCode = 207;
778             return;
779         case Z_SortKey_sortAttributes:
780             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
781             sort_criteria[i].attrUse =
782                 zebra_maps_sort (zh->reg->zebra_maps,
783                                  sk->u.sortAttributes,
784                                  &sort_criteria[i].numerical);
785             yaz_log (LOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
786             if (sort_criteria[i].attrUse == -1 && sks->which != Z_SortKeySpec_null)
787             {
788                 zh->errCode = 116;
789                 return;
790             }
791             if (sortIdx_type (zh->reg->sortIdx, sort_criteria[i].attrUse))
792             {
793                 if (sks->which != Z_SortKeySpec_null)
794                 {
795                     zh->errCode = 207;
796                     return;
797                 }
798                 else
799                     sort_criteria[i].attrUse = -1;
800             }
801             break;
802         }
803     }
804     rfd = rset_open (rset, RSETF_READ);
805     while (rset_read (rset, rfd, &key, &term_index))
806     {
807         kno++;
808         if (key.sysno != psysno)
809         {
810             (sset->hits)++;
811             psysno = key.sysno;
812             resultSetInsertSort (zh, sset,
813                                  sort_criteria, num_criteria, psysno);
814         }
815     }
816     rset_close (rset, rfd);
817
818     yaz_log (LOG_LOG, "%d keys, %d sysnos, sort", kno, sset->hits);
819     for (i = 0; i < rset->no_rset_terms; i++)
820         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
821                  rset->rset_terms[i]->name,
822                  rset->rset_terms[i]->nn,
823                  rset->rset_terms[i]->flags,
824                  rset->rset_terms[i]->count);
825
826     *sort_status = Z_SortResponse_success;
827 }
828
829 RSET resultSetRef (ZebraHandle zh, const char *resultSetId)
830 {
831     ZebraSet s;
832
833     if ((s = resultSetGet (zh, resultSetId)))
834         return s->rset;
835     return NULL;
836 }
837
838 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset)
839 {
840     int kno = 0;
841     struct it_key key;
842     RSFD rfd;
843     int term_index, i;
844     ZebraRankClass rank_class;
845     struct rank_control *rc;
846     struct zset_sort_info *sort_info;
847     const char *rank_handler_name = res_get_def(zh->res, "rank", "rank-1");
848
849     sort_info = zebraSet->sort_info;
850     sort_info->num_entries = 0;
851     zebraSet->hits = 0;
852     rfd = rset_open (rset, RSETF_READ);
853
854     rank_class = zebraRankLookup (zh, rank_handler_name);
855     if (!rank_class)
856     {
857         yaz_log (LOG_WARN, "No such rank handler: %s", rank_handler_name);
858         return;
859     }
860     rc = rank_class->control;
861
862     if (rset_read (rset, rfd, &key, &term_index))
863     {
864         int psysno = key.sysno;
865         int score;
866         void *handle =
867             (*rc->begin) (zh->reg, rank_class->class_handle, rset);
868         (zebraSet->hits)++;
869         do
870         {
871             kno++;
872             if (key.sysno != psysno)
873             {
874                 score = (*rc->calc) (handle, psysno);
875
876                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
877                 (zebraSet->hits)++;
878                 psysno = key.sysno;
879             }
880             (*rc->add) (handle, key.seqno, term_index);
881         }
882         while (rset_read (rset, rfd, &key, &term_index));
883         score = (*rc->calc) (handle, psysno);
884         resultSetInsertRank (zh, sort_info, psysno, score, 'A');
885         (*rc->end) (zh->reg, handle);
886     }
887     rset_close (rset, rfd);
888
889     yaz_log (LOG_LOG, "%d keys, %d sysnos, rank", kno, zebraSet->hits);
890     for (i = 0; i < rset->no_rset_terms; i++)
891         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
892                  rset->rset_terms[i]->name,
893                  rset->rset_terms[i]->nn,
894                  rset->rset_terms[i]->flags,
895                  rset->rset_terms[i]->count);
896     
897 }
898
899 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
900 {
901     ZebraRankClass p = zh->reg->rank_classes;
902     while (p && strcmp (p->control->name, name))
903         p = p->next;
904     if (p && !p->init_flag)
905     {
906         if (p->control->create)
907             p->class_handle = (*p->control->create)(zh);
908         p->init_flag = 1;
909     }
910     return p;
911 }
912
913 void zebraRankInstall (struct zebra_register *reg, struct rank_control *ctrl)
914 {
915     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
916     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
917     memcpy (p->control, ctrl, sizeof(*p->control));
918     p->control->name = xstrdup (ctrl->name);
919     p->init_flag = 0;
920     p->next = reg->rank_classes;
921     reg->rank_classes = p;
922 }
923
924 void zebraRankDestroy (struct zebra_register *reg)
925 {
926     ZebraRankClass p = reg->rank_classes;
927     while (p)
928     {
929         ZebraRankClass p_next = p->next;
930         if (p->init_flag && p->control->destroy)
931             (*p->control->destroy)(reg, p->class_handle);
932         xfree (p->control->name);
933         xfree (p->control);
934         xfree (p);
935         p = p_next;
936     }
937     reg->rank_classes = NULL;
938 }