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