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