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