Fixes for hit estimates. Added zebra_set_approx_limit.
[idzebra-moved-to-github.git] / rset / rset.c
1 /* $Id: rset.c,v 1.51 2005-06-09 10:39:53 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 <string.h>
25 #include <idzebra/util.h>
26 #include <assert.h>
27 #include <yaz/nmem.h>
28 #include <rset.h>
29
30 static int log_level = 0;
31 static int log_level_initialized = 0;
32
33 /**
34    \brief Common constuctor for RFDs
35    \param rs Result set handle.
36    
37    Creates an rfd. Either allocates a new one, in which case the priv 
38    pointer is null, and will have to be filled in, or picks up one 
39    from the freelist, in which case the priv is already allocated,
40    and presumably everything that hangs from it as well 
41 */
42 RSFD rfd_create_base(RSET rs)
43 {
44     RSFD rnew = rs->free_list;
45
46     if (rnew) 
47     {
48         rs->free_list = rnew->next;
49         assert(rnew->rset==rs);
50         yaz_log(log_level, "rfd_create_base (fl): rfd=%p rs=%p fl=%p priv=%p", 
51                 rnew, rs, rs->free_list, rnew->priv); 
52     } 
53     else
54     {
55         rnew = nmem_malloc(rs->nmem, sizeof(*rnew));
56         rnew->counted_buf = nmem_malloc(rs->nmem, rs->keycontrol->key_size);
57         rnew->priv = 0;
58         rnew->rset = rs;
59         yaz_log(log_level, "rfd_create_base (new): rfd=%p rs=%p fl=%p priv=%p", 
60                 rnew, rs, rs->free_list, rnew->priv); 
61     }
62     rnew->next = rs->use_list;
63     rs->use_list = rnew;
64     rnew->counted_items = 0;
65     return rnew;
66 }
67
68 /**
69    \brief Closes a result set RFD handle
70    \param rfd the RFD handle.
71 */
72 void rset_close(RSFD rfd)
73 {
74     RSFD *pfd;
75     RSET rs = rfd->rset;
76
77     if (rs->hits_count == 0)
78     {
79         TERMID termid;
80         char buf[100];
81         while(rfd->counted_items < rs->hits_limit
82               && rset_default_read(rfd, buf, &termid))
83             ;
84         
85         rs->hits_count = rfd->counted_items;
86         rs->hits_approx = 0;
87         if (rs->hits_count >= rs->hits_limit)
88         {
89             double cur, tot;
90             zint est;
91             rset_pos(rfd, &cur, &tot); 
92             if (tot > 0) {
93                 int i;
94                 double ratio = cur/tot;
95                 est = (zint)(0.5 + rs->hits_count / ratio);
96                 yaz_log(log_level, "Estimating hits (%s) "
97                         "%0.1f->" ZINT_FORMAT
98                         "; %0.1f->" ZINT_FORMAT,
99                         rs->control->desc,
100                         cur, rs->hits_count,
101                         tot, est);
102                 i = 0; /* round to significant digits */
103                 while (est > rs->hits_round) {
104                     est /= 10;
105                     i++;
106                 }
107                 while (i--)
108                     est *= 10;
109                 rs->hits_count = est;
110                 rs->hits_approx = 1;
111             }
112         }
113         yaz_log(log_level, "rset_close p=%p count=" ZINT_FORMAT, rs,
114                 rs->hits_count);
115     }
116     (*rs->control->f_close)(rfd);
117     
118     yaz_log(log_level, "rfd_delete_base: rfd=%p rs=%p priv=%p fl=%p",
119             rfd, rs, rfd->priv, rs->free_list); 
120     for (pfd = &rs->use_list; *pfd; pfd = &(*pfd)->next)
121         if (*pfd == rfd)
122         {
123             *pfd = (*pfd)->next;
124             rfd->next = rs->free_list;
125             rs->free_list = rfd;
126             return;
127         }
128     yaz_log(YLOG_WARN, "rset_close handle not found. type=%s",
129             rs->control->desc);
130 }
131
132 /**
133    \brief Common constuctor for RSETs
134    \param sel The interface control handle
135    \param nmem The memory handle for it.
136    \param kcontrol Key control info (decode, encode, comparison etc)
137    \param scope scope for set
138    \param term Information about term for it (NULL for none).
139    \param no_children number of child rsets (0 for none)
140    \param children child rsets (NULL for none).
141    
142    Creates an rfd. Either allocates a new one, in which case the priv 
143    pointer is null, and will have to be filled in, or picks up one 
144    from the freelist, in which case the priv is already allocated,
145    and presumably everything that hangs from it as well 
146 */
147 RSET rset_create_base(const struct rset_control *sel, 
148                       NMEM nmem, struct rset_key_control *kcontrol,
149                       int scope, TERMID term,
150                       int no_children, RSET *children)
151 {
152     RSET rset;
153     assert(nmem);
154     if (!log_level_initialized) 
155     {
156         log_level = yaz_log_module_level("rset");
157         log_level_initialized = 1;
158     }
159
160     rset = (RSET) nmem_malloc(nmem, sizeof(*rset));
161     yaz_log(log_level, "rs_create(%s) rs=%p (nm=%p)", sel->desc, rset, nmem); 
162     rset->nmem = nmem;
163     rset->control = sel;
164     rset->refcount = 1;
165     rset->priv = 0;
166     rset->free_list = NULL;
167     rset->use_list = NULL;
168     rset->hits_count = 0;
169     rset->hits_limit = 0;
170     rset->hits_round = 1000;
171     rset->keycontrol = kcontrol;
172     (*kcontrol->inc)(kcontrol);
173     rset->scope = scope;
174     rset->term = term;
175     if (term)
176         term->rset = rset;
177
178     rset->no_children = no_children;
179     rset->children = 0;
180     if (no_children)
181     {
182         rset->children = (RSET*)
183             nmem_malloc(rset->nmem, no_children*sizeof(RSET *));
184         memcpy(rset->children, children, no_children*sizeof(RSET *));
185     }
186     return rset;
187 }
188
189 /**
190    \brief Destructor RSETs
191    \param rs Handle for result set.
192    
193    Destroys a result set and all its children.
194    The f_delete method of control is called for the result set.
195 */
196 void rset_delete(RSET rs)
197 {
198     (rs->refcount)--;
199     yaz_log(log_level, "rs_delete(%s), rs=%p, refcount=%d",
200             rs->control->desc, rs, rs->refcount); 
201     if (!rs->refcount)
202     {
203         int i;
204         if (rs->use_list)
205             yaz_log(YLOG_WARN, "rs_delete(%s) still has RFDs in use",
206                     rs->control->desc);
207         for (i = 0; i<rs->no_children; i++)
208             rset_delete(rs->children[i]);
209         (*rs->control->f_delete)(rs);
210         (*rs->keycontrol->dec)(rs->keycontrol);
211     }
212 }
213
214 /**
215    \brief Test for last use of RFD
216    \param rfd RFD handle.
217    
218    Returns 1 if this RFD is the last reference to it; 0 otherwise.
219 */
220 int rfd_is_last(RSFD rfd)
221 {
222     if (rfd->rset->use_list == rfd && rfd->next == 0)
223         return 1;
224     return 0;
225 }
226
227 /**
228    \brief Duplicate an RSET
229    \param rs Handle for result set.
230    
231    Duplicates a result set by incrementing the reference count to it.
232 */
233 RSET rset_dup (RSET rs)
234 {
235     (rs->refcount)++;
236     yaz_log(log_level, "rs_dup(%s), rs=%p, refcount=%d",
237             rs->control->desc, rs, rs->refcount); 
238     (*rs->keycontrol->inc)(rs->keycontrol);
239     return rs;
240 }
241
242 /** 
243     \brief Estimates hit count for result set.
244     \param rs Result Set.
245
246     rset_count uses rset_pos to get the total and returns that.
247     This is ok for rsisamb/c/s, and for some other rsets, but in case of
248     booleans etc it will give bad estimate, as nothing has been read
249     from that rset
250 */
251 zint rset_count(RSET rs)
252 {
253     double cur, tot;
254     RSFD rfd = rset_open(rs, 0);
255     rset_pos(rfd, &cur, &tot);
256     rset_close(rfd);
257     return (zint) tot;
258 }
259
260 /**
261    \brief is a getterms function for those that don't have any
262    \param ct result set handle
263    \param terms array of terms (0..maxterms-1)
264    \param maxterms length of terms array
265    \param curterm current size of terms array
266
267    If there is a term associated with rset the term is appeneded; otherwise
268    the terms array is untouched but curterm is incremented anyway.
269 */
270 void rset_get_one_term(RSET ct, TERMID *terms, int maxterms, int *curterm)
271 {
272     if (ct->term)
273     {
274         if (*curterm < maxterms)
275             terms[*curterm] = ct->term;
276         (*curterm)++;
277     }
278 }
279
280 struct ord_list *ord_list_create(NMEM nmem)
281 {
282     return 0;
283 }
284
285 struct ord_list *ord_list_append(NMEM nmem, struct ord_list *list,
286                                         int ord)
287 {
288     struct ord_list *n = nmem_malloc(nmem, sizeof(*n));
289     n->ord = ord;
290     n->next = list;
291     return n;
292 }
293
294 struct ord_list *ord_list_dup(NMEM nmem, struct ord_list *list)
295 {
296     struct ord_list *n = ord_list_create(nmem);
297     for (; list; list = list->next)
298         n = ord_list_append(nmem, n, list->ord);
299     return n;
300 }
301
302 /**
303    \brief Creates a TERMID entry.
304    \param name Term/Name buffer with given length
305    \param length of term
306    \param flags for term
307    \param type Term Type, Z_Term_general, Z_Term_characterString,..
308    \param nmem memory for term.
309    \param ol ord list
310    \param reg_type register type
311 */
312 TERMID rset_term_create(const char *name, int length, const char *flags,
313                         int type, NMEM nmem, struct ord_list *ol,
314                         int reg_type)
315
316 {
317     TERMID t;
318     yaz_log (log_level, "term_create '%s' %d f=%s type=%d nmem=%p",
319             name, length, flags, type, nmem);
320     t= (TERMID) nmem_malloc(nmem, sizeof(*t));
321     if (!name)
322         t->name = NULL;
323     else if (length == -1)
324         t->name = nmem_strdup(nmem, name);
325     else
326     {
327         t->name = (char*) nmem_malloc(nmem, length+1);
328         memcpy (t->name, name, length);
329         t->name[length] = '\0';
330     }
331     if (!flags)
332         t->flags = NULL;
333     else
334         t->flags = nmem_strdup(nmem, flags);
335     t->type = type;
336     t->reg_type = reg_type;
337     t->rankpriv = 0;
338     t->rset = 0;
339     t->ol = ord_list_dup(nmem, ol);
340     return t;
341 }
342
343 int rset_default_read(RSFD rfd, void *buf, TERMID *term)
344 {
345     RSET rset = rfd->rset;
346     int rc = (*rset->control->f_read)(rfd, buf, term);
347     if (rc > 0)
348     {
349         if (rfd->counted_items == 0 ||
350             (rset->keycontrol->cmp)(buf, rfd->counted_buf) > rset->scope)
351         {
352             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
353             rfd->counted_items++;
354         }
355     }
356     return rc;
357 }
358
359 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
360                          const void *untilbuf)
361 {
362     RSET rset = rfd->rset;
363     int more;
364
365     if (rset->control->f_forward &&
366         rfd->counted_items >= rset->hits_limit)
367     {
368         assert (rset->control->f_forward != rset_default_forward);
369         return rset->control->f_forward(rfd, buf, term, untilbuf);
370     }
371     
372     while ((more = rset_read(rfd, buf, term)) > 0)
373     {
374         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) <= 1)
375             break;
376     }
377     if (log_level)
378         yaz_log (log_level, "rset_default_forward exiting m=%d c=%d",
379                  more, rset->scope);
380     
381     return more;
382 }
383
384 void rset_visit(RSET rset, int level)
385 {
386     int i;
387     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
388             rset->hits_approx ? '~' : '=',
389             rset->hits_count);
390     for (i = 0; i<rset->no_children; i++)
391         rset_visit(rset->children[i], level+1);
392 }
393