Split function rpn_scan in two.
[idzebra-moved-to-github.git] / index / rpnscan.c
1 /* $Id: rpnscan.c,v 1.2 2006-09-21 10:10:07 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #ifdef WIN32
26 #include <io.h>
27 #endif
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <ctype.h>
32
33 #include <yaz/diagbib1.h>
34 #include "index.h"
35 #include <zebra_xpath.h>
36 #include <attrfind.h>
37 #include <charmap.h>
38 #include <rset.h>
39
40 struct scan_info_entry {
41     char *term;
42     ISAM_P isam_p;
43 };
44
45 struct scan_info {
46     struct scan_info_entry *list;
47     ODR odr;
48     int before, after;
49     char prefix[20];
50 };
51
52 /* convert APT SCAN term to internal cmap */
53 static ZEBRA_RES trans_scan_term(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
54                                  char *termz, int reg_type)
55 {
56     char termz0[IT_MAX_WORD];
57
58     if (zapt_term_to_utf8(zh, zapt, termz0) == ZEBRA_FAIL)
59         return ZEBRA_FAIL;    /* error */
60     else
61     {
62         const char **map;
63         const char *cp = (const char *) termz0;
64         const char *cp_end = cp + strlen(cp);
65         const char *src;
66         int i = 0;
67         const char *space_map = NULL;
68         int len;
69             
70         while ((len = (cp_end - cp)) > 0)
71         {
72             map = zebra_maps_input(zh->reg->zebra_maps, reg_type, &cp, len, 0);
73             if (**map == *CHR_SPACE)
74                 space_map = *map;
75             else
76             {
77                 if (i && space_map)
78                     for (src = space_map; *src; src++)
79                         termz[i++] = *src;
80                 space_map = NULL;
81                 for (src = *map; *src; src++)
82                     termz[i++] = *src;
83             }
84         }
85         termz[i] = '\0';
86     }
87     return ZEBRA_OK;
88 }
89
90 static void count_set(ZebraHandle zh, RSET rset, zint *count)
91 {
92     zint psysno = 0;
93     struct it_key key;
94     RSFD rfd;
95
96     yaz_log(YLOG_DEBUG, "count_set");
97
98     rset->hits_limit = zh->approx_limit;
99
100     *count = 0;
101     rfd = rset_open(rset, RSETF_READ);
102     while (rset_read(rfd, &key,0 /* never mind terms */))
103     {
104         if (key.mem[0] != psysno)
105         {
106             psysno = key.mem[0];
107             if (rfd->counted_items >= rset->hits_limit)
108                 break;
109         }
110     }
111     rset_close (rfd);
112     *count = rset->hits_count;
113 }
114
115 static int scan_handle (char *name, const char *info, int pos, void *client)
116 {
117     int len_prefix, idx;
118     struct scan_info *scan_info = (struct scan_info *) client;
119
120     len_prefix = strlen(scan_info->prefix);
121     if (memcmp (name, scan_info->prefix, len_prefix))
122         return 1;
123     if (pos > 0)
124         idx = scan_info->after - pos + scan_info->before;
125     else
126         idx = - pos - 1;
127
128     /* skip special terms.. of no interest */
129     if (name[len_prefix] < 4)
130         return 1;
131
132     if (idx < 0)
133         return 0;
134     scan_info->list[idx].term = (char *)
135         odr_malloc(scan_info->odr, strlen(name + len_prefix)+1);
136     strcpy(scan_info->list[idx].term, name + len_prefix);
137     assert (*info == sizeof(ISAM_P));
138     memcpy (&scan_info->list[idx].isam_p, info+1, sizeof(ISAM_P));
139     return 0;
140 }
141
142
143 #define RPN_MAX_ORDS 32
144
145 static ZEBRA_RES rpn_scan_ver1(ZebraHandle zh, ODR stream, 
146                                Z_AttributesPlusTerm *zapt,
147                                int *position, int *num_entries, 
148                                ZebraScanEntry **list,
149                                int *is_partial, RSET limit_set,
150                                int return_zero,
151                                int index_type, int ord_no, int *ords)
152 {
153     int pos = *position;
154     int num = *num_entries;
155     int before;
156     int after;
157     int i;
158     struct scan_info *scan_info_array;
159     char termz[IT_MAX_WORD+20];
160     ZebraScanEntry *glist;
161     NMEM rset_nmem = 0;
162     struct rset_key_control *kc = 0;
163     int ptr[RPN_MAX_ORDS];
164
165     before = pos-1;
166     if (before < 0)
167         before = 0;
168     after = 1+num-pos;
169     if (after < 0)
170         after = 0;
171     yaz_log(YLOG_DEBUG, "rpn_scan pos=%d num=%d before=%d "
172             "after=%d before+after=%d",
173             pos, num, before, after, before+after);
174     scan_info_array = (struct scan_info *)
175         odr_malloc(stream, ord_no * sizeof(*scan_info_array));
176     for (i = 0; i < ord_no; i++)
177     {
178         int j, prefix_len = 0;
179         int before_tmp = before, after_tmp = after;
180         struct scan_info *scan_info = scan_info_array + i;
181         struct rpn_char_map_info rcmi;
182
183         rpn_char_map_prepare (zh->reg, index_type, &rcmi);
184
185         scan_info->before = before;
186         scan_info->after = after;
187         scan_info->odr = stream;
188
189         scan_info->list = (struct scan_info_entry *)
190             odr_malloc(stream, (before+after) * sizeof(*scan_info->list));
191         for (j = 0; j<before+after; j++)
192             scan_info->list[j].term = NULL;
193
194         prefix_len += key_SU_encode (ords[i], termz + prefix_len);
195         termz[prefix_len] = 0;
196         strcpy(scan_info->prefix, termz);
197
198         if (trans_scan_term(zh, zapt, termz+prefix_len, index_type) == 
199             ZEBRA_FAIL)
200             return ZEBRA_FAIL;
201         
202         dict_scan(zh->reg->dict, termz, &before_tmp, &after_tmp,
203                   scan_info, scan_handle);
204     }
205     glist = (ZebraScanEntry *)
206         odr_malloc(stream, (before+after)*sizeof(*glist));
207
208     rset_nmem = nmem_create();
209     kc = zebra_key_control_create(zh);
210
211     /* consider terms after main term */
212     for (i = 0; i < ord_no; i++)
213         ptr[i] = before;
214     
215     *is_partial = 0;
216     for (i = 0; i<after; i++)
217     {
218         int j, j0 = -1;
219         const char *mterm = NULL;
220         const char *tst;
221         RSET rset = 0;
222         int lo = i + pos-1; /* offset in result list */
223
224         /* find: j0 is the first of the minimal values */
225         for (j = 0; j < ord_no; j++)
226         {
227             if (ptr[j] < before+after && ptr[j] >= 0 &&
228                 (tst = scan_info_array[j].list[ptr[j]].term) &&
229                 (!mterm || strcmp (tst, mterm) < 0))
230             {
231                 j0 = j;
232                 mterm = tst;
233             }
234         }
235         if (j0 == -1)
236             break;  /* no value found, stop */
237
238         /* get result set for first one , but only if it's within bounds */
239         if (lo >= 0)
240         {
241             /* get result set for first term */
242             zebra_term_untrans_iconv(zh, stream->mem, index_type,
243                                      &glist[lo].term, mterm);
244             rset = rset_trunc(zh, &scan_info_array[j0].list[ptr[j0]].isam_p, 1,
245                               glist[lo].term, strlen(glist[lo].term),
246                               NULL, 0, zapt->term->which, rset_nmem, 
247                               kc, kc->scope, 0, index_type, 0 /* hits_limit */,
248                               0 /* term_ref_id_str */);
249         }
250         ptr[j0]++; /* move index for this set .. */
251         /* get result set for remaining scan terms */
252         for (j = j0+1; j<ord_no; j++)
253         {
254             if (ptr[j] < before+after && ptr[j] >= 0 &&
255                 (tst = scan_info_array[j].list[ptr[j]].term) &&
256                 !strcmp (tst, mterm))
257             {
258                 if (lo >= 0)
259                 {
260                     RSET rsets[2];
261                     
262                     rsets[0] = rset;
263                     rsets[1] =
264                         rset_trunc(
265                             zh, &scan_info_array[j].list[ptr[j]].isam_p, 1,
266                             glist[lo].term,
267                             strlen(glist[lo].term), NULL, 0,
268                             zapt->term->which,rset_nmem,
269                             kc, kc->scope, 0, index_type, 0 /* hits_limit */,
270                             0 /* term_ref_id_str */ );
271                     rset = rset_create_or(rset_nmem, kc,
272                                           kc->scope, 0 /* termid */,
273                                           2, rsets);
274                 }
275                 ptr[j]++;
276             }
277         }
278         if (lo >= 0)
279         {
280             zint count;
281             /* merge with limit_set if given */
282             if (limit_set)
283             {
284                 RSET rsets[2];
285                 rsets[0] = rset;
286                 rsets[1] = rset_dup(limit_set);
287                 
288                 rset = rset_create_and(rset_nmem, kc, kc->scope, 2, rsets);
289             }
290             /* count it */
291             count_set(zh, rset, &count);
292             glist[lo].occurrences = count;
293             rset_delete(rset);
294         }
295     }
296     if (i < after)
297     {
298         *num_entries -= (after-i);
299         *is_partial = 1;
300         if (*num_entries < 0)
301         {
302             (*kc->dec)(kc);
303             nmem_destroy(rset_nmem);
304             *num_entries = 0;
305             return ZEBRA_OK;
306         }
307     }
308     /* consider terms before main term */
309     for (i = 0; i<ord_no; i++)
310         ptr[i] = 0;
311     
312     for (i = 0; i<before; i++)
313     {
314         int j, j0 = -1;
315         const char *mterm = NULL;
316         const char *tst;
317         RSET rset;
318         int lo = before-1-i; /* offset in result list */
319         zint count;
320         
321         for (j = 0; j <ord_no; j++)
322         {
323             if (ptr[j] < before && ptr[j] >= 0 &&
324                 (tst = scan_info_array[j].list[before-1-ptr[j]].term) &&
325                 (!mterm || strcmp (tst, mterm) > 0))
326             {
327                 j0 = j;
328                     mterm = tst;
329             }
330         }
331         if (j0 == -1)
332             break;
333         
334         zebra_term_untrans_iconv(zh, stream->mem, index_type,
335                                  &glist[lo].term, mterm);
336         
337         rset = rset_trunc
338             (zh, &scan_info_array[j0].list[before-1-ptr[j0]].isam_p, 1,
339              glist[lo].term, strlen(glist[lo].term),
340              NULL, 0, zapt->term->which, rset_nmem,
341              kc, kc->scope, 0, index_type, 0 /* hits_limit */,
342              0 /* term_ref_id_str */);
343         
344         ptr[j0]++;
345         
346         for (j = j0+1; j<ord_no; j++)
347         {
348             if (ptr[j] < before && ptr[j] >= 0 &&
349                 (tst = scan_info_array[j].list[before-1-ptr[j]].term) &&
350                 !strcmp (tst, mterm))
351             {
352                 RSET rsets[2];
353                 
354                 rsets[0] = rset;
355                 rsets[1] = rset_trunc(
356                     zh,
357                     &scan_info_array[j].list[before-1-ptr[j]].isam_p, 1,
358                     glist[lo].term,
359                     strlen(glist[lo].term), NULL, 0,
360                     zapt->term->which, rset_nmem,
361                     kc, kc->scope, 0, index_type, 0 /* hits_limit */,
362                     0 /* term_ref_id_str */);
363                 rset = rset_create_or(rset_nmem, kc,
364                                       kc->scope, 0 /* termid */, 2, rsets);
365                 
366                 ptr[j]++;
367             }
368         }
369         if (limit_set)
370         {
371             RSET rsets[2];
372             rsets[0] = rset;
373             rsets[1] = rset_dup(limit_set);
374             
375             rset = rset_create_and(rset_nmem, kc, kc->scope, 2, rsets);
376         }
377         count_set(zh, rset, &count);
378         glist[lo].occurrences = count;
379         rset_delete (rset);
380     }
381     (*kc->dec)(kc);
382     nmem_destroy(rset_nmem);
383     i = before-i;
384     if (i)
385     {
386         *is_partial = 1;
387         *position -= i;
388         *num_entries -= i;
389         if (*num_entries <= 0)
390         {
391             *num_entries = 0;
392             return ZEBRA_OK;
393         }
394     }
395     
396     *list = glist + i;               /* list is set to first 'real' entry */
397     
398     yaz_log(YLOG_DEBUG, "position = %d, num_entries = %d",
399             *position, *num_entries);
400     return ZEBRA_OK;
401 }
402
403
404 ZEBRA_RES rpn_scan(ZebraHandle zh, ODR stream, Z_AttributesPlusTerm *zapt,
405                    oid_value attributeset,
406                    int num_bases, char **basenames,
407                    int *position, int *num_entries, ZebraScanEntry **list,
408                    int *is_partial, RSET limit_set, int return_zero)
409 {
410     int base_no;
411     int ords[RPN_MAX_ORDS], ord_no = 0;
412
413     unsigned index_type;
414     char *search_type = NULL;
415     char rank_type[128];
416     int complete_flag;
417     int sort_flag;
418
419     *list = 0;
420     *is_partial = 0;
421
422     if (attributeset == VAL_NONE)
423         attributeset = VAL_BIB1;
424
425     if (!limit_set) /* no limit set given already */
426     {
427         /* see if there is a @attr 8=set */
428         AttrType termset;
429         int termset_value_numeric;
430         const char *termset_value_string;
431         attr_init_APT(&termset, zapt, 8);
432         termset_value_numeric =
433             attr_find_ex(&termset, NULL, &termset_value_string);
434         if (termset_value_numeric != -1)
435         {
436             char resname[32];
437             const char *termset_name = 0;
438             
439             if (termset_value_numeric != -2)
440             {
441                 
442                 sprintf(resname, "%d", termset_value_numeric);
443                 termset_name = resname;
444             }
445             else
446                 termset_name = termset_value_string;
447             
448             limit_set = resultSetRef (zh, termset_name);
449         }
450     }
451         
452     yaz_log(YLOG_DEBUG, "position = %d, num = %d set=%d",
453             *position, *num_entries, attributeset);
454         
455     if (zebra_maps_attr(zh->reg->zebra_maps, zapt, &index_type, &search_type,
456                         rank_type, &complete_flag, &sort_flag))
457     {
458         *num_entries = 0;
459         zebra_setError(zh, YAZ_BIB1_UNSUPP_ATTRIBUTE_TYPE, 0);
460         return ZEBRA_FAIL;
461     }
462     if (num_bases > RPN_MAX_ORDS)
463     {
464         zebra_setError(zh, YAZ_BIB1_TOO_MANY_DATABASES_SPECIFIED, 0);
465         return ZEBRA_FAIL;
466     }
467
468     for (base_no = 0; base_no < num_bases; base_no++)
469     {
470         int ord;
471
472         if (zebraExplain_curDatabase (zh->reg->zei, basenames[base_no]))
473         {
474             zebra_setError(zh, YAZ_BIB1_DATABASE_UNAVAILABLE,
475                            basenames[base_no]);
476             *num_entries = 0;
477             return ZEBRA_FAIL;
478         }
479         if (zebra_apt_get_ord(zh, zapt, index_type, 0, attributeset, &ord) 
480             != ZEBRA_OK)
481             continue;
482         ords[ord_no++] = ord;
483     }
484     if (ord_no == 0)
485     {
486         *num_entries = 0; /* zebra_apt_get_ord should set error reason */
487         return ZEBRA_FAIL;
488     }
489     /* prepare dictionary scanning */
490     if (*num_entries < 1)
491     {
492         *num_entries = 0;
493         return ZEBRA_OK;
494     }
495     return rpn_scan_ver1(zh, stream, zapt, position, num_entries, list,
496                          is_partial, limit_set, return_zero,
497                          index_type, ord_no, ords);
498 }
499
500 /*
501  * Local variables:
502  * c-basic-offset: 4
503  * indent-tabs-mode: nil
504  * End:
505  * vim: shiftwidth=4 tabstop=8 expandtab
506  */
507