7703f37b2aa63fc14dd232e69f9adebf1045f9ba
[yaz-moved-to-github.git] / src / sru_facet.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file sru_facet.c
7  * \brief Implements SRU 2.0 facets
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14
15 #include <yaz/srw.h>
16 #include <yaz/wrbuf.h>
17 #if YAZ_HAVE_XML2
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <assert.h>
21 #endif
22
23 #include "sru-p.h"
24 #include <yaz/pquery.h>
25 #include <yaz/facet.h>
26
27 static void insert_field(WRBUF w, const char *field, size_t length,
28                          const char *attr)
29 {
30     const char *cp0 = wrbuf_cstr(w);
31     const char *cp = cp0;
32
33     while (1)
34     {
35         const char *cp2 = strstr(cp, "@attr 1=");
36         if (!cp2)
37             break;
38         cp = cp2 + 8;
39         if (!strncmp(cp, field, length) &&
40             (cp[length] == ' ' || cp[length] == ',' || cp[length] == '\0'))
41         {
42             /* found the field */
43
44             cp += length;
45             wrbuf_insert(w, cp - cp0, attr, strlen(attr));
46             wrbuf_insert(w, cp - cp0, " ", 1);
47             return;
48         }
49         while (*cp && *cp != ',')
50             cp++;
51     }
52     if (wrbuf_len(w))
53         wrbuf_puts(w, ",");
54     wrbuf_puts(w, "@attr 1=");
55     wrbuf_write(w, field, length);
56     wrbuf_puts(w, " ");
57     wrbuf_puts(w, attr);
58 }
59
60 void yaz_sru_facet_request(ODR o, Z_FacetList **facetList, const char **limit,
61                            const char **start, const char **sort)
62 {
63     if (o->direction == ODR_ENCODE)
64     {
65         Z_FacetList *fl = *facetList;
66         if (fl)
67         {
68             WRBUF w_limit = wrbuf_alloc();
69             WRBUF w_start = wrbuf_alloc();
70             WRBUF w_sort = wrbuf_alloc();
71             int i;
72             for (i = 0; i < fl->num; i++)
73             {
74                 struct yaz_facet_attr av;
75                 yaz_facet_attr_init(&av);
76                 yaz_facet_attr_get_z_attributes(fl->elements[i]->attributes,
77                                                 &av);
78                 if (av.errcode == 0)
79                 {
80                     if (av.limit)
81                     {
82                         wrbuf_printf(w_limit, "%d", av.limit);
83                         if (av.useattr)
84                             wrbuf_printf(w_limit, ":%s", av.useattr);
85                         wrbuf_puts(w_limit, ",");
86                     }
87                     if (av.start || av.useattr)
88                     {
89                         wrbuf_printf(w_start, "%d",
90                                      av.start == 0 ? 1 : av.start);
91                         if (av.useattr)
92                             wrbuf_printf(w_start, ":%s", av.useattr);
93                         wrbuf_puts(w_start, ",");
94                     }
95                     if (av.sortorder == 1)
96                     {
97                         /* allow sorting per field */
98                         /* not really according to spec */
99                         wrbuf_printf(w_sort, "alphanumeric");
100                         if (av.useattr)
101                             wrbuf_printf(w_sort, ":%s", av.useattr);
102                         wrbuf_puts(w_sort, ",");
103                     }
104                 }
105             }
106             if (wrbuf_len(w_limit) > 1)
107             {
108                 wrbuf_cut_right(w_limit, 1); /* remove , */
109                 *limit = odr_strdup(o, wrbuf_cstr(w_limit));
110             }
111             if (wrbuf_len(w_start) > 1)
112             {
113                 wrbuf_cut_right(w_start, 1); /* remove , */
114                 *start = odr_strdup(o, wrbuf_cstr(w_start));
115             }
116             if (wrbuf_len(w_sort) > 1)
117             {
118                 wrbuf_cut_right(w_sort, 1); /* remove , */
119                 *sort = odr_strdup(o, wrbuf_cstr(w_sort));
120             }
121             wrbuf_destroy(w_limit);
122             wrbuf_destroy(w_start);
123             wrbuf_destroy(w_sort);
124         }
125     }
126     else if (o->direction == ODR_DECODE)
127     {
128         WRBUF w = wrbuf_alloc();
129
130         if (*limit)
131         {
132             const char *cp = *limit;
133             int nor = 0;
134             int val = 0;
135             while (sscanf(cp, "%d%n", &val, &nor) >= 1 && nor > 0)
136             {
137                 cp += nor;
138                 if (*cp == ':') /* field name follows */
139                 {
140                     char tmp[40];
141                     const char *cp0 = ++cp;
142                     while (*cp && *cp != ',')
143                         cp++;
144                     sprintf(tmp, "@attr 3=%d", val);
145                     insert_field(w, cp0, cp - cp0, tmp);
146                 }
147                 if (*cp != ',')
148                     break;
149                 cp++;
150             }
151         }
152         if (*start)
153         {
154             const char *cp = *start;
155             int nor = 0;
156             int val = 0;
157             while (sscanf(cp, "%d%n", &val, &nor) >= 1 && nor > 0)
158             {
159                 cp += nor;
160                 if (*cp == ':') /* field name follows */
161                 {
162                     char tmp[40];
163                     const char *cp0 = ++cp;
164                     while (*cp && *cp != ',')
165                         cp++;
166                     sprintf(tmp, "@attr 4=%d", val);
167                     insert_field(w, cp0, cp - cp0, tmp);
168                 }
169                 if (*cp != ',')
170                     break;
171                 cp++;
172             }
173         }
174
175         if (*sort)
176         {
177             const char *cp = *sort;
178             while (1)
179             {
180                 int val = 0;
181                 const char *cp0 = cp;
182                 while (*cp && *cp != ':' && *cp != ',')
183                     cp++;
184                 if (!strncmp(cp0, "alphanumeric", cp - cp0))
185                     val = 1;
186                 if (*cp == ':') /* field name follows */
187                 {
188                     char tmp[40];
189                     cp0 = ++cp;
190                     while (*cp && *cp != ',')
191                         cp++;
192                     sprintf(tmp, "@attr 2=%d", val);
193                     insert_field(w, cp0, cp - cp0, tmp);
194                 }
195                 if (*cp != ',')
196                     break;
197                 cp++;
198             }
199         }
200
201         if (wrbuf_len(w))
202             *facetList = yaz_pqf_parse_facet_list(o, wrbuf_cstr(w));
203         else
204             *facetList = 0;
205         wrbuf_destroy(w);
206     }
207 }
208
209 #if YAZ_HAVE_XML2
210 void yaz_sru_facet_response(ODR o, Z_FacetList **facetList, xmlNodePtr n)
211 {
212     if (o->direction == ODR_ENCODE)
213     {
214         Z_FacetList *fl = *facetList;
215         if (fl)
216         {
217             int i;
218             const char *ns =
219                 "http://docs.oasis-open.org/ns/search-ws/facetedResults";
220             xmlNode *p1 = xmlNewChild(n, 0, BAD_CAST "facetedResults", 0);
221             xmlNsPtr ns_fr = xmlNewNs(p1, BAD_CAST ns, BAD_CAST "fr");
222             for (i = 0; i < fl->num; i++)
223             {
224                 Z_FacetField *ff = fl->elements[i];
225                 xmlNode *p2 = xmlNewChild(p1, ns_fr, BAD_CAST "facet", 0);
226                 int j;
227                 xmlNode *p3;
228                 struct yaz_facet_attr av;
229                 yaz_facet_attr_init(&av);
230                 yaz_facet_attr_get_z_attributes(ff->attributes, &av);
231                 add_xsd_string(p2, "index", av.useattr);
232                 p3 = xmlNewChild(p2, 0, BAD_CAST "terms", 0);
233                 for (j = 0; j < ff->num_terms; j++)
234                 {
235                     Z_FacetTerm *ft = ff->terms[j];
236                     Z_Term *zt = ft->term;
237                     xmlNode *p4 = xmlNewChild(p3, 0, BAD_CAST "term", 0);
238                     if (zt->which == Z_Term_general)
239                         add_xsd_string_n(p4, "actualTerm",
240                                          (char *) zt->u.general->buf,
241                                          zt->u.general->len);
242                     if (ft->count)
243                         add_xsd_integer(p4, "count", ft->count);
244                 }
245             }
246         }
247     }
248     else if (o->direction == ODR_DECODE)
249     {
250         Z_FacetList *fl = (Z_FacetList *) odr_malloc(o, sizeof(*fl));
251         xmlNode *p1;
252
253         fl->num = 0;
254         for (p1 = n->children; p1; p1 = p1->next)
255             if (yaz_match_xsd_element(p1, "facet"))
256                 fl->num++;
257         if (fl->num > 0)
258         {
259             int i = 0;
260             *facetList = fl;
261             fl->elements = (Z_FacetField **)
262                 odr_malloc(o, sizeof(*fl->elements) * fl->num);
263             for (p1 = n->children; p1; p1 = p1->next)
264                 if (yaz_match_xsd_element(p1, "facet"))
265                 {
266                     char *index_name = 0;
267                     xmlNode *p_terms = 0;
268                     xmlNode *p2 = p1->children;
269                     Z_FacetField *ff = (Z_FacetField *)
270                         odr_malloc(o, sizeof(*ff));
271                     fl->elements[i++] = ff;
272                     ff->attributes = 0;
273                     ff->num_terms = 0;
274                     ff->terms = 0;
275                     for (; p2; p2 = p2->next)
276                     {
277                         if (yaz_match_xsd_string(p2, "index", o, &index_name))
278                             ;
279                         else if (yaz_match_xsd_element(p2, "terms"))
280                             p_terms = p2;
281                     }
282                     if (index_name)
283                     {
284                         Z_AttributeList *al =
285                             (Z_AttributeList*) odr_malloc(o, sizeof(*al));
286                         Z_ComplexAttribute *ca =
287                             (Z_ComplexAttribute *) odr_malloc(o, sizeof(*ca));
288                         Z_AttributeElement *ae =
289                             (Z_AttributeElement *) odr_malloc(o, sizeof(*ae));
290                         al->num_attributes = 1;
291                         al->attributes = (Z_AttributeElement **)
292                             odr_malloc(o, sizeof(*al->attributes));
293                         al->attributes[0] = ae;
294                         ae->attributeSet = 0;
295                         ae->attributeType = odr_intdup(o, 1);
296                         ae->which = Z_AttributeValue_complex;
297                         ae->value.complex = ca;
298                         ca->num_semanticAction = 0;
299                         ca->semanticAction = 0;
300                         ca->num_list = 1;
301                         ca->list = (Z_StringOrNumeric **)
302                             odr_malloc(o, sizeof(*ca->list));
303                         ca->list[0] = (Z_StringOrNumeric *)
304                             odr_malloc(o, sizeof(**ca->list));
305                         ca->list[0]->which = Z_StringOrNumeric_string;
306                         ca->list[0]->u.string = index_name;
307                         ff->attributes = al;
308                     }
309                     if (p_terms)
310                     {
311                         xmlNode *p;
312                         int i = 0;
313                         for (p = p_terms->children; p; p = p->next)
314                         {
315                             if (yaz_match_xsd_element(p, "term"))
316                                 ff->num_terms++;
317                         }
318                         if (ff->num_terms)
319                             ff->terms = (Z_FacetTerm **)
320                                 odr_malloc(o,
321                                            sizeof(*ff->terms) * ff->num_terms);
322                         for (p = p_terms->children; p; p = p->next)
323                         {
324                             if (yaz_match_xsd_element(p, "term"))
325                             {
326                                 char *cstr = 0;
327                                 Odr_int *count = 0;
328                                 xmlNode *p2 = p->children;
329                                 for (; p2; p2 = p2->next)
330                                 {
331                                     if (yaz_match_xsd_string(p2, "actualTerm", o,
332                                                          &cstr))
333                                         ;
334                                     else if (yaz_match_xsd_integer(p2, "count", o,
335                                                                &count))
336                                         ;
337                                 }
338                                 if (cstr && count)
339                                 {
340                                     ff->terms[i++] =
341                                         facet_term_create_cstr(o, cstr, *count);
342                                 }
343                             }
344                         }
345                         ff->num_terms = i;
346                         if (ff->num_terms == 0)
347                             ff->terms = 0;
348                     }
349                 }
350
351         }
352     }
353 }
354
355 #endif
356
357
358 /*
359  * Local variables:
360  * c-basic-offset: 4
361  * c-file-style: "Stroustrup"
362  * indent-tabs-mode: nil
363  * End:
364  * vim: shiftwidth=4 tabstop=8 expandtab
365  */
366