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