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