Refactor: hide reclist structure
[pazpar2-moved-to-github.git] / src / reclists.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <assert.h>
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <yaz/yaz-util.h>
27
28 #include "pazpar2.h"
29 #include "reclists.h"
30 #include "jenkins_hash.h"
31
32 struct reclist
33 {
34     struct reclist_bucket **hashtable;
35     int hashtable_size;
36     int hashmask;
37
38     struct record_cluster **flatlist;
39     int flatlist_size;
40     int num_records;
41     int pointer;
42
43     NMEM nmem;
44 };
45
46 static struct reclist_sortparms *qsort_sortparms = 0; /* thread pr */
47
48 struct reclist_bucket
49 {
50     struct record_cluster *record;
51     struct reclist_bucket *next;
52 };
53
54 #if 0
55 struct reclist_sortparms * 
56 reclist_sortparms_insert_field_id(NMEM nmem,
57                          struct reclist_sortparms **sortparms,
58                          int field_id,
59                          enum conf_sortkey_type type,
60                          int increasing)
61 {
62     struct reclist_sortparms * tmp_rlsp = 0;
63     // assert(nmem);
64
65     if(!sortparms || field_id < 0)
66         return 0;
67
68     // construct new reclist_sortparms
69     tmp_rlsp  = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
70     tmp_rlsp->offset = field_id;
71     tmp_rlsp->type = type;
72     tmp_rlsp->increasing = increasing;
73
74
75     // insert in *sortparms place, moving *sortparms one down the list
76     tmp_rlsp->next = *sortparms;
77     *sortparms = tmp_rlsp;
78
79     return *sortparms;
80 };
81 #endif
82
83 #if 0
84 struct reclist_sortparms * 
85 reclist_sortparms_insert(NMEM nmem, 
86                          struct reclist_sortparms **sortparms,
87                          struct conf_service * service,
88                          const char * name,
89                          int increasing)
90 {
91     int field_id = 0;
92
93     if (!sortparms || !service || !name)  
94         return 0;
95     
96     field_id = conf_service_sortkey_field_id(service, name);
97
98     if (-1 == field_id)
99         return 0;
100
101     return reclist_sortparms_insert_field_id(nmem, sortparms, field_id,
102                                              service->sortkeys[field_id].type,
103                                              increasing);
104 };
105 #endif
106
107
108 struct reclist_sortparms *reclist_parse_sortparms(NMEM nmem, const char *parms,
109     struct conf_service *service)
110 {
111     struct reclist_sortparms *res = 0;
112     struct reclist_sortparms **rp = &res;
113
114     if (strlen(parms) > 256)
115         return 0;
116     while (*parms)
117     {
118         char parm[256];
119         char *pp;
120         const char *cpp;
121         int increasing;
122         int i;
123         int offset = 0;
124         enum conf_sortkey_type type;
125         struct reclist_sortparms *new;
126
127         if (!(cpp = strchr(parms, ',')))
128             cpp = parms + strlen(parms);
129         strncpy(parm, parms, cpp - parms); 
130         parm[cpp-parms] = '\0';
131
132         if ((pp = strchr(parm, ':')))
133         {
134             increasing = pp[1] == '1' ? 1 : 0;
135             *pp = '\0';
136         }
137         else
138             increasing = 0;
139         if (!strcmp(parm, "relevance"))
140         {
141             type = Metadata_sortkey_relevance;
142         }
143         else
144         {
145             for (i = 0; i < service->num_sortkeys; i++)
146             {
147                 struct conf_sortkey *sk = &service->sortkeys[i];
148                 if (!strcmp(sk->name, parm))
149                 {
150                     type = sk->type;
151                     if (type == Metadata_sortkey_skiparticle)
152                         type = Metadata_sortkey_string;
153                     break;
154                 }
155             }
156             if (i >= service->num_sortkeys)
157             {
158                 yaz_log(YLOG_FATAL, "Bad sortkey: %s", parm);
159                 return 0;
160             }
161             else
162                 offset = i;
163         }
164         new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
165         new->next = 0;
166         new->offset = offset;
167         new->type = type;
168         new->increasing = increasing;
169         rp = &new->next;
170         if (*(parms = cpp))
171             parms++;
172     }
173     return res;
174 }
175
176 static int reclist_cmp(const void *p1, const void *p2)
177 {
178     struct record_cluster *r1 = (*(struct record_cluster**) p1);
179     struct record_cluster *r2 = (*(struct record_cluster**) p2);
180     struct reclist_sortparms *s;
181     int res = 0;
182
183     for (s = qsort_sortparms; s && res == 0; s = s->next)
184     {
185         union data_types *ut1 = r1->sortkeys[s->offset];
186         union data_types *ut2 = r2->sortkeys[s->offset];
187         switch (s->type)
188         {
189             const char *s1, *s2;
190             
191             case Metadata_sortkey_relevance:
192                 res = r2->relevance - r1->relevance;
193                 break;
194             case Metadata_sortkey_string:
195                 s1 = ut1 ? ut1->text.sort : "";
196                 s2 = ut2 ? ut2->text.sort : "";
197                 res = strcmp(s2, s1);
198                 if (res)
199                 {
200                     if (s->increasing)
201                         res *= -1;
202                 }
203                 break;
204             case Metadata_sortkey_numeric:
205                 if (ut1 && ut2)
206                 {
207                     if (s->increasing)
208                         res = ut1->number.min  - ut2->number.min;
209                     else
210                         res = ut2->number.max  - ut1->number.max;
211                 }
212                 else if (ut1 && !ut2)
213                     res = -1;
214                 else if (!ut1 && ut2)
215                     res = 1;
216                 else
217                     res = 0;
218                 break;
219             default:
220                 yaz_log(YLOG_WARN, "Bad sort type: %d", s->type);
221                 res = 0;
222         }
223     }
224     return res;
225 }
226
227 void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
228 {
229     qsort_sortparms = parms;
230     qsort(l->flatlist, l->num_records, 
231           sizeof(struct record_cluster*), reclist_cmp);
232     reclist_rewind(l);
233 }
234
235 struct record_cluster *reclist_read_record(struct reclist *l)
236 {
237     if (l && l->pointer < l->num_records)
238         return l->flatlist[l->pointer++];
239     else
240         return 0;
241 }
242
243 void reclist_rewind(struct reclist *l)
244 {
245     if (l)
246         l->pointer = 0;
247 }
248
249 struct reclist *reclist_create(NMEM nmem, int numrecs)
250 {
251     int hashsize = 1;
252     struct reclist *res;
253
254     assert(numrecs);
255     while (hashsize < numrecs)
256         hashsize <<= 1;
257     res = nmem_malloc(nmem, sizeof(struct reclist));
258     res->hashtable 
259         = nmem_malloc(nmem, hashsize * sizeof(struct reclist_bucket*));
260     memset(res->hashtable, 0, hashsize * sizeof(struct reclist_bucket*));
261     res->hashtable_size = hashsize;
262     res->nmem = nmem;
263     res->hashmask = hashsize - 1; // Creates a bitmask
264
265     res->num_records = 0;
266     res->flatlist = nmem_malloc(nmem, numrecs * sizeof(struct record_cluster*));
267     res->flatlist_size = numrecs;
268
269     return res;
270 }
271
272 int reclist_get_num_records(struct reclist *l)
273 {
274     if (l)
275         return l->num_records;
276     return 0;
277 }
278
279 struct record_cluster *reclist_get_cluster(struct reclist *l, int i)
280 {
281     return l->flatlist[i];
282 }
283
284 // Insert a record. Return record cluster (newly formed or pre-existing)
285 struct record_cluster *reclist_insert( struct reclist *l,
286                                        struct conf_service *service, 
287                                        struct record  *record,
288                                        char *merge_key, int *total)
289 {
290     unsigned int bucket;
291     struct reclist_bucket **p;
292     struct record_cluster *cluster = 0;
293     
294     assert(service);
295     assert(l);
296     assert(record);
297     assert(merge_key);
298     assert(total);
299
300     bucket = jenkins_hash((unsigned char*) merge_key) & l->hashmask;
301
302     for (p = &l->hashtable[bucket]; *p; p = &(*p)->next)
303     {
304         // We found a matching record. Merge them
305         if (!strcmp(merge_key, (*p)->record->merge_key))
306         {
307             struct record_cluster *existing = (*p)->record;
308             record->next = existing->records;
309             existing->records = record;
310             cluster = existing;
311             break;
312         }
313     }
314     if (!cluster && l->num_records < l->flatlist_size)
315     {
316         struct reclist_bucket *new =
317             nmem_malloc(l->nmem, sizeof(struct reclist_bucket));
318         struct record_cluster *newc =
319             nmem_malloc(l->nmem, sizeof(struct record_cluster));
320         
321         record->next = 0;
322         new->record = newc;
323         new->next = 0;
324         newc->records = record;
325         newc->merge_key = merge_key;
326         newc->relevance = 0;
327         newc->term_frequency_vec = 0;
328         newc->recid = merge_key;
329         (*total)++;
330         newc->metadata = nmem_malloc(l->nmem,
331                 sizeof(struct record_metadata*) * service->num_metadata);
332         memset(newc->metadata, 0, 
333                sizeof(struct record_metadata*) * service->num_metadata);
334         newc->sortkeys = nmem_malloc(l->nmem,
335                 sizeof(struct record_metadata*) * service->num_sortkeys);
336         memset(newc->sortkeys, 0,
337                sizeof(union data_types*) * service->num_sortkeys);
338
339         *p = new;
340         l->flatlist[l->num_records++] = newc;
341         cluster = newc;
342     }
343     return cluster;
344 }
345
346
347 /*
348  * Local variables:
349  * c-basic-offset: 4
350  * c-file-style: "Stroustrup"
351  * indent-tabs-mode: nil
352  * End:
353  * vim: shiftwidth=4 tabstop=8 expandtab
354  */
355