Integrated ranking algorithm from Liverpool University
[idzebra-moved-to-github.git] / index / zvrank.c
1 /* $Id: zvrank.c,v 1.3 2003-03-26 16:41:48 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003
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 /* zvrank.c */
24 /* Zebra Vector Space Model RANKing */
25 /*
26 ** six (seven) letter identifier for weighting schema
27 ** best document weighting:
28 **  tfc nfc (tpc npc) [original naming]
29 **  ntc atc  npc apc  [SMART naming, used here]
30 ** best query weighting:
31 **  nfx tfx bfx (npx tpx bpx) [original naming]
32 **  atn ntn btn  apn npn bpn  [SMART naming]
33 */
34
35 #include <math.h>  /* for log */
36
37 #include <stdio.h>
38 #include <assert.h>
39 #ifdef WIN32
40 #include <io.h>
41 #else
42 #include <unistd.h>
43 #endif
44
45 #include "index.h"
46
47 double blog2(double x) { /* sometimes log_e or log_10 is used */
48   if (x <= 0)
49     return 0;
50   return log(x);
51 }
52
53 /* structures */
54
55 struct rs_info { /* for result set */
56   int db_docs;        /* number of documents in database (collection) */
57   int db_terms;       /* number of distinct terms in database */
58   int db_f_max;       /* maximum of f_t in database */
59   char *db_f_max_str; /* string (most frequent term) */
60   /**/
61   char rschema[8];    /* name of ranking schema */
62   /**/
63   int veclen;
64   void (*d_tf_fct)(void *, void *);   /* doc term frequency function */
65   void (*d_idf_fct)(void *, void *);  /* doc idf function */
66   void (*d_norm_fct)(void *, void *); /* doc normalization function */
67   /**/
68   void (*q_tf_fct)(void *, void *);   /* query term frequency function */
69   void (*q_idf_fct)(void *, void *);  /* query idf function */
70   void (*q_norm_fct)(void *, void *); /* query normalization function */
71   
72   double (*sim_fct)(void *, void *);  /* similarity function (scoring function) */
73   struct ds_info *qdoc;
74   struct ds_info *rdoc;
75 };
76 typedef struct rs_info *RS;
77
78 void prn_rs(RS rs) {
79   yaz_log(LOG_DEBUG, "* RS:\n");
80   yaz_log(LOG_DEBUG, " db_docs:   %d\n", rs->db_docs);
81   yaz_log(LOG_DEBUG, " db_terms:  %d\n", rs->db_terms);
82   yaz_log(LOG_DEBUG, " f_max:     %d\n", rs->db_f_max);
83   yaz_log(LOG_DEBUG, " f_max_str: %s\n", rs->db_f_max_str);
84   yaz_log(LOG_DEBUG, " veclen:    %d\n", rs->veclen);
85   /* rschema implies functions */
86   yaz_log(LOG_DEBUG, " rschema:   %s\n", rs->rschema);
87   return;
88 }
89
90 struct ds_info {       /* document info */
91   char *docid;         /* unique doc identifier */
92   int  docno;          /* doc number */
93   int doclen;          /* document length */
94   int d_f_max;         /* maximum number of any term in doc */
95   char *d_f_max_str;   /* most frequent term in d */
96   int veclen;          /* vector length */
97   struct ts_info *terms;
98   double docsim;       /* similarity in [0, ..., 1] (= score/1000) */
99 };
100 typedef struct ds_info* DS;
101
102 void prn_ds(DS ds) {
103   yaz_log(LOG_DEBUG, " * DS:\n");
104   yaz_log(LOG_DEBUG, " docid:      %s\n", ds->docid);
105   yaz_log(LOG_DEBUG, " docno:      %d\n", ds->docno);
106   yaz_log(LOG_DEBUG, " doclen:     %d\n", ds->doclen);
107   yaz_log(LOG_DEBUG, " d_f_max:    %d\n", ds->d_f_max);
108   yaz_log(LOG_DEBUG, " d_f_max_str:%s\n", ds->d_f_max_str);
109   yaz_log(LOG_DEBUG, " veclen:     %d\n", ds->veclen);
110   return;
111 }
112
113 struct ts_info {       /* term info */
114   char *name;
115   int *id;
116   /**/
117   int gocc;
118   int locc;
119   double tf;
120   double idf;
121   double wt;
122 };
123 typedef struct ts_info *TS;
124
125 void prn_ts(TS ts) {
126   yaz_log(LOG_DEBUG, " * TERM:%s gocc:%d locc:%d  tf:%f idf:%f wt:%f\n",
127           ts->name, ts->gocc, ts->locc, ts->tf, ts->idf, ts->wt);
128   return;
129 }
130
131 /* end structures */
132
133 /* *** */
134
135 /* 
136 ** weighting functions 
137 */
138
139 /* calculate new term frequency vector */
140 void tf_none(void *rsi, void *dsi) {
141   DS ds=(DS)dsi;
142   int i;
143   int veclen;
144   int freq;
145   /**/
146   veclen=ds->veclen;
147   for (i=0; i < veclen; i++) {
148     freq=ds->terms[i].locc;
149     ds->terms[i].tf=freq;
150   }
151   return;
152 }
153
154 void tf_binary(void *rsi, void *dsi) {
155   DS ds=(DS)dsi;
156   int i;
157   int veclen;
158   int freq;
159   /**/
160   veclen=ds->veclen;
161   for (i=0; i < veclen; i++) {
162     freq=ds->terms[i].locc;
163     if (freq > 0)
164       ds->terms[i].tf=1.0;
165     else
166       ds->terms[i].tf=0.0;
167   }
168   return;
169 }
170
171 void tf_max_norm(void *rsi, void *dsi) {
172   RS rs=(RS)rsi;
173   DS ds=(DS)dsi;
174   int tf_max;
175   int i;
176   int veclen;
177   int freq;
178   /**/
179   tf_max=rs->db_f_max;
180   veclen=ds->veclen;
181   for (i=0; i < veclen; i++) {
182     freq=ds->terms[i].locc;
183     if ((freq > 0) &&
184         (tf_max > 0)) 
185       ds->terms[i].tf=freq/tf_max;
186     else
187       ds->terms[i].tf=0.0;
188   }
189   return;
190 }
191
192 void tf_aug_norm(void *rsi, void *dsi) {
193   RS rs=(RS)rsi;
194   DS ds=(DS)dsi;
195   double K;
196   double tf_max;
197   int i;
198   int veclen;
199   int freq;
200   /**/
201   tf_max=rs->db_f_max;
202   veclen=ds->veclen;
203   K=0.5;
204   for (i=0; i < veclen; i++) {
205     freq=ds->terms[i].locc;
206     if ((freq > 0) &&
207         (tf_max > 0)) 
208       ds->terms[i].tf=K+(1-K)*(freq/tf_max);
209     else
210       ds->terms[i].tf=0.0;
211   }
212   return;
213 }
214
215 void tf_square(void *rsi, void *dsi) {
216   DS ds=(DS)dsi;
217   int i;
218   int veclen;
219   int freq;
220   /**/
221   veclen=ds->veclen;
222   for (i=0; i < veclen; i++) {
223     freq=ds->terms[i].locc;
224     if (freq > 0) 
225       ds->terms[i].tf=freq*freq;
226     else
227       ds->terms[i].tf=0.0;
228   }
229   return;
230 }
231
232 void tf_log(void *rsi, void *dsi) {
233   DS ds=(DS)dsi;
234   int i;
235   int veclen;
236   int freq;
237   /**/    
238   veclen=ds->veclen;
239   for (i=0; i < veclen; i++) {
240     freq=ds->terms[i].locc;
241     if (freq > 0) 
242       ds->terms[i].tf=1+blog2(freq);
243     else
244       ds->terms[i].tf=0.0;
245   }
246   return;
247 }
248
249 /* calculate inverse document frequency vector */
250 void idf_none(void *rsi, void *dsi) {
251   DS ds=(DS)dsi;
252   int i, veclen;
253   /**/
254   veclen=ds->veclen;
255   for (i=0; i < veclen; i++) {
256     ds->terms[i].idf=1.0;
257   }
258   return;
259 }
260
261 void idf_tfidf(void *rsi, void *dsi) {
262   RS rs=(RS)rsi;
263   DS ds=(DS)dsi;
264   int num_docs;
265   int i, veclen;
266   int gocc;
267   double idf;
268   /**/
269   veclen=ds->veclen;
270   num_docs=rs->db_docs;
271   for (i=0; i < veclen; i++) {
272     gocc=ds->terms[i].gocc;
273     if (gocc==0) 
274       idf=0.0; 
275     else
276       idf=blog2(num_docs/gocc);
277     ds->terms[i].idf=idf;
278   }
279   return;
280 }
281
282 void idf_prob(void *rsi, void *dsi) {
283   RS rs=(RS)rsi;
284   DS ds=(DS)dsi;
285   int num_docs;
286   int i, veclen;
287   int gocc;
288   double idf;
289   /**/
290   veclen=ds->veclen;
291   num_docs=rs->db_docs;
292   for (i=0; i < veclen; i++) {
293     gocc=ds->terms[i].gocc;
294     if (gocc==0)
295       idf=0.0; 
296     else
297       idf=(num_docs-gocc)/gocc;
298     ds->terms[i].idf=idf;
299   }
300   return;
301 }
302
303 void idf_freq(void *rsi, void *dsi) {
304   RS rs=(RS)rsi;
305   DS ds=(DS)dsi;
306   int num_docs;
307   int i, veclen;
308   double idf;
309   /**/
310   veclen=ds->veclen;
311   num_docs=rs->db_docs;
312   if (num_docs==0)
313     idf=0.0;
314   else
315     idf=1/num_docs;
316   for (i=0; i < veclen; i++) {
317     ds->terms[i].idf=idf;
318   }
319   return;
320 }
321
322 void idf_squared(void *rsi, void *dsi) {
323   RS rs=(RS)rsi;
324   DS ds=(DS)dsi;
325   int num_docs;
326   int i, veclen;
327   int gocc;
328   double idf;
329   /**/
330   veclen=ds->veclen;
331   num_docs=rs->db_docs;
332   for (i=0; i < veclen; i++) {
333     gocc=ds->terms[i].gocc;
334     if (gocc==0.0)
335       idf=0.0;
336     else
337       idf=blog2(num_docs/gocc);
338       idf=idf*idf;
339       ds->terms[i].idf=idf;
340   }
341   return;
342 }
343
344 /* calculate normalized weight (tf-idf) vector */
345 void norm_none(void *rsi, void *dsi) {
346   DS ds=(DS)dsi;
347   int i, veclen;
348   /**/
349   veclen=ds->veclen;
350   for (i=0; i < veclen; i++) {
351     ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
352   }
353   return;
354 }
355
356 void norm_sum(void *rsi, void *dsi) {
357   DS ds=(DS)dsi;
358   int i, veclen;
359   double tfs=0.0;
360   /**/
361   veclen=ds->veclen;
362   for (i=0; i < veclen; i++) {
363     ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
364     tfs+=ds->terms[i].wt;
365   } 
366   for (i=0; i < veclen; i++) {
367     if (tfs > 0)
368       ds->terms[i].wt=ds->terms[i].wt/tfs;
369   }
370   return;
371 }
372
373 void norm_cosine(void *rsi, void *dsi) {
374   DS ds=(DS)dsi;
375   int i, veclen;
376   double tfs=0.0;
377   /**/
378   veclen=ds->veclen;
379   for (i=0; i < veclen; i++) {
380     ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
381     tfs+=(ds->terms[i].wt*ds->terms[i].wt);
382   } 
383   for (i=0; i < veclen; i++) {
384     if (tfs > 0)
385       ds->terms[i].wt=ds->terms[i].wt/tfs;
386   }
387   return;
388 }
389
390 void norm_fourth(void *rsi, void *dsi) {
391   DS ds=(DS)dsi;
392   int i, veclen;
393   double tfs=0.0, fr;
394   /**/
395   veclen=ds->veclen;
396   for (i=0; i < veclen; i++) {
397     ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
398     fr=(ds->terms[i].wt*ds->terms[i].wt);
399     fr=fr*fr;
400     tfs+=fr; /* ^ 4 */
401   }
402   for (i=0; i < veclen; i++) {
403     if (tfs > 0)
404       ds->terms[i].wt=ds->terms[i].wt/tfs;
405   }
406   return;
407 }
408
409 void norm_max(void *rsi, void *dsi) {
410   DS ds=(DS)dsi;
411   int i, veclen;
412   double tfm=0.0;
413   /**/
414   veclen=ds->veclen;
415   for (i=0; i < veclen; i++) {
416     ds->terms[i].wt=ds->terms[i].tf*ds->terms[i].idf;
417     if (ds->terms[i].wt > tfm)
418       tfm=ds->terms[i].wt;
419   }
420   for (i=0; i < veclen; i++) {
421     if (tfm > 0)
422       ds->terms[i].wt=ds->terms[i].wt/tfm;
423   }
424   return;
425 }
426
427 /* add: norm_pivot, ... */
428
429 double sim_cosine(void *dsi1, void *dsi2) {
430   DS ds1=(DS)dsi1;
431   DS ds2=(DS)dsi2;
432   int i, veclen;
433   double smul=0.0, sdiv=0.0, sqr11=0.0, sqr22=0.0;
434   double v1, v2;
435   /**/
436   veclen=ds1->veclen; /* and ds2->veclen */
437   for (i=0; i < veclen; i++) {
438     v1=ds1->terms[i].wt;
439     v2=ds2->terms[i].wt;
440     smul +=(v1*v2);
441     sqr11+=(v1*v1);
442     sqr22+=(v2*v2);
443   }
444   sdiv=sqrt(sqr11*sqr22);
445   if (sdiv==0.0)
446     return 0.0;
447   return (smul/sdiv);
448 }
449
450 /* add: norm_jaccard, norm_dice, ... */
451
452 /* end weighting functions */
453
454 /* *** */
455
456 /* best-fully-weighted */
457 const char* def_rschema="ntc-atn";
458
459 /* prototype */
460 void zv_init_schema(RS, const char*);
461
462 void zv_init(RS rs) {
463   char *sname="ntc-atn";/* obtain from configuration file */ 
464   yaz_log(LOG_DEBUG, "zv_init\n");
465   /* alloc rs */
466   rs->db_docs=100000;   /* assign correct value here */
467   rs->db_terms=500000;  /* assign correct value here */
468   rs->db_f_max=50;      /* assign correct value here */
469   rs->db_f_max_str="a"; /* assign correct value here */
470   zv_init_schema(rs, sname);
471   return;
472 }
473
474 void zv_init_schema(RS rs, const char *sname) {
475   int slen;
476   char c0, c1, c2, c3, c4, c5, c6;
477   /**/
478   yaz_log(LOG_DEBUG, "zv_init_schema\n");
479   slen=strlen(sname);
480   if (slen>0) c0=sname[0]; else c0=def_rschema[0];
481   if (slen>0) c1=sname[1]; else c1=def_rschema[1];
482   if (slen>0) c2=sname[2]; else c2=def_rschema[2];
483   c3='-';
484   if (slen>0) c4=sname[4]; else c4=def_rschema[4];
485   if (slen>0) c5=sname[5]; else c5=def_rschema[5];
486   if (slen>0) c6=sname[6]; else c6=def_rschema[6];
487   /**/
488   /* assign doc functions */
489   switch (c0) {
490   case 'b':
491     rs->d_tf_fct=tf_binary;
492     rs->rschema[0]='b';
493     break;
494   case 'm':
495     rs->d_tf_fct=tf_max_norm;
496     rs->rschema[0]='m';
497     break;
498   case 'a':
499     rs->d_tf_fct=tf_aug_norm;
500     rs->rschema[0]='a';
501     break;
502   case 's':
503     rs->d_tf_fct=tf_square;
504     rs->rschema[0]='s';
505     break;
506   case 'l':
507     rs->d_tf_fct=tf_log;
508     rs->rschema[0]='l';
509     break;
510   default: /* 'n' */
511     rs->d_tf_fct=tf_none;
512     rs->rschema[0]='n';
513   }
514   switch (c1) {
515   case 't':
516     rs->d_idf_fct=idf_tfidf;
517     rs->rschema[1]='t';
518     break;
519   case 'p':
520     rs->d_idf_fct=idf_prob;
521     rs->rschema[1]='p';
522     break;
523   case 'f':
524     rs->d_idf_fct=idf_freq;
525     rs->rschema[1]='f';
526     break;
527   case 's':
528     rs->d_idf_fct=idf_squared;
529     rs->rschema[1]='s';
530     break;
531   default: /* 'n' */
532     rs->d_idf_fct=idf_none;
533     rs->rschema[1]='n';
534   }
535   switch (c2) {
536   case 's':
537     rs->d_norm_fct=norm_sum;
538     rs->rschema[2]='s';
539     break;
540   case 'c':
541     rs->d_norm_fct=norm_cosine;
542     rs->rschema[2]='c';
543     break;
544   case 'f':
545     rs->d_norm_fct=norm_fourth;
546     rs->rschema[2]='t';
547     break;
548   case 'm':
549     rs->d_norm_fct=norm_max;
550     rs->rschema[2]='m';
551     break;
552   default: /* 'n' */
553     rs->d_norm_fct=norm_none;
554     rs->rschema[2]='n';
555   }
556   /**/
557   rs->rschema[3]='-';
558   /* assign query functions */
559   switch (c4) {
560   case 'b':
561     rs->q_tf_fct=tf_binary;
562     rs->rschema[4]='b';
563     break;
564   case 'm':
565     rs->q_tf_fct=tf_max_norm;
566     rs->rschema[4]='m';
567     break;
568   case 'a':
569     rs->q_tf_fct=tf_aug_norm;
570     rs->rschema[4]='a';
571     break;
572   case 's':
573     rs->q_tf_fct=tf_square;
574     rs->rschema[4]='s';
575     break;
576   case 'l':
577     rs->q_tf_fct=tf_log;
578     rs->rschema[4]='l';
579     break;
580   default: /* 'n' */
581     rs->q_tf_fct=tf_none;
582     rs->rschema[4]='n';
583   }
584   switch (c5) {
585   case 't':
586     rs->q_idf_fct=idf_tfidf;
587     rs->rschema[5]='t';
588     break;
589   case 'p':
590     rs->q_idf_fct=idf_prob;
591     rs->rschema[5]='p';
592     break;
593   case 'f':
594     rs->q_idf_fct=idf_freq;
595     rs->rschema[5]='f';
596     break;
597   case 's':
598     rs->q_idf_fct=idf_squared;
599     rs->rschema[5]='s';
600     break;
601   default: /* 'n' */
602     rs->q_idf_fct=idf_none;
603     rs->rschema[5]='n';
604   }
605   switch (c6) {
606   case 's':
607     rs->q_norm_fct=norm_sum;
608     rs->rschema[6]='s';
609     break;
610   case 'c':
611     rs->q_norm_fct=norm_cosine;
612     rs->rschema[6]='c';
613     break;
614   case 'f':
615     rs->q_norm_fct=norm_fourth;
616     rs->rschema[6]='f';
617     break;
618   case 'm':
619     rs->q_norm_fct=norm_max;
620     rs->rschema[6]='m';
621     break;
622   default: /* 'n' */
623     rs->q_norm_fct=norm_none;
624     rs->rschema[6]='n';
625   }
626   rs->rschema[7]='\0';
627   /**/
628   rs->sim_fct=sim_cosine;
629   yaz_log(LOG_DEBUG, "zv_schema %s\n", rs->rschema);
630
631   return;
632 }
633
634 /******/
635
636 struct rank_class_info { /* where do we need this ? */
637     int dummy;
638 };
639
640 /*
641 struct rank_term_info {
642     int local_occur;
643     int global_occur;
644     int global_inv;
645     int rank_flag;
646     int rank_weight;
647 };
648
649 struct rank_set_info {
650     int last_pos;
651     int no_entries;
652     int no_rank_entries;
653     struct rank_term_info *entries;
654 };
655 */
656
657
658 /*
659  * zv_create: Creates/Initialises this rank handler. This routine is 
660  *  called exactly once. The routine returns the class_handle.
661  */
662 static void *zv_create (ZebraHandle zh) {
663     struct rank_class_info *ci = (struct rank_class_info *)
664         xmalloc (sizeof(*ci));
665     yaz_log(LOG_DEBUG, "zv_create\n");
666     logf (LOG_DEBUG, "zv_create");
667     return ci;
668 }
669
670 /*
671  * zv_destroy: Destroys this rank handler. This routine is called
672  *  when the handler is no longer needed - i.e. when the server
673  *  dies. The class_handle was previously returned by create.
674  */
675 static void zv_destroy (struct zebra_register *reg, void *class_handle) {
676     struct rank_class_info *ci = (struct rank_class_info *) class_handle;
677     yaz_log(LOG_DEBUG, "zv_destroy\n");
678     logf (LOG_DEBUG, "zv_destroy");
679     xfree (ci);
680 }
681
682
683 /*
684  * zv_begin: Prepares beginning of "real" ranking. Called once for
685  *  each result set. The returned handle is a "set handle" and
686  *  will be used in each of the handlers below.
687  */
688 static void *zv_begin (struct zebra_register *reg, void *class_handle, RSET rset)
689 {
690   struct rs_info *rs=(struct rs_info *)xmalloc(sizeof(*rs));
691   int i;
692   int veclen, gocc;
693   /**/
694   logf (LOG_DEBUG, "rank-1 zvbegin");
695   yaz_log(LOG_DEBUG, "zv_begin\n");
696   veclen=rset->no_rset_terms; /* smaller vector here */
697   zv_init(rs);
698   rs->veclen=veclen;
699   prn_rs(rs);
700   
701   rs->qdoc=(struct ds_info *)xmalloc(sizeof(*rs->qdoc));
702   rs->qdoc->terms=(struct ts_info *)xmalloc(sizeof(*rs->qdoc->terms)*rs->veclen);
703   rs->qdoc->veclen=veclen;
704   
705   rs->rdoc=(struct ds_info *)xmalloc(sizeof(*rs->rdoc));
706   rs->rdoc->terms=(struct ts_info *)xmalloc(sizeof(*rs->rdoc->terms)*rs->veclen);
707   rs->rdoc->veclen=veclen;
708   /* yaz_log(LOG_DEBUG, "zv_begin_init\n"); */
709   for (i = 0; i < rs->veclen; i++)
710     {
711       gocc=rset->rset_terms[i]->nn;
712       /* yaz_log(LOG_DEBUG, "zv_begin_init i=%d gocc=%d\n", i, gocc); */
713       /* "rank": check name from config file */ 
714       if (!strncmp (rset->rset_terms[i]->flags, "rank,", 5)) {
715         yaz_log (LOG_LOG, "%s", rset->rset_terms[i]->flags);
716         /*si->entries[i].rank_flag = 1;
717           (si->no_rank_entries)++;
718         */
719       } else {
720         /* si->entries[i].rank_flag = 0; */
721       }
722       rs->qdoc->terms[i].gocc=gocc;
723       rs->qdoc->terms[i].locc=1;  /* assume query has no duplicates */
724       rs->rdoc->terms[i].gocc=gocc;
725       rs->rdoc->terms[i].locc=0;
726     }
727   return rs;
728 }
729
730 /*
731  * zv_end: Terminates ranking process. Called after a result set
732  *  has been ranked.
733  */
734 static void zv_end (struct zebra_register *reg, void *rsi)
735 {
736   RS rs=(RS)rsi;
737   yaz_log(LOG_DEBUG, "zv_end\n");
738   logf (LOG_DEBUG, "rank-1 end");
739   xfree(rs->qdoc->terms);
740   xfree(rs->rdoc->terms);
741   xfree(rs->qdoc);
742   xfree(rs->rdoc);
743   xfree(rs);
744   return;
745 }
746
747 /*
748  * zv_add: Called for each word occurence in a result set. This routine
749  *  should be as fast as possible. This routine should "incrementally"
750  *  update the score.
751  */
752 static void zv_add (void *rsi, int seqno, int i) {
753     RS rs=(RS)rsi;
754     /*logf (LOG_DEBUG, "rank-1 add seqno=%d term_index=%d", seqno, term_index);*/
755     /*si->last_pos = seqno;*/
756     rs->rdoc->terms[i].locc++;
757 }
758
759 /*
760  * zv_calc: Called for each document in a result. This handler should 
761  *  produce a score based on previous call(s) to the add handler. The
762  *  score should be between 0 and 1000. If score cannot be obtained
763  *  -1 should be returned.
764  */
765 static int zv_calc (void *rsi, int sysno)
766 {
767     int i, veclen; 
768     int score=0;
769     double dscore=0.0;
770     RS rs=(RS)rsi;
771     /* yaz_log(LOG_DEBUG, "zv_calc\n"); */
772     /**/
773     veclen=rs->veclen;
774     if (veclen==0)
775         return -1;
776     for (i = 0; i < veclen; i++) {
777         (*rs->q_tf_fct)(rs, rs->qdoc); /* we should actually do this once */
778         (*rs->q_idf_fct)(rs, rs->qdoc);
779         (*rs->q_norm_fct)(rs, rs->qdoc);
780
781         (*rs->d_tf_fct)(rs, rs->rdoc);
782         (*rs->d_idf_fct)(rs, rs->rdoc);
783         (*rs->d_norm_fct)(rs, rs->rdoc);
784         
785         dscore=rs->sim_fct(rs->qdoc, rs->rdoc);
786     }
787     score = dscore * 1000;
788     yaz_log (LOG_LOG, "sysno=%d score=%d", sysno, score);
789     if (score > 1000)
790         score = 1000;
791     /*
792       for (i = 0; i < si->no_entries; i++)
793         si->entries[i].local_occur = 0;
794     */
795     return score;
796 }
797
798 /*
799  * Pseudo-meta code with sequence of calls as they occur in a
800  * server. Handlers are prefixed by --:
801  *
802  *     server init
803  *     -- create
804  *     foreach search
805  *        rank result set
806  *        -- begin
807  *        foreach record
808  *           foreach word
809  *              -- add
810  *           -- calc
811  *        -- end
812  *     -- destroy
813  *     server close
814  */
815
816 static struct rank_control rank_control_vsm = {
817     "zvrank", /* "zv_rank", */ /* zvrank */
818     zv_create,
819     zv_destroy,
820     zv_begin,
821     zv_end,
822     zv_calc,
823     zv_add,
824 };
825  
826 struct rank_control *rankzv_class = &rank_control_vsm;
827
828 /* EOF */