breaking too long source code lines, otherwise no changes
[pazpar2-moved-to-github.git] / src / reclists.c
1 /* $Id: reclists.c,v 1.14 2007-04-25 09:23:03 marc 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 *reclist_parse_sortparms(NMEM nmem, const char *parms)
45 {
46     struct reclist_sortparms *res = 0;
47     struct reclist_sortparms **rp = &res;
48     struct conf_service *service = config->servers->service;
49
50     if (strlen(parms) > 256)
51         return 0;
52     while (*parms)
53     {
54         char parm[256];
55         char *pp;
56         const char *cpp;
57         int increasing;
58         int i;
59         int offset;
60         enum conf_sortkey_type type;
61         struct reclist_sortparms *new;
62
63         if (!(cpp = strchr(parms, ',')))
64             cpp = parms + strlen(parms);
65         strncpy(parm, parms, cpp - parms); 
66         parm[cpp-parms] = '\0';
67
68         if ((pp = strchr(parm, ':')))
69         {
70             increasing = pp[1] == '1' ? 1 : 0;
71             *pp = '\0';
72         }
73         else
74             increasing = 0;
75         if (!strcmp(parm, "relevance"))
76         {
77             type = Metadata_sortkey_relevance;
78             offset = -1;
79         }
80         else
81         {
82             for (i = 0; i < service->num_sortkeys; i++)
83             {
84                 struct conf_sortkey *sk = &service->sortkeys[i];
85                 if (!strcmp(sk->name, parm))
86                 {
87                     type = sk->type;
88                     if (type == Metadata_sortkey_skiparticle)
89                         type = Metadata_sortkey_string;
90                     break;
91                 }
92             }
93             if (i >= service->num_sortkeys)
94             {
95                 yaz_log(YLOG_FATAL, "Bad sortkey: %s", parm);
96                 return 0;
97             }
98             else
99                 offset = i;
100         }
101         new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
102         new->next = 0;
103         new->offset = offset;
104         new->type = type;
105         new->increasing = increasing;
106         rp = &new->next;
107         if (*(parms = cpp))
108             parms++;
109     }
110     return res;
111 }
112
113 static int reclist_cmp(const void *p1, const void *p2)
114 {
115     struct record_cluster *r1 = (*(struct record_cluster**) p1);
116     struct record_cluster *r2 = (*(struct record_cluster**) p2);
117     struct reclist_sortparms *s;
118
119     for (s = sortparms; s; s = s->next)
120     {
121         int res;
122         switch (s->type)
123         {
124             case Metadata_sortkey_relevance:
125                 res = r2->relevance - r1->relevance;
126                 break;
127             case Metadata_sortkey_string:
128                 res = strcmp(r2->sortkeys[s->offset]->text, 
129                              r1->sortkeys[s->offset]->text);
130                 break;
131             case Metadata_sortkey_numeric:
132                 res = 0;
133                 break;
134             default:
135                 yaz_log(YLOG_FATAL, "Bad sort type: %d", s->type);
136                 exit(1);
137         }
138         if (res)
139         {
140             if (s->increasing)
141                 res *= -1;
142             return res;
143         }
144     }
145     return 0;
146 }
147
148 void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
149 {
150     sortparms = parms;
151     qsort(l->flatlist, l->num_records, 
152           sizeof(struct record_cluster*), reclist_cmp);
153     reclist_rewind(l);
154 }
155
156 struct record_cluster *reclist_read_record(struct reclist *l)
157 {
158     if (l->pointer < l->num_records)
159         return l->flatlist[l->pointer++];
160     else
161         return 0;
162 }
163
164 void reclist_rewind(struct reclist *l)
165 {
166     l->pointer = 0;
167 }
168
169 // Jenkins one-at-a-time hash (from wikipedia)
170 static unsigned int hash(const unsigned char *key)
171 {
172     unsigned int hash = 0;
173
174     while (*key)
175     {
176         hash += *(key++);
177         hash += (hash << 10);
178         hash ^= (hash >> 6);
179     }
180     hash += (hash << 3);
181     hash ^= (hash >> 11);
182     hash += (hash << 15);
183     return hash;
184 }
185
186 struct reclist *reclist_create(NMEM nmem, int numrecs)
187 {
188     int hashsize = 1;
189     struct reclist *res;
190
191     assert(numrecs);
192     while (hashsize < numrecs)
193         hashsize <<= 1;
194     res = nmem_malloc(nmem, sizeof(struct reclist));
195     res->hashtable 
196         = nmem_malloc(nmem, hashsize * sizeof(struct reclist_bucket*));
197     memset(res->hashtable, 0, hashsize * sizeof(struct reclist_bucket*));
198     res->hashtable_size = hashsize;
199     res->nmem = nmem;
200     res->hashmask = hashsize - 1; // Creates a bitmask
201
202     res->num_records = 0;
203     res->flatlist = nmem_malloc(nmem, numrecs * sizeof(struct record_cluster*));
204     res->flatlist_size = numrecs;
205
206     return res;
207 }
208
209 // Insert a record. Return record cluster (newly formed or pre-existing)
210 struct record_cluster *reclist_insert( struct reclist *l,
211                                        struct conf_service *service, 
212                                        struct record  *record,
213                                        const char *merge_key, int *total)
214 {
215     unsigned int bucket;
216     struct reclist_bucket **p;
217     struct record_cluster *cluster = 0;
218     
219     assert(service);
220     assert(l);
221     assert(record);
222     assert(merge_key);
223     assert(total);
224
225     bucket = hash((unsigned char*) merge_key) & l->hashmask;
226
227     for (p = &l->hashtable[bucket]; *p; p = &(*p)->next)
228     {
229         // We found a matching record. Merge them
230         if (!strcmp(merge_key, (*p)->record->merge_key))
231         {
232             struct record_cluster *existing = (*p)->record;
233             record->next = existing->records;
234             existing->records = record;
235             cluster = existing;
236             break;
237         }
238     }
239     if (!cluster && l->num_records < l->flatlist_size)
240     {
241         struct reclist_bucket *new =
242             nmem_malloc(l->nmem, sizeof(struct reclist_bucket));
243         struct record_cluster *newc =
244             nmem_malloc(l->nmem, sizeof(struct record_cluster));
245         
246         record->next = 0;
247         new->record = newc;
248         new->next = 0;
249         newc->records = record;
250         newc->merge_key = (char *) merge_key;
251         newc->relevance = 0;
252         newc->term_frequency_vec = 0;
253         newc->recid = (*total)++;
254         newc->metadata = nmem_malloc(l->nmem,
255                 sizeof(struct record_metadata*) * service->num_metadata);
256         memset(newc->metadata, 0, 
257                sizeof(struct record_metadata*) * service->num_metadata);
258         newc->sortkeys = nmem_malloc(l->nmem,
259                 sizeof(struct record_metadata*) * service->num_sortkeys);
260         memset(newc->sortkeys, 0,
261                sizeof(union data_types*) * service->num_sortkeys);
262
263         *p = new;
264         l->flatlist[l->num_records++] = newc;
265         cluster = newc;
266     }
267     return cluster;
268 }
269
270
271 /*
272  * Local variables:
273  * c-basic-offset: 4
274  * indent-tabs-mode: nil
275  * End:
276  * vim: shiftwidth=4 tabstop=8 expandtab
277  */