Extended the result set system. Added support for filtering/limits.
[idzebra-moved-to-github.git] / rset / rsprox.c
1 /* $Id: rsprox.c,v 1.28 2005-05-03 09:11:36 adam Exp $
2    Copyright (C) 1995-2005
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 Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include <idzebra/util.h>
29 #include <rset.h>
30
31 #ifndef RSET_DEBUG
32 #define RSET_DEBUG 0
33 #endif
34
35 static RSFD r_open (RSET ct, int flag);
36 static void r_close (RSFD rfd);
37 static void r_delete (RSET ct);
38 static int r_forward(RSFD rfd, void *buf, TERMID *term, const void *untilbuf);
39 static int r_read (RSFD rfd, void *buf, TERMID *term);
40 static int r_write (RSFD rfd, const void *buf);
41 static void r_pos (RSFD rfd, double *current, double *total);
42 static void r_get_terms(RSET ct, TERMID *terms, int maxterms, int *curterm);
43
44 static const struct rset_control control = 
45 {
46     "prox",
47     r_delete,
48     r_get_terms,
49     r_open,
50     r_close,
51     r_forward,
52     r_pos,
53     r_read,
54     r_write,
55 };
56
57 struct rset_prox_info {
58     RSET *rset;   /* array of 'child' rsets */
59     int rset_no;  /* how many of them */
60     int ordered;
61     int exclusion;
62     int relation;
63     int distance;
64 };
65
66 struct rset_prox_rfd {
67     RSFD *rfd;
68     char **buf;  /* lookahead key buffers */
69     char *more;  /* more in each lookahead? */
70     TERMID *terms; /* lookahead terms */
71     zint hits;
72 };    
73
74
75 RSET rsprox_create(NMEM nmem, struct rset_key_control *kcontrol,
76                    int scope,
77                    int rset_no, RSET *rset,
78                    int ordered, int exclusion,
79                    int relation, int distance)
80 {
81     RSET rnew = rset_create_base(&control, nmem, kcontrol, scope,0);
82     struct rset_prox_info *info;
83     info = (struct rset_prox_info *) nmem_malloc(rnew->nmem,sizeof(*info));
84     info->rset = nmem_malloc(rnew->nmem,rset_no * sizeof(*info->rset));
85     memcpy(info->rset, rset,
86            rset_no * sizeof(*info->rset));
87     info->rset_no = rset_no;
88     info->ordered = ordered;
89     info->exclusion = exclusion;
90     info->relation = relation;
91     info->distance = distance;
92     rnew->priv = info;
93     return rnew;
94 }
95
96 static void r_delete (RSET ct)
97 {
98     struct rset_prox_info *info = (struct rset_prox_info *) ct->priv;
99     int i;
100
101     for (i = 0; i<info->rset_no; i++)
102         rset_delete(info->rset[i]);
103 }
104
105 static RSFD r_open (RSET ct, int flag)
106 {
107     struct rset_prox_info *info = (struct rset_prox_info *) ct->priv;
108     RSFD rfd;
109     struct rset_prox_rfd *p;
110     int i;
111
112     if (flag & RSETF_WRITE)
113     {
114         yaz_log(YLOG_FATAL, "prox set type is read-only");
115         return NULL;
116     }
117     rfd = rfd_create_base(ct);
118     if (rfd->priv)
119         p=(struct rset_prox_rfd *)(rfd->priv);
120     else {
121         p = (struct rset_prox_rfd *) nmem_malloc(ct->nmem,sizeof(*p));
122         rfd->priv = p;
123         p->more = nmem_malloc (ct->nmem,sizeof(*p->more) * info->rset_no);
124         p->buf = nmem_malloc(ct->nmem,sizeof(*p->buf) * info->rset_no);
125         p->terms = nmem_malloc(ct->nmem,sizeof(*p->terms) * info->rset_no);
126         for (i = 0; i < info->rset_no; i++) 
127         {
128             p->buf[i] = nmem_malloc(ct->nmem,ct->keycontrol->key_size);
129             p->terms[i] = 0;
130         }
131         p->rfd = nmem_malloc(ct->nmem,sizeof(*p->rfd) * info->rset_no);
132     }
133     yaz_log(YLOG_DEBUG,"rsprox (%s) open [%p] n=%d", 
134             ct->control->desc, rfd, info->rset_no);
135
136     for (i = 0; i < info->rset_no; i++) {
137         p->rfd[i] = rset_open (info->rset[i], RSETF_READ);
138         p->more[i] = rset_read (p->rfd[i], p->buf[i], &p->terms[i]);
139     }
140     p->hits = 0;
141     return rfd;
142 }
143
144 static void r_close (RSFD rfd)
145 {
146     struct rset_prox_info *info = (struct rset_prox_info *)(rfd->rset->priv);
147     struct rset_prox_rfd *p=(struct rset_prox_rfd *)(rfd->priv);
148     
149     int i;
150     for (i = 0; i<info->rset_no; i++)
151         rset_close (p->rfd[i]);
152     rfd_delete_base(rfd);
153 }
154
155 static int r_forward(RSFD rfd, void *buf, TERMID *term, const void *untilbuf)
156 {
157     struct rset_prox_info *info = (struct rset_prox_info *)(rfd->rset->priv);
158     struct rset_prox_rfd *p = (struct rset_prox_rfd *)(rfd->priv);
159     const struct rset_key_control *kctrl = rfd->rset->keycontrol;
160     int cmp = 0;
161     int i;
162
163     if (untilbuf)
164     {
165         /* it is enough to forward first one. Other will follow. */
166         if ( p->more[0] &&   /* was: cmp >=2 */
167            ((kctrl->cmp)(untilbuf, p->buf[0]) >= rfd->rset->scope) ) 
168             p->more[0] = rset_forward(p->rfd[0], p->buf[0], 
169                                       &p->terms[0], untilbuf);
170     }
171     if (info->ordered && info->relation == 3 && info->exclusion == 0
172         && info->distance == 1)
173     {
174         while (p->more[0]) 
175         {
176             for (i = 1; i < info->rset_no; i++)
177             {
178                 if (!p->more[i]) 
179                 {
180                     p->more[0] = 0; /* saves us a goto out of while loop. */
181                     break;
182                 }
183                 cmp = (*kctrl->cmp) (p->buf[i], p->buf[i-1]);
184                 if (cmp >= rfd->rset->scope )  /* cmp>1 */
185                 {
186                     p->more[i-1] = rset_forward (p->rfd[i-1],
187                                                  p->buf[i-1],
188                                                  &p->terms[i-1],
189                                                  p->buf[i]);
190                     break;
191                 }
192                 else if ( cmp>0 ) /* cmp == 1*/
193                 {
194                     if ((*kctrl->getseq)(p->buf[i-1]) +1 != 
195                         (*kctrl->getseq)(p->buf[i]))
196                     { /* FIXME - We need more flexible multilevel stuff */
197                         p->more[i-1] = rset_read ( p->rfd[i-1], p->buf[i-1],
198                                                    &p->terms[i-1]);
199                         break;
200                     }
201                 }
202                 else
203                 {
204                     p->more[i] = rset_forward (p->rfd[i], 
205                                   p->buf[i], &p->terms[i], p->buf[i-1]);
206                     break;
207                 }
208             }
209             if (i == info->rset_no)
210             {
211                 memcpy (buf, p->buf[0], kctrl->key_size);
212                 if (term)
213                     *term = p->terms[0];
214                 p->more[0] = rset_read (p->rfd[0], p->buf[0], &p->terms[0]);
215                 p->hits++;
216                 return 1;
217             }
218         }
219     }
220     else if (info->rset_no == 2)
221     {
222         while (p->more[0] && p->more[1]) 
223         {
224             int cmp = (*kctrl->cmp)(p->buf[0], p->buf[1]);
225             if ( cmp <= - rfd->rset->scope) /* cmp<-1*/
226                 p->more[0] = rset_forward (p->rfd[0], p->buf[0], 
227                                            &p->terms[0],p->buf[1]);
228             else if ( cmp >= rfd->rset->scope ) /* cmp>1 */
229                 p->more[1] = rset_forward (p->rfd[1], p->buf[1], 
230                                            &p->terms[1],p->buf[0]);
231             else
232             {
233                 zint seqno[500]; /* FIXME - why 500 ?? */
234                 int n = 0;
235                 
236                 seqno[n++] = (*kctrl->getseq)(p->buf[0]);
237                 while ((p->more[0] = rset_read (p->rfd[0],
238                                         p->buf[0], &p->terms[0])) >= -1 &&
239                        p->more[0] <= -1)
240                     if (n < 500)
241                         seqno[n++] = (*kctrl->getseq)(p->buf[0]);
242                 
243                 for (i = 0; i<n; i++)
244                 {
245                     zint diff = (*kctrl->getseq)(p->buf[1]) - seqno[i];
246                     int excl = info->exclusion;
247                     if (!info->ordered && diff < 0)
248                         diff = -diff;
249                     switch (info->relation)
250                     {
251                     case 1:      /* < */
252                         if (diff < info->distance && diff >= 0)
253                             excl = !excl;
254                         break;
255                     case 2:      /* <= */
256                         if (diff <= info->distance && diff >= 0)
257                             excl = !excl;
258                         break;
259                     case 3:      /* == */
260                         if (diff == info->distance && diff >= 0)
261                             excl = !excl;
262                         break;
263                     case 4:      /* >= */
264                         if (diff >= info->distance && diff >= 0)
265                             excl = !excl;
266                         break;
267                     case 5:      /* > */
268                         if (diff > info->distance && diff >= 0)
269                             excl = !excl;
270                         break;
271                     case 6:      /* != */
272                         if (diff != info->distance && diff >= 0)
273                             excl = !excl;
274                         break;
275                     }
276                     if (excl)
277                     {
278                         memcpy (buf, p->buf[1], kctrl->key_size);
279                         if (term)
280                             *term = p->terms[1];
281                         p->more[1] = rset_read ( p->rfd[1], p->buf[1],
282                                                  &p->terms[1]);
283                         p->hits++;
284                         return 1;
285                     }
286                 }
287                 p->more[1] = rset_read (p->rfd[1], p->buf[1], &p->terms[1]);
288             }
289         }
290     }
291     return 0;
292 }
293
294
295 static int r_read (RSFD rfd, void *buf, TERMID *term)
296 {
297     return r_forward(rfd, buf, term, 0);
298 }
299
300 static int r_write (RSFD rfd, const void *buf)
301 {
302     yaz_log(YLOG_FATAL, "prox set type is read-only");
303     return -1;
304 }
305
306 static void r_pos (RSFD rfd, double *current, double *total)
307 {
308     struct rset_prox_info *info = (struct rset_prox_info *)(rfd->rset->priv);
309     struct rset_prox_rfd *p = (struct rset_prox_rfd *)(rfd->priv);
310     int i;
311     double r = 0.0;
312     double cur, tot = -1.0;
313     double scur = 0.0, stot = 0.0;
314
315     yaz_log(YLOG_DEBUG, "rsprox_pos");
316
317     for (i = 0; i < info->rset_no; i++)
318     {
319         rset_pos(p->rfd[i],  &cur, &tot);
320         if (tot>0) {
321             scur += cur;
322             stot += tot;
323         }
324     }
325     if (tot <0) {  /* nothing found */
326         *current = -1;
327         *total = -1;
328     } else if (tot < 1) { /* most likely tot==0 */
329         *current = 0;
330         *total = 0;
331     } else {
332         r = scur/stot; 
333         *current = (double) p->hits;
334         *total=*current/r ; 
335     }
336     yaz_log(YLOG_DEBUG,"prox_pos: [%d] %0.1f/%0.1f= %0.4f ",
337                     i,*current, *total, r);
338 }
339
340
341
342 static void r_get_terms(RSET ct, TERMID *terms, int maxterms, int *curterm)
343 {
344     struct rset_prox_info *info =
345               (struct rset_prox_info *) ct->priv;
346     int i;
347     for (i = 0; i<info->rset_no; i++)
348         rset_getterms(info->rset[i], terms, maxterms, curterm);
349 }
350