Remove log msg
[pazpar2-moved-to-github.git] / src / reclists.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2012 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 "ppmutex.h"
29 #include "session.h"
30 #include "reclists.h"
31 #include "jenkins_hash.h"
32
33 struct reclist
34 {
35     struct reclist_bucket **hashtable;
36     unsigned hash_size;
37
38     int num_records;
39     struct reclist_bucket *sorted_list;
40     struct reclist_bucket *sorted_ptr;
41     NMEM nmem;
42     YAZ_MUTEX mutex;
43 };
44
45 struct reclist_bucket
46 {
47     struct record_cluster *record;
48     struct reclist_bucket *hash_next;
49     struct reclist_bucket *sorted_next;
50     struct reclist_sortparms *sort_parms;
51 };
52
53 struct reclist_sortparms *reclist_parse_sortparms(NMEM nmem, const char *parms,
54                                                   struct conf_service *service)
55 {
56     struct reclist_sortparms *res = 0;
57     struct reclist_sortparms **rp = &res;
58
59     if (strlen(parms) > 256)
60         return 0;
61     while (*parms)
62     {
63         char parm[256];
64         char *pp;
65         const char *cpp;
66         int increasing = 0;
67         int i;
68         int offset = 0;
69         enum conf_sortkey_type type = Metadata_sortkey_string;
70         struct reclist_sortparms *new;
71
72         if (!(cpp = strchr(parms, ',')))
73             cpp = parms + strlen(parms);
74         strncpy(parm, parms, cpp - parms);
75         parm[cpp-parms] = '\0';
76
77         if ((pp = strchr(parm, ':')))
78         {
79             if (pp[1] == '1')
80                 increasing = 1;
81             else if (pp[1] == '0')
82                 increasing = 0;
83             else
84             {
85                 yaz_log(YLOG_FATAL, "Bad sortkey modifier: %s", parm);
86                 return 0;
87             }
88
89             if (pp[2])
90             {
91                 if (pp[2] == 'p')
92                     type = Metadata_sortkey_position;
93                 else
94                     yaz_log(YLOG_FATAL, "Bad sortkey modifier: %s", parm);
95             }
96             *pp = '\0';
97         }
98         if (type != Metadata_sortkey_position)
99         {
100             if (!strcmp(parm, "relevance"))
101             {
102                 type = Metadata_sortkey_relevance;
103             }
104             else if (!strcmp(parm, "position"))
105             {
106                 type = Metadata_sortkey_position;
107             }
108             else
109             {
110                 for (i = 0; i < service->num_sortkeys; i++)
111                 {
112                     struct conf_sortkey *sk = &service->sortkeys[i];
113                     if (!strcmp(sk->name, parm))
114                     {
115                         type = sk->type;
116                         if (type == Metadata_sortkey_skiparticle)
117                             type = Metadata_sortkey_string;
118                         break;
119                     }
120                 }
121                 if (i >= service->num_sortkeys)
122                 {
123                     yaz_log(YLOG_FATAL, "Sortkey not defined in service: %s",
124                             parm);
125                     return 0;
126                 }
127                 offset = i;
128             }
129         }
130         new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
131         new->next = 0;
132         new->offset = offset;
133         new->type = type;
134         new->increasing = increasing;
135         new->name = nmem_strdup(nmem, parm);
136         rp = &new->next;
137         if (*(parms = cpp))
138             parms++;
139     }
140     return res;
141 }
142
143 static int reclist_cmp(const void *p1, const void *p2)
144 {
145     struct reclist_sortparms *sortparms =
146         (*(struct reclist_bucket **) p1)->sort_parms;
147     struct record_cluster *r1 = (*(struct reclist_bucket**) p1)->record;
148     struct record_cluster *r2 = (*(struct reclist_bucket**) p2)->record;
149     struct reclist_sortparms *s;
150     int res = 0;
151
152     for (s = sortparms; s && res == 0; s = s->next)
153     {
154         union data_types *ut1 = r1->sortkeys[s->offset];
155         union data_types *ut2 = r2->sortkeys[s->offset];
156         const char *s1, *s2;
157         switch (s->type)
158         {
159         case Metadata_sortkey_relevance:
160             res = r2->relevance_score - r1->relevance_score;
161             break;
162         case Metadata_sortkey_string:
163             s1 = ut1 ? ut1->text.sort : "";
164             s2 = ut2 ? ut2->text.sort : "";
165             res = strcmp(s2, s1);
166             if (res)
167             {
168                 if (s->increasing)
169                     res *= -1;
170             }
171             break;
172         case Metadata_sortkey_numeric:
173             if (ut1 && ut2)
174             {
175                 if (s->increasing)
176                     res = ut1->number.min  - ut2->number.min;
177                 else
178                     res = ut2->number.max  - ut1->number.max;
179             }
180             else if (ut1 && !ut2)
181                 res = -1;
182             else if (!ut1 && ut2)
183                 res = 1;
184             else
185                 res = 0;
186             break;
187         case Metadata_sortkey_position:
188             if (r1->records && r2->records)
189             {
190                 int pos1 = 0, pos2 = 0;
191                 struct record *rec;
192                 for (rec = r1->records; rec; rec = rec->next)
193                     if (pos1 == 0 || rec->position < pos1)
194                         pos1 = rec->position;
195                 for (rec = r2->records; rec; rec = rec->next)
196                     if (pos2 == 0 || rec->position < pos2)
197                         pos2 = rec->position;
198                 res = pos1 - pos2;
199             }
200             break;
201         default:
202             yaz_log(YLOG_WARN, "Bad sort type: %d", s->type);
203             res = 0;
204             break;
205         }
206     }
207     if (res == 0)
208         res = strcmp(r1->recid, r2->recid);
209     return res;
210 }
211
212 void reclist_limit(struct reclist *l, struct session *se)
213 {
214     unsigned i;
215     int num = 0;
216     struct reclist_bucket **pp = &l->sorted_list;
217
218     reclist_enter(l);
219     for (i = 0; i < l->hash_size; i++)
220     {
221         struct reclist_bucket *p;
222         for (p = l->hashtable[i]; p; p = p->hash_next)
223         {
224             if (session_check_cluster_limit(se, p->record))
225             {
226                 *pp = p;
227                 pp = &p->sorted_next;
228                 num++;
229             }
230         }
231     }
232     *pp = 0;
233     l->num_records = num;
234     reclist_leave(l);
235 }
236
237 void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
238 {
239     struct reclist_bucket **flatlist = xmalloc(sizeof(*flatlist) * l->num_records);
240     struct reclist_bucket *ptr;
241     struct reclist_bucket **prev;
242     int i = 0;
243
244     reclist_enter(l);
245
246     ptr = l->sorted_list;
247     prev = &l->sorted_list;
248     while (ptr)
249     {
250         ptr->sort_parms = parms;
251         flatlist[i] = ptr;
252         ptr = ptr->sorted_next;
253         i++;
254     }
255     assert(i == l->num_records);
256
257     qsort(flatlist, l->num_records, sizeof(*flatlist), reclist_cmp);
258     for (i = 0; i < l->num_records; i++)
259     {
260         *prev = flatlist[i];
261         prev = &flatlist[i]->sorted_next;
262     }
263     *prev = 0;
264
265     xfree(flatlist);
266
267     reclist_leave(l);
268 }
269
270 struct record_cluster *reclist_read_record(struct reclist *l)
271 {
272     if (l && l->sorted_ptr)
273     {
274         struct record_cluster *t = l->sorted_ptr->record;
275         l->sorted_ptr = l->sorted_ptr->sorted_next;
276         return t;
277     }
278     else
279         return 0;
280 }
281
282 void reclist_enter(struct reclist *l)
283 {
284     yaz_mutex_enter(l->mutex);
285     if (l)
286         l->sorted_ptr = l->sorted_list;
287 }
288
289
290 void reclist_leave(struct reclist *l)
291 {
292     yaz_mutex_leave(l->mutex);
293     if (l)
294         l->sorted_ptr = l->sorted_list;
295 }
296
297
298 struct reclist *reclist_create(NMEM nmem)
299 {
300     struct reclist *res = nmem_malloc(nmem, sizeof(struct reclist));
301     res->hash_size = 399;
302     res->hashtable
303         = nmem_malloc(nmem, res->hash_size * sizeof(struct reclist_bucket*));
304     memset(res->hashtable, 0, res->hash_size * sizeof(struct reclist_bucket*));
305     res->nmem = nmem;
306
307     res->sorted_ptr = 0;
308     res->sorted_list = 0;
309
310     res->num_records = 0;
311     res->mutex = 0;
312     pazpar2_mutex_create(&res->mutex, "reclist");
313     return res;
314 }
315
316 void reclist_destroy(struct reclist *l)
317 {
318     if (l)
319     {
320         unsigned i;
321         for (i = 0; i < l->hash_size; i++)
322         {
323             struct reclist_bucket *p;
324             for (p = l->hashtable[i]; p; p = p->hash_next)
325             {
326                 wrbuf_destroy(p->record->relevance_explain1);
327                 wrbuf_destroy(p->record->relevance_explain2);
328             }
329         }
330         yaz_mutex_destroy(&l->mutex);
331     }
332 }
333
334 int reclist_get_num_records(struct reclist *l)
335 {
336     if (l)
337         return l->num_records;
338     return 0;
339 }
340
341 // Insert a record. Return record cluster (newly formed or pre-existing)
342 struct record_cluster *reclist_insert(struct reclist *l,
343                                       struct conf_service *service,
344                                       struct record *record,
345                                       const char *merge_key, int *total)
346 {
347     unsigned int bucket;
348     struct reclist_bucket **p;
349     struct record_cluster *cluster = 0;
350
351     assert(service);
352     assert(l);
353     assert(record);
354     assert(merge_key);
355     assert(total);
356
357     bucket = jenkins_hash((unsigned char*) merge_key) % l->hash_size;
358
359     yaz_mutex_enter(l->mutex);
360     for (p = &l->hashtable[bucket]; *p; p = &(*p)->hash_next)
361     {
362         // We found a matching record. Merge them
363         if (!strcmp(merge_key, (*p)->record->merge_key))
364         {
365             struct record_cluster *existing = (*p)->record;
366             struct record *re = existing->records;
367
368             for (; re; re = re->next)
369             {
370                 if (re->client == record->client &&
371                     record_compare(record, re, service))
372                 {
373                     yaz_mutex_leave(l->mutex);
374                     return 0;
375                 }
376             }
377             record->next = existing->records;
378             existing->records = record;
379             cluster = existing;
380             break;
381         }
382     }
383     if (!cluster)
384     {
385         struct reclist_bucket *new =
386             nmem_malloc(l->nmem, sizeof(*new));
387
388         cluster = nmem_malloc(l->nmem, sizeof(*cluster));
389
390         record->next = 0;
391         new->record = cluster;
392         new->hash_next = 0;
393         cluster->records = record;
394         cluster->merge_key = nmem_strdup(l->nmem, merge_key);
395         cluster->relevance_score = 0;
396         cluster->term_frequency_vec = 0;
397         cluster->recid = nmem_strdup(l->nmem, merge_key);
398         (*total)++;
399         cluster->metadata =
400             nmem_malloc(l->nmem,
401                         sizeof(struct record_metadata*) * service->num_metadata);
402         memset(cluster->metadata, 0,
403                sizeof(struct record_metadata*) * service->num_metadata);
404         cluster->sortkeys =
405             nmem_malloc(l->nmem, sizeof(struct record_metadata*) * service->num_sortkeys);
406         memset(cluster->sortkeys, 0,
407                sizeof(union data_types*) * service->num_sortkeys);
408
409         cluster->relevance_explain1 = wrbuf_alloc();
410         cluster->relevance_explain2 = wrbuf_alloc();
411         /* attach to hash list */
412         *p = new;
413         l->num_records++;
414     }
415     yaz_mutex_leave(l->mutex);
416     return cluster;
417 }
418
419 int reclist_sortparms_cmp(struct reclist_sortparms *sort1, struct reclist_sortparms *sort2)
420 {
421     int rc;
422     if (sort1 == sort2)
423         return 0;
424     if (sort1 == 0 || sort2 == 0)
425         return 1;
426     rc = strcmp(sort1->name, sort2->name) || sort1->increasing != sort2->increasing || sort1->type != sort2->type;
427     return rc;
428 }
429 /*
430  * Local variables:
431  * c-basic-offset: 4
432  * c-file-style: "Stroustrup"
433  * indent-tabs-mode: nil
434  * End:
435  * vim: shiftwidth=4 tabstop=8 expandtab
436  */
437