New method result set method rs_hits that returns the number of
[idzebra-moved-to-github.git] / rset / rsrel.c
1 /*
2  * Copyright (C) 1994-1997, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: rsrel.c,v $
7  * Revision 1.22  1997-12-18 10:54:25  adam
8  * New method result set method rs_hits that returns the number of
9  * hits in result-set (if known). The ranked result set returns real
10  * number of hits but only when not combined with other operands.
11  *
12  * Revision 1.21  1997/11/18 10:05:08  adam
13  * Changed character map facility so that admin can specify character
14  * mapping files for each register type, w, p, etc.
15  *
16  * Revision 1.20  1997/10/31 12:37:55  adam
17  * Code calls xfree() instead of free().
18  *
19  * Revision 1.19  1997/10/01 11:44:06  adam
20  * Small improvement of new ranking.
21  *
22  * Revision 1.18  1997/09/24 13:36:41  adam
23  * More work on new ranking algorithm.
24  *
25  * Revision 1.17  1997/09/22 12:39:07  adam
26  * Added get_pos method for the ranked result sets.
27  *
28  * Revision 1.16  1997/09/17 12:19:23  adam
29  * Zebra version corresponds to YAZ version 1.4.
30  * Changed Zebra server so that it doesn't depend on global common_resource.
31  *
32  * Revision 1.15  1997/09/09 13:38:16  adam
33  * Partial port to WIN95/NT.
34  *
35  * Revision 1.14  1996/11/08 11:15:58  adam
36  * Compressed isam fully supported.
37  *
38  * Revision 1.13  1996/10/29 13:55:26  adam
39  * Include of zebrautl.h instead of alexutil.h.
40  *
41  * Revision 1.12  1996/10/08 13:00:40  adam
42  * Bug fix: result sets with ranked operands in boolean operations weren't
43  * sorted.
44  *
45  * Revision 1.11  1996/10/07  16:05:29  quinn
46  * Work.
47  *
48  * Revision 1.9  1995/12/11  09:15:26  adam
49  * New set types: sand/sor/snot - ranked versions of and/or/not in
50  * ranked/semi-ranked result sets.
51  * Note: the snot not finished yet.
52  * New rset member: flag.
53  * Bug fix: r_delete in rsrel.c did free bad memory block.
54  *
55  * Revision 1.8  1995/12/05  11:25:45  adam
56  * Doesn't include math.h.
57  *
58  * Revision 1.7  1995/10/12  12:41:57  adam
59  * Private info (buf) moved from struct rset_control to struct rset.
60  * Bug fixes in relevance.
61  *
62  * Revision 1.6  1995/10/10  14:00:04  adam
63  * Function rset_open changed its wflag parameter to general flags.
64  *
65  * Revision 1.5  1995/10/06  14:38:06  adam
66  * New result set method: r_score.
67  * Local no (sysno) and score is transferred to retrieveCtrl.
68  *
69  * Revision 1.4  1995/09/14  07:48:56  adam
70  * Other score calculation.
71  *
72  * Revision 1.3  1995/09/11  15:23:40  adam
73  * More work on relevance search.
74  *
75  * Revision 1.2  1995/09/11  13:09:41  adam
76  * More work on relevance feedback.
77  *
78  * Revision 1.1  1995/09/08  14:52:42  adam
79  * Work on relevance feedback.
80  *
81  */
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <assert.h>
87
88 #include <isam.h>
89 #include <isamc.h>
90 #include <rsrel.h>
91 #include <zebrautl.h>
92
93 static void *r_create(const struct rset_control *sel, void *parms,
94                       int *flags);
95 static RSFD r_open (RSET ct, int flag);
96 static void r_close (RSFD rfd);
97 static void r_delete (RSET ct);
98 static void r_rewind (RSFD rfd);
99 static int r_count (RSET ct);
100 static int r_hits (RSET ct, void *oi);
101 static int r_read (RSFD rfd, void *buf);
102 static int r_write (RSFD rfd, const void *buf);
103 static int r_score (RSFD rfd, int *score);
104
105 static const rset_control control = 
106 {
107     "relevance",
108     r_create,
109     r_open,
110     r_close,
111     r_delete,
112     r_rewind,
113     r_count,
114     r_hits,
115     r_read,
116     r_write,
117     r_score
118 };
119
120 const rset_control *rset_kind_relevance = &control;
121
122 struct rset_rel_info {
123     int     key_size;
124     int     max_rec;
125     int     no_rec;
126     int     hits;                       /* hits count */
127     int     (*cmp)(const void *p1, const void *p2);
128     int     (*get_pos)(const void *p);
129     char    *key_buf;                   /* key buffer */
130     float   *score_buf;                 /* score buffer */
131     int     *sort_idx;                  /* score sorted index */
132     int     *sysno_idx;                 /* sysno sorted index (ring buffer) */
133     struct rset_rel_rfd *rfd_list;
134 };
135
136 struct rset_rel_rfd {
137     int     last_read_pos;
138     int     position;
139     int     flag;
140     struct rset_rel_rfd *next;
141     struct rset_rel_info *info;
142 };
143
144 static void add_rec (struct rset_rel_info *info, double score, void *key)
145 {
146     int idx, i, j;
147
148     (info->hits)++;
149     for (i = 0; i<info->no_rec; i++)
150     {
151         idx = info->sort_idx[i];
152         if (score <= info->score_buf[idx])
153             break;
154     }
155     if (info->no_rec < info->max_rec)
156     {                                        /* there is room for this entry */
157         for (j = info->no_rec; j > i; --j)
158             info->sort_idx[j] = info->sort_idx[j-1];
159         idx = info->sort_idx[j] = info->no_rec;
160         ++(info->no_rec);
161     }
162     else if (i == 0)
163         return;                              /* score too low */
164     else
165     {
166         idx = info->sort_idx[0];             /* remove this entry */
167
168         --i;
169         for (j = 0; j < i; ++j)              /* make room */
170             info->sort_idx[j] = info->sort_idx[j+1];
171         info->sort_idx[j] = idx;             /* allocate sort entry */
172     }
173     memcpy (info->key_buf + idx*info->key_size, key, info->key_size);
174     info->score_buf[idx] = score;
175 }
176
177
178 static struct rset_rel_info *qsort_info;
179
180 static int qcomp (const void *p1, const void *p2)
181 {
182     int i1 = *(int*) p1;
183     int i2 = *(int*) p2;
184
185     return qsort_info->cmp (qsort_info->key_buf + i1*qsort_info->key_size,
186                             qsort_info->key_buf + i2*qsort_info->key_size);
187 }
188
189 #define NEW_RANKING 0
190
191 #define SCORE_SHOW 0.0                       /* base score for showing up */
192 #define SCORE_COOC 0.3                       /* component dependent on co-oc */
193 #define SCORE_DYN  (1-(SCORE_SHOW+SCORE_COOC)) /* dynamic component of score */
194
195 static void relevance (struct rset_rel_info *info, rset_relevance_parms *parms)
196 {
197     char **isam_buf;
198     char *isam_tmp_buf;
199     int  *isam_r;
200     int  *max_tf, *tf;
201
202     int  *pos_tf = NULL;
203     int score_sum = 0;
204     int no_occur = 0;
205     char *isam_prev_buf = NULL;
206     int fact1, fact2;
207
208     ISPT *isam_pt = NULL;
209     ISAMC_PP *isamc_pp = NULL;
210     int i;
211
212     logf (LOG_DEBUG, "relevance");
213     isam_buf = xmalloc (parms->no_isam_positions * sizeof(*isam_buf));
214     isam_r = xmalloc (sizeof (*isam_r) * parms->no_isam_positions);
215     if (parms->is)
216         isam_pt = xmalloc (sizeof (*isam_pt) * parms->no_isam_positions);
217     else if (parms->isc)
218         isamc_pp = xmalloc (sizeof (*isamc_pp) * parms->no_isam_positions);
219     else
220     {
221         logf (LOG_FATAL, "No isamc or isam in rs_rel");
222         abort ();
223     }
224     isam_tmp_buf = xmalloc (info->key_size);
225     max_tf = xmalloc (sizeof (*max_tf) * parms->no_terms);
226     tf = xmalloc (sizeof (*tf) * parms->no_terms);
227
228     for (i = 0; i<parms->no_terms; i++)
229         max_tf[i] = 0;
230     for (i = 0; i<parms->no_isam_positions; i++)
231     {
232         isam_buf[i] = xmalloc (info->key_size);
233         if (isam_pt)
234         {
235             isam_pt[i] = is_position (parms->is, parms->isam_positions[i]);
236             max_tf [parms->term_no[i]] = is_numkeys (isam_pt[i]);
237             isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
238         } 
239         else if (isamc_pp)
240         {
241             isamc_pp[i] = isc_pp_open (parms->isc, parms->isam_positions[i]);
242             max_tf [parms->term_no[i]] = isc_pp_num (isamc_pp[i]);
243             isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
244         }
245         logf (LOG_DEBUG, "max tf %d = %d", i, max_tf[i]);
246     }
247     switch (parms->method)
248     {
249     case RSREL_METHOD_B:
250         while (1)
251         {
252             int r, min = -1;
253             int pos = 0;
254             for (i = 0; i<parms->no_isam_positions; i++)
255                 if (isam_r[i] && 
256                     (min < 0 ||
257                      (r = (*parms->cmp)(isam_buf[i], isam_buf[min])) < 1))
258                     min = i;
259             if (!isam_prev_buf)
260             {
261                 pos_tf = xmalloc (sizeof(*pos_tf) * parms->no_isam_positions);
262                 isam_prev_buf = xmalloc (info->key_size);
263                 fact1 = 100000/parms->no_isam_positions;
264                 fact2 = 100000/
265                     (parms->no_isam_positions*parms->no_isam_positions);
266                 
267                 no_occur = score_sum = 0;
268                 memcpy (isam_prev_buf, isam_buf[min], info->key_size);
269                 for (i = 0; i<parms->no_isam_positions; i++)
270                     pos_tf[i] = -10;
271             }
272             else if (min < 0 ||
273                      (*parms->cmp)(isam_buf[min], isam_prev_buf) > 1)
274             {
275                 logf (LOG_LOG, "final occur = %d ratio=%d",
276                       no_occur, score_sum / no_occur);
277                 add_rec (info, score_sum / (10000.0*no_occur), isam_prev_buf);
278                 if (min < 0)
279                     break;
280                 no_occur = score_sum = 0;
281                 memcpy (isam_prev_buf, isam_buf[min], info->key_size);
282                 for (i = 0; i<parms->no_isam_positions; i++)
283                     pos_tf[i] = -10;
284             }
285             pos = (*parms->get_pos)(isam_buf[min]);
286             logf (LOG_LOG, "pos=%d", pos);
287             for (i = 0; i<parms->no_isam_positions; i++)
288             {
289                 int d = pos - pos_tf[i];
290                 
291                 no_occur++;
292                 if (pos_tf[i] < 0 && i != min)
293                     continue;
294                 if (d < 10)
295                     d = 10;
296                 if (i == min)
297                     score_sum += fact2 / d;
298                 else
299                     score_sum += fact1 / d;
300             }
301             pos_tf[min] = pos;
302             logf (LOG_LOG, "score_sum = %d", score_sum);
303             i = min;
304             if (isam_pt)
305                 isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
306             else if (isamc_pp)
307                 isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
308         } /* while */
309         xfree (isam_prev_buf);
310         xfree (pos_tf);
311         break;
312     case RSREL_METHOD_A:
313         while (1)
314         {
315             int min = -1, i, r;
316             double score;
317             int co_oc, last_term;    /* Number of co-occurrences */
318             
319             last_term = -1;
320             /* find min with lowest sysno */
321             for (i = 0; i<parms->no_isam_positions; i++)
322             {
323                 if (isam_r[i] && 
324                     (min < 0
325                      || (r = (*parms->cmp)(isam_buf[i], isam_buf[min])) < 2))
326                 {
327                     min = i;
328                     co_oc = 1;
329                 }
330                 else if (!r && last_term != parms->term_no[i]) 
331                     co_oc++;  /* new occurrence */
332                 last_term = parms->term_no[i];
333             }
334             
335             if (min < 0)
336                 break;
337             memcpy (isam_tmp_buf, isam_buf[min], info->key_size);
338             /* calculate for all with those sysno */
339             for (i = 0; i < parms->no_terms; i++)
340                 tf[i] = 0;
341             for (i = 0; i<parms->no_isam_positions; i++)
342             {
343                 int r;
344                 
345                 if (isam_r[i])
346                     r = (*parms->cmp)(isam_buf[i], isam_tmp_buf);
347                 else 
348                     r = 2;
349                 if (r <= 1 && r >= -1)
350                 {
351                     do
352                     {
353                         tf[parms->term_no[i]]++;
354                         if (isam_pt)
355                             isam_r[i] = is_readkey (isam_pt[i], isam_buf[i]);
356                         else if (isamc_pp)
357                             isam_r[i] = isc_pp_read (isamc_pp[i], isam_buf[i]);
358                     } while (isam_r[i] && 
359                              (*parms->cmp)(isam_buf[i], isam_tmp_buf) <= 1);
360                 }
361             }
362             /* calculate relevance value */
363             score = 0.0;
364             for (i = 0; i<parms->no_terms; i++)
365                 if (tf[i])
366                     score += SCORE_SHOW + SCORE_COOC*co_oc/parms->no_terms +
367                         SCORE_DYN*tf[i]/max_tf[i];
368             /* if value is in the top score, then save it - don't emit yet */
369             add_rec (info, score/parms->no_terms, isam_tmp_buf);
370         } /* while */
371         break;
372     } /* switch */
373     for (i = 0; i<info->no_rec; i++)
374         info->sysno_idx[i] = i;
375     qsort_info = info;
376     qsort (info->sysno_idx, info->no_rec, sizeof(*info->sysno_idx), qcomp);
377     for (i = 0; i<parms->no_isam_positions; i++)
378     {
379         if (isam_pt)
380             is_pt_free (isam_pt[i]);
381         if (isamc_pp)
382             isc_pp_close (isamc_pp[i]);
383         xfree (isam_buf[i]);
384     }
385     xfree (max_tf);
386     xfree (isam_tmp_buf);
387     xfree (isam_buf);
388     xfree (isam_r);
389     xfree (isam_pt);
390     xfree (isamc_pp);
391     xfree(tf);
392 }
393
394 static void *r_create (const struct rset_control *sel, void *parms,
395                        int *flags)
396 {
397     rset_relevance_parms *r_parms = parms;
398     struct rset_rel_info *info;
399
400     *flags |= RSET_FLAG_RANKED;
401     info = xmalloc (sizeof(struct rset_rel_info));
402     info->key_size = r_parms->key_size;
403     assert (info->key_size > 1);
404     info->max_rec = r_parms->max_rec;
405     assert (info->max_rec > 1);
406     info->cmp = r_parms->cmp;
407     info->get_pos = r_parms->get_pos;
408
409     info->key_buf = xmalloc (info->key_size * info->max_rec);
410     info->score_buf = xmalloc (sizeof(*info->score_buf) * info->max_rec);
411     info->sort_idx = xmalloc (sizeof(*info->sort_idx) * info->max_rec);
412     info->sysno_idx = xmalloc (sizeof(*info->sysno_idx) * info->max_rec);
413     info->no_rec = 0;
414     info->hits = 0;
415     info->rfd_list = NULL;
416
417     relevance (info, r_parms);
418     return info;
419 }
420
421 static RSFD r_open (RSET ct, int flag)
422 {
423     struct rset_rel_rfd *rfd;
424     struct rset_rel_info *info = ct->buf;
425
426     if (flag & RSETF_WRITE)
427     {
428         logf (LOG_FATAL, "relevance set type is read-only");
429         return NULL;
430     }
431     rfd = xmalloc (sizeof(*rfd));
432     rfd->flag = flag;
433     rfd->next = info->rfd_list;
434     rfd->info = info;
435     info->rfd_list = rfd;
436     r_rewind (rfd);
437     return rfd;
438 }
439
440 static void r_close (RSFD rfd)
441 {
442     struct rset_rel_info *info = ((struct rset_rel_rfd*)rfd)->info;
443     struct rset_rel_rfd **rfdp;
444     
445     for (rfdp = &info->rfd_list; *rfdp; rfdp = &(*rfdp)->next)
446         if (*rfdp == rfd)
447         {
448             *rfdp = (*rfdp)->next;
449             xfree (rfd);
450             return;
451         }
452     logf (LOG_FATAL, "r_close but no rfd match!");
453     assert (0);
454 }
455
456 static void r_delete (RSET ct)
457 {
458     struct rset_rel_info *info = ct->buf;
459
460     assert (info->rfd_list == NULL);
461     xfree (info->key_buf);
462     xfree (info->score_buf);
463     xfree (info->sort_idx);
464     xfree (info->sysno_idx);
465     xfree (info);
466 }
467
468 static void r_rewind (RSFD rfd)
469 {
470     struct rset_rel_rfd *p = rfd;
471     struct rset_rel_info *info = p->info;
472
473     if (p->flag & RSETF_SORT_RANK)
474         p->position = info->no_rec;
475     else
476         p->position = 0;
477 }
478
479 static int r_count (RSET ct)
480 {
481     struct rset_rel_info *info = ct->buf;
482
483     return info->no_rec;
484 }
485
486 static int r_hits (RSET ct, void *oi)
487 {
488     struct rset_rel_info *info = ct->buf;
489
490     return info->hits;
491 }
492
493 static int r_read (RSFD rfd, void *buf)
494 {
495     struct rset_rel_rfd *p = rfd;
496     struct rset_rel_info *info = p->info;
497
498     if (p->flag & RSETF_SORT_RANK)
499     {
500         if (p->position <= 0)
501             return 0;
502         --(p->position);
503         p->last_read_pos = info->sort_idx[p->position];
504     }
505     else
506     {
507         if (p->position == info->no_rec)
508             return 0;
509         p->last_read_pos = info->sysno_idx[p->position];
510         ++(p->position);
511     }
512     memcpy ((char*) buf,
513             info->key_buf + info->key_size * p->last_read_pos,
514             info->key_size);
515     return 1;
516 }
517
518 static int r_score (RSFD rfd, int *score)
519 {
520     struct rset_rel_rfd *p = rfd;
521     struct rset_rel_info *info = p->info;
522
523     *score = (int) (1000*info->score_buf[p->last_read_pos]);
524     return 1;
525 }
526
527 static int r_write (RSFD rfd, const void *buf)
528 {
529     logf (LOG_FATAL, "relevance set type is read-only");
530     return -1;
531 }