Bug fix in ranked search. Use=Any keys inserted.
[idzebra-moved-to-github.git] / index / zrpn.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zrpn.c,v $
7  * Revision 1.17  1995-10-04 12:55:17  adam
8  * Bug fix in ranked search. Use=Any keys inserted.
9  *
10  * Revision 1.16  1995/10/02  16:24:40  adam
11  * Use attribute actually used in search requests.
12  *
13  * Revision 1.15  1995/10/02  15:18:52  adam
14  * New member in recRetrieveCtrl: diagnostic.
15  *
16  * Revision 1.14  1995/09/28  12:10:32  adam
17  * Bug fixes. Field prefix used in queries.
18  *
19  * Revision 1.13  1995/09/18  14:17:50  adam
20  * Minor changes.
21  *
22  * Revision 1.12  1995/09/15  14:45:21  adam
23  * Retrieve control.
24  * Work on truncation.
25  *
26  * Revision 1.11  1995/09/14  11:53:27  adam
27  * First work on regular expressions/truncations.
28  *
29  * Revision 1.10  1995/09/11  15:23:26  adam
30  * More work on relevance search.
31  *
32  * Revision 1.9  1995/09/11  13:09:35  adam
33  * More work on relevance feedback.
34  *
35  * Revision 1.8  1995/09/08  14:52:27  adam
36  * Minor changes. Dictionary is lower case now.
37  *
38  * Revision 1.7  1995/09/07  13:58:36  adam
39  * New parameter: result-set file descriptor (RSFD) to support multiple
40  * positions within the same result-set.
41  * Boolean operators: and, or, not implemented.
42  * Result-set references.
43  *
44  * Revision 1.6  1995/09/06  16:11:18  adam
45  * Option: only one word key per file.
46  *
47  * Revision 1.5  1995/09/06  10:33:04  adam
48  * More work on present. Some log messages removed.
49  *
50  * Revision 1.4  1995/09/05  15:28:40  adam
51  * More work on search engine.
52  *
53  * Revision 1.3  1995/09/04  15:20:22  adam
54  * Minor changes.
55  *
56  * Revision 1.2  1995/09/04  12:33:43  adam
57  * Various cleanup. YAZ util used instead.
58  *
59  * Revision 1.1  1995/09/04  09:10:40  adam
60  * More work on index add/del/update.
61  * Merge sort implemented.
62  * Initial work on z39 server.
63  *
64  */
65 #include <stdio.h>
66 #include <assert.h>
67 #include <unistd.h>
68
69 #include "zserver.h"
70
71 #include <rsisam.h>
72 #include <rstemp.h>
73 #include <rsnull.h>
74 #include <rsbool.h>
75 #include <rsrel.h>
76
77 /*
78  * attr_print: log attributes
79  */
80 static void attr_print (Z_AttributesPlusTerm *t)
81 {
82     int of, i;
83     for (of = 0; of < t->num_attributes; of++)
84     {
85         Z_AttributeElement *element;
86         element = t->attributeList[of];
87
88         switch (element->which) 
89         {
90         case Z_AttributeValue_numeric:
91             logf (LOG_DEBUG, "attributeType=%d value=%d", 
92                   *element->attributeType,
93                   *element->value.numeric);
94             break;
95         case Z_AttributeValue_complex:
96             logf (LOG_DEBUG, "attributeType=%d complex", 
97                   *element->attributeType);
98             for (i = 0; i<element->value.complex->num_list; i++)
99             {
100                 if (element->value.complex->list[i]->which ==
101                     Z_StringOrNumeric_string)
102                     logf (LOG_DEBUG, "   string: '%s'",
103                           element->value.complex->list[i]->u.string);
104                 else if (element->value.complex->list[i]->which ==
105                          Z_StringOrNumeric_numeric)
106                     logf (LOG_DEBUG, "   numeric: '%d'",
107                           *element->value.complex->list[i]->u.numeric);
108             }
109             break;
110         default:
111             assert (0);
112         }
113     }
114 }
115
116 typedef struct {
117     int type;
118     int major;
119     int minor;
120     Z_AttributesPlusTerm *zapt;
121 } AttrType;
122
123 static int attr_find (AttrType *src)
124 {
125     while (src->major < src->zapt->num_attributes)
126     {
127         Z_AttributeElement *element;
128         element = src->zapt->attributeList[src->major];
129
130         if (src->type == *element->attributeType)
131         {
132             switch (element->which) 
133             {
134             case Z_AttributeValue_numeric:
135                 ++(src->major);
136                 return *element->value.numeric;
137                 break;
138             case Z_AttributeValue_complex:
139                 if (src->minor >= element->value.complex->num_list ||
140                     element->value.complex->list[src->minor]->which !=  
141                     Z_StringOrNumeric_numeric)
142                     break;
143                 ++(src->minor);
144                 return *element->value.complex->list[src->minor-1]->u.numeric;
145             default:
146                 assert (0);
147             }
148         }
149         ++(src->major);
150     }
151     return -1;
152 }
153
154 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
155                        int type)
156 {
157     src->zapt = zapt;
158     src->type = type;
159     src->major = 0;
160     src->minor = 0;
161 }
162
163 struct trunc_info {
164     int  *indx;
165     char **heap;
166     int  heapnum;
167     int  (*cmp)(const void *p1, const void *p2);
168     int  keysize;
169     char *swapbuf;
170     char *tmpbuf;
171     char *buf;
172 };
173
174 static void heap_swap (struct trunc_info *ti, int i1, int i2)
175 {
176     int swap;
177
178     memcpy (ti->swapbuf, ti->heap[i1], ti->keysize);
179     memcpy (ti->heap[i1], ti->heap[i2], ti->keysize);
180     memcpy (ti->heap[i2], ti->swapbuf, ti->keysize);
181
182     swap = ti->indx[i1];
183     ti->indx[i1] = ti->indx[i2];
184     ti->indx[i2] = swap;
185 }
186
187 static void heap_delete (struct trunc_info *ti)
188 {
189     int cur = 1, child = 2;
190
191     assert (ti->heapnum > 0);
192     memcpy (ti->heap[1], ti->heap[ti->heapnum], ti->keysize);
193     ti->indx[1] = ti->indx[ti->heapnum--];
194     while (child <= ti->heapnum) {
195         if (child < ti->heapnum &&
196             (*ti->cmp)(ti->heap[child], ti->heap[1+child]) > 0)
197             child++;
198         if ((*ti->cmp)(ti->heap[cur], ti->heap[child]) > 0)
199         {
200             heap_swap (ti, cur, child);
201             cur = child;
202             child = 2*cur;
203         }
204         else
205             break;
206     }
207 }
208
209 static void heap_insert (struct trunc_info *ti, const char *buf, int indx)
210 {
211     int cur, parent;
212
213     cur = ++(ti->heapnum);
214     memcpy (ti->heap[cur], buf, ti->keysize);
215     ti->indx[cur] = indx;
216     parent = cur/2;
217     while (parent && (*ti->cmp)(ti->heap[parent], ti->heap[cur]) > 0)
218     {
219         heap_swap (ti, cur, parent);
220         cur = parent;
221         parent = cur/2;
222     }
223 }
224
225 static
226 struct trunc_info *heap_init (int size, int key_size,
227                               int (*cmp)(const void *p1, const void *p2))
228 {
229     struct trunc_info *ti = xmalloc (sizeof(*ti));
230     int i;
231
232     ++size;
233     ti->heapnum = 0;
234     ti->keysize = key_size;
235     ti->cmp = cmp;
236     ti->indx = xmalloc (size * sizeof(*ti->indx));
237     ti->heap = xmalloc (size * sizeof(*ti->heap));
238     ti->swapbuf = xmalloc (ti->keysize);
239     ti->tmpbuf = xmalloc (ti->keysize);
240     ti->buf = xmalloc (size * ti->keysize);
241     for (i = size; --i >= 0; )
242         ti->heap[i] = ti->buf + ti->keysize * i;
243     return ti;
244 }
245
246 static void heap_close (struct trunc_info *ti)
247 {
248     xfree (ti->indx);
249     xfree (ti->heap);
250     xfree (ti->swapbuf);
251     xfree (ti->tmpbuf);
252     xfree (ti);
253 }
254
255 static RSET rset_trunc (ISAM isam, ISAM_P *isam_p, int from, int to,
256                         int merge_chunk)
257 {
258     logf (LOG_DEBUG, "rset_trunc, range=%d-%d", from, to-1);
259     if (to - from > merge_chunk)
260     {
261         return NULL;
262     }
263     else
264     {
265         ISPT *ispt;
266         int i;
267         struct trunc_info *ti;
268         RSET result;
269         RSFD rsfd;
270         rset_temp_parms parms;
271
272         ispt = xmalloc (sizeof(*ispt) * (to-from));
273         parms.key_size = sizeof (struct it_key);
274         result = rset_create (rset_kind_temp, &parms);
275         rsfd = rset_open (result, 1);
276
277         ti = heap_init (to-from, sizeof(struct it_key),
278                         key_compare);
279         for (i = to-from; --i >= 0; )
280         {
281             ispt[i] = is_position (isam, isam_p[from+i]);
282             if (is_readkey (ispt[i], ti->tmpbuf))
283                 heap_insert (ti, ti->tmpbuf, i);
284         }
285         while (ti->heapnum)
286         {
287             int n = ti->indx[1];
288
289             rset_write (result, rsfd, ti->heap[1]);
290             heap_delete (ti);
291             if (is_readkey (ispt[n], ti->tmpbuf))
292                 heap_insert (ti, ti->tmpbuf, n);
293         }
294         for (i = to-from; --i >= 0; )
295             is_pt_free (ispt[i]);
296         rset_close (result, rsfd);
297         heap_close (ti);
298         xfree (ispt);
299         return result;
300     }
301 }
302
303 static ISAM_P *isam_p_buf = NULL;
304 static int isam_p_size = 0;
305 static int isam_p_indx;
306
307 static void add_isam_p (const char *info)
308 {
309     if (isam_p_indx == isam_p_size)
310     {
311         ISAM_P *new_isam_p_buf;
312         
313         isam_p_size = 2*isam_p_size + 100;
314         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
315                                   isam_p_size);
316         if (isam_p_buf)
317         {
318             memcpy (new_isam_p_buf, isam_p_buf,
319                     isam_p_indx * sizeof(*isam_p_buf));
320             xfree (isam_p_buf);
321         }
322         isam_p_buf = new_isam_p_buf;
323     }
324     assert (*info == sizeof(*isam_p_buf));
325     memcpy (isam_p_buf + isam_p_indx, info+1, sizeof(*isam_p_buf));
326     isam_p_indx++;
327 }
328
329 static int grep_handle (Dict_char *name, const char *info)
330 {
331     logf (LOG_DEBUG, "dict name: %s", name);
332     add_isam_p (info);
333     return 0;
334 }
335
336 static int trunc_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
337                        const char *term_sub, ISAM_P **isam_ps)
338 {
339     char term_dict[2*IT_MAX_WORD+2];
340     int i, j;
341     const char *info;    
342     AttrType truncation;
343     int truncation_value;
344     AttrType use;
345     int use_value;
346
347     attr_init (&use, zapt, 1);
348     use_value = attr_find (&use);
349     logf (LOG_DEBUG, "use value %d", truncation_value);
350     attr_init (&truncation, zapt, 5);
351     truncation_value = attr_find (&truncation);
352     logf (LOG_DEBUG, "truncation value %d", truncation_value);
353
354     if (use_value == -1)
355         use_value = 1016;
356     i = index_word_prefix (term_dict, 1, use_value);
357
358     switch (truncation_value)
359     {
360     case -1:         /* not specified */
361     case 100:        /* do not truncate */
362         strcat (term_dict, term_sub);
363         logf (LOG_DEBUG, "dict_lookup: %s", term_dict);
364         if ((info = dict_lookup (zi->wordDict, term_dict)))
365             add_isam_p (info);
366         break;
367     case 1:          /* right truncation */
368         strcat (term_dict, term_sub);
369         strcat (term_dict, ".*");
370         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
371         break;
372     case 2:          /* left truncation */
373     case 3:          /* left&right truncation */
374         zi->errCode = 120;
375         return -1;
376     case 101:        /* process # in term */
377         for (j = strlen(term_dict), i = 0; term_sub[i] && i < 2; i++)
378             term_dict[j++] = term_sub[i];
379         for (; term_sub[i]; i++)
380             if (term_sub[i] == '#')
381             {
382                 term_dict[j++] = '.';
383                 term_dict[j++] = '*';
384             }
385             else
386                 term_dict[j++] = term_sub[i];
387         term_dict[j] = '\0';
388         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
389         break;
390     case 102:        /* regular expression */
391         strcat (term_dict, term_sub);
392         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
393         break;
394     }
395     *isam_ps = isam_p_buf;
396     logf (LOG_DEBUG, "%d positions", isam_p_indx);
397     return 0;
398 }
399
400 #if 0
401 static void field_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
402                         char *termz)
403 {
404     size_t i, j, sizez;
405     AttrType use;
406     int use_value;
407     Z_Term *term = zapt->term;
408
409     attr_init (&use, zapt, 1);
410     use_value = attr_find (&use);
411     if (use_value == -1)
412         use_value = 1016;
413
414     i = index_word_prefix (termz, 1, use_value);
415     sizez = i + term->u.general->len;
416     if (sizez > IT_MAX_WORD)
417         sizez = IT_MAX_WORD;
418     for (j = 0; i < sizez; i++, j++)
419         termz[i] = index_char_cvt (term->u.general->buf[j]);
420     termz[i] = '\0';
421 }
422 #else
423 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
424                         char *termz)
425 {
426     size_t i, sizez;
427     Z_Term *term = zapt->term;
428
429     sizez = term->u.general->len;
430     if (sizez > IT_MAX_WORD)
431         sizez = IT_MAX_WORD;
432     for (i = 0; i < sizez; i++)
433         termz[i] = index_char_cvt (term->u.general->buf[i]);
434     termz[i] = '\0';
435 }
436 #endif
437
438 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
439                                       Z_AttributesPlusTerm *zapt)
440 {
441     rset_relevance_parms parms;
442     char termz[IT_MAX_WORD+1];
443     char term_sub[IT_MAX_WORD+1];
444     char *p0 = termz, *p1 = NULL;
445     Z_Term *term = zapt->term;
446
447     parms.key_size = sizeof(struct it_key);
448     parms.max_rec = 100;
449     parms.cmp = key_compare;
450     parms.is = zi->wordIsam;
451
452     if (term->which != Z_Term_general)
453     {
454         zi->errCode = 124;
455         return NULL;
456     }
457     trans_term (zi, zapt, termz);
458     isam_p_indx = 0;  /* global, set by trunc_term - see below */
459     while (1)
460     {
461         if ((p1 = strchr (p0, ' ')))
462         {
463             memcpy (term_sub, p0, p1-p0);
464             term_sub[p1-p0] = '\0';
465         }
466         else
467             strcpy (term_sub, p0);
468         if (trunc_term (zi, zapt, term_sub, &parms.isam_positions))
469             return NULL;
470         if (!p1)
471             break;
472         p0 = p1+1;
473     }
474     parms.no_isam_positions = isam_p_indx;
475     if (isam_p_indx > 0)
476         return rset_create (rset_kind_relevance, &parms);
477     else
478         return rset_create (rset_kind_null, NULL);
479 }
480
481 static RSET rpn_search_APT_word (ZServerInfo *zi,
482                                  Z_AttributesPlusTerm *zapt)
483 {
484     ISAM_P *isam_positions;
485     rset_isam_parms parms;
486
487     char termz[IT_MAX_WORD+1];
488     Z_Term *term = zapt->term;
489
490     if (term->which != Z_Term_general)
491     {
492         zi->errCode = 124;
493         return NULL;
494     }
495     trans_term (zi, zapt, termz);
496     isam_p_indx = 0;  /* global, set by trunc_term - see below */
497     if (trunc_term (zi, zapt, termz, &isam_positions))
498         return NULL;
499     if (isam_p_indx < 1)
500         return rset_create (rset_kind_null, NULL);
501     else if (isam_p_indx == 1)
502     {
503         parms.is = zi->wordIsam;
504         parms.pos = *isam_positions;
505         return rset_create (rset_kind_isam, &parms);
506     }
507     else
508         return rset_trunc (zi->wordIsam, isam_positions, 0, isam_p_indx, 400);
509 }
510
511 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
512                                    Z_AttributesPlusTerm *zapt)
513 {
514     ISAM_P *isam_positions;
515     rset_isam_parms parms;
516
517     char termz[IT_MAX_WORD+1];
518     Z_Term *term = zapt->term;
519
520     if (term->which != Z_Term_general)
521     {
522         zi->errCode = 124;
523         return NULL;
524     }
525     trans_term (zi, zapt, termz);
526     isam_p_indx = 0;  /* global, set by trunc_term - see below */
527     if (trunc_term (zi, zapt, termz, &isam_positions))
528         return NULL;
529     if (isam_p_indx != 1)
530         return rset_create (rset_kind_null, NULL);
531     parms.is = zi->wordIsam;
532     parms.pos = *isam_positions;
533     return rset_create (rset_kind_isam, &parms);
534 }
535
536 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt)
537 {
538     AttrType relation;
539     AttrType structure;
540     int relation_value, structure_value;
541
542     attr_init (&relation, zapt, 2);
543     attr_init (&structure, zapt, 4);
544     
545     relation_value = attr_find (&relation);
546     structure_value = attr_find (&structure);
547     switch (structure_value)
548     {
549     case -1:
550         if (relation_value == 102) /* relevance relation */
551             return rpn_search_APT_relevance (zi, zapt);
552         return rpn_search_APT_word (zi, zapt);
553     case 1: /* phrase */
554         if (relation_value == 102) /* relevance relation */
555             return rpn_search_APT_relevance (zi, zapt);
556         return rpn_search_APT_phrase (zi, zapt);
557         break;
558     case 2: /* word */
559         if (relation_value == 102) /* relevance relation */
560             return rpn_search_APT_relevance (zi, zapt);
561         return rpn_search_APT_word (zi, zapt);
562     case 3: /* key */
563         break;
564     case 4: /* year */
565         break;
566     case 5: /* date - normalized */
567         break;
568     case 6: /* word list */
569         return rpn_search_APT_relevance (zi, zapt);
570     case 100: /* date - un-normalized */
571         break;
572     case 101: /* name - normalized */
573         break;
574     case 102: /* date - un-normalized */
575         break;
576     case 103: /* structure */
577         break;
578     case 104: /* urx */
579         break;
580     case 105: /* free-form-text */
581         return rpn_search_APT_relevance (zi, zapt);
582     case 106: /* document-text */
583         return rpn_search_APT_relevance (zi, zapt);
584     case 107: /* local-number */
585         break;
586     case 108: /* string */ 
587         return rpn_search_APT_word (zi, zapt);
588     case 109: /* numeric string */
589         break;
590     }
591     zi->errCode = 118;
592     return NULL;
593 }
594
595 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
596 {
597     ZServerSet *s;
598
599     if (!(s = resultSetGet (zi, resultSetId)))
600         return rset_create (rset_kind_null, NULL);
601     return s->rset;
602 }
603
604 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs)
605 {
606     RSET r = NULL;
607     if (zs->which == Z_RPNStructure_complex)
608     {
609         rset_bool_parms bool_parms;
610
611         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1);
612         if (bool_parms.rset_l == NULL)
613             return NULL;
614         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2);
615         if (bool_parms.rset_r == NULL)
616         {
617             rset_delete (bool_parms.rset_l);
618             return NULL;
619         }
620         bool_parms.key_size = sizeof(struct it_key);
621         bool_parms.cmp = key_compare;
622
623         switch (zs->u.complex->operator->which)
624         {
625         case Z_Operator_and:
626             r = rset_create (rset_kind_and, &bool_parms);
627             break;
628         case Z_Operator_or:
629             r = rset_create (rset_kind_or, &bool_parms);
630             break;
631         case Z_Operator_and_not:
632             r = rset_create (rset_kind_not, &bool_parms);
633             break;
634         default:
635             assert (0);
636         }
637     }
638     else if (zs->which == Z_RPNStructure_simple)
639     {
640         if (zs->u.simple->which == Z_Operand_APT)
641         {
642             logf (LOG_DEBUG, "rpn_search_APT");
643             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm);
644         }
645         else if (zs->u.simple->which == Z_Operand_resultSetId)
646         {
647             logf (LOG_DEBUG, "rpn_search_ref");
648             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
649         }
650         else
651         {
652             assert (0);
653         }
654     }
655     else
656     {
657         assert (0);
658     }
659     return r;
660 }
661
662 static void count_set (RSET r, int *count)
663 {
664     int psysno = 0;
665     struct it_key key;
666     RSFD rfd;
667
668     logf (LOG_DEBUG, "rpn_save_set");
669     *count = 0;
670     rfd = rset_open (r, 0);
671     while (rset_read (r, rfd, &key))
672     {
673         if (key.sysno != psysno)
674         {
675             psysno = key.sysno;
676             (*count)++;
677         }
678     }
679     rset_close (r, rfd);
680     logf (LOG_DEBUG, "%d distinct sysnos", *count);
681 }
682
683 int rpn_search (ZServerInfo *zi,
684                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
685                 const char *setname, int *hits)
686 {
687     RSET rset;
688
689     zi->errCode = 0;
690     zi->errString = NULL;
691     rset = rpn_search_structure (zi, rpn->RPNStructure);
692     if (!rset)
693         return zi->errCode;
694     count_set (rset, hits);
695     resultSetAdd (zi, setname, 1, rset);
696     return zi->errCode;
697 }
698