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