Happy new year
[pazpar2-moved-to-github.git] / src / termlists.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 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 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <yaz/yaz-util.h>
28
29 #include "termlists.h"
30 #include "jenkins_hash.h"
31
32 // Discussion:
33 // As terms are found in incoming records, they are added to (or updated in) a
34 // Hash table. When term records are updated, a frequency value is updated. At
35 // the same time, a highscore is maintained for the most frequent terms.
36
37 struct termlist_bucket
38 {
39     struct termlist_score term;
40     struct termlist_bucket *next;
41 };
42
43 struct termlist
44 {
45     struct termlist_bucket **hashtable;
46     unsigned hash_size;
47
48     int no_entries;
49     NMEM nmem;
50 };
51
52 struct termlist *termlist_create(NMEM nmem)
53 {
54     struct termlist *res = nmem_malloc(nmem, sizeof(struct termlist));
55     res->hash_size = 399;
56     res->hashtable =
57         nmem_malloc(nmem, res->hash_size * sizeof(struct termlist_bucket*));
58     memset(res->hashtable, 0, res->hash_size * sizeof(struct termlist_bucket*));
59     res->nmem = nmem;
60     res->no_entries = 0;
61     return res;
62 }
63
64 void termlist_insert(struct termlist *tl, const char *display_term,
65                      const char *norm_term, int freq)
66 {
67     unsigned int bucket;
68     struct termlist_bucket **p;
69     char buf[256];
70
71     if (strlen(norm_term) > 255)
72         return;
73     strcpy(buf, norm_term);
74     bucket = jenkins_hash((unsigned char *)buf) % tl->hash_size;
75     for (p = &tl->hashtable[bucket]; *p; p = &(*p)->next)
76     {
77         if (!strcmp(buf, (*p)->term.norm_term))
78         {
79             (*p)->term.frequency += freq;
80             break;
81         }
82     }
83     if (!*p) // We made it to the end of the bucket without finding match
84     {
85         struct termlist_bucket *new = nmem_malloc(tl->nmem,
86                 sizeof(struct termlist_bucket));
87         new->term.norm_term = nmem_strdup(tl->nmem, buf);
88         new->term.display_term = *display_term ?
89             nmem_strdup(tl->nmem, display_term) : new->term.norm_term;
90         new->term.frequency = freq;
91         new->next = 0;
92         *p = new;
93         tl->no_entries++;
94     }
95 }
96
97 static int compare(const void *s1, const void *s2)
98 {
99     struct termlist_score **p1 = (struct termlist_score **) s1;
100     struct termlist_score **p2 = (struct termlist_score **) s2;
101     int d = (*p2)->frequency - (*p1)->frequency;
102     if (d)
103         return d;
104     return strcmp((*p1)->display_term, (*p2)->display_term);
105 }
106
107 struct termlist_score **termlist_highscore(struct termlist *tl, int *len,
108                                            NMEM nmem)
109 {
110     struct termlist_score **highscore =
111         (struct termlist_score **)
112         nmem_malloc(nmem, tl->no_entries * sizeof(*highscore));
113
114     int no = 0;
115     unsigned bucket;
116     for (bucket = 0; bucket < tl->hash_size; bucket++)
117     {
118         struct termlist_bucket *p;
119         for (p = tl->hashtable[bucket]; p; p = p->next)
120             highscore[no++] = &p->term;
121     }
122     assert(no == tl->no_entries);
123     qsort(highscore, tl->no_entries, sizeof(struct termlist_score*), compare);
124     *len = tl->no_entries;
125     return highscore;
126 }
127
128 /*
129  * Local variables:
130  * c-basic-offset: 4
131  * c-file-style: "Stroustrup"
132  * indent-tabs-mode: nil
133  * End:
134  * vim: shiftwidth=4 tabstop=8 expandtab
135  */
136