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