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