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