67e4733342a80d831ec5be7faab61fe211bd9ffc
[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             xmlSetNs(p1, ns_fr);
223             for (i = 0; i < fl->num; i++)
224             {
225                 Z_FacetField *ff = fl->elements[i];
226                 xmlNode *p2 = xmlNewChild(p1, 0, BAD_CAST "facet", 0);
227                 int j;
228                 xmlNode *p3;
229                 struct yaz_facet_attr av;
230                 yaz_facet_attr_init(&av);
231                 yaz_facet_attr_get_z_attributes(ff->attributes, &av);
232                 add_xsd_string(p2, "index", av.useattr);
233                 p3 = xmlNewChild(p2, 0, BAD_CAST "terms", 0);
234                 for (j = 0; j < ff->num_terms; j++)
235                 {
236                     Z_FacetTerm *ft = ff->terms[j];
237                     Z_Term *zt = ft->term;
238                     xmlNode *p4 = xmlNewChild(p3, 0, BAD_CAST "term", 0);
239                     if (zt->which == Z_Term_general)
240                         add_xsd_string_n(p4, "actualTerm",
241                                          (char *) zt->u.general->buf,
242                                          zt->u.general->len);
243                     if (ft->count)
244                         add_xsd_integer(p4, "count", ft->count);
245                 }
246             }
247         }
248     }
249     else if (o->direction == ODR_DECODE)
250     {
251         Z_FacetList *fl = (Z_FacetList *) odr_malloc(o, sizeof(*fl));
252         xmlNode *p1;
253
254         fl->num = 0;
255         for (p1 = n->children; p1; p1 = p1->next)
256             if (yaz_match_xsd_element(p1, "facet"))
257                 fl->num++;
258         if (fl->num > 0)
259         {
260             int i = 0;
261             *facetList = fl;
262             fl->elements = (Z_FacetField **)
263                 odr_malloc(o, sizeof(*fl->elements) * fl->num);
264             for (p1 = n->children; p1; p1 = p1->next)
265                 if (yaz_match_xsd_element(p1, "facet"))
266                 {
267                     char *index_name = 0;
268                     xmlNode *p_terms = 0;
269                     xmlNode *p2 = p1->children;
270                     Z_FacetField *ff = (Z_FacetField *)
271                         odr_malloc(o, sizeof(*ff));
272                     fl->elements[i++] = ff;
273                     ff->attributes = 0;
274                     ff->num_terms = 0;
275                     ff->terms = 0;
276                     for (; p2; p2 = p2->next)
277                     {
278                         if (yaz_match_xsd_string(p2, "index", o, &index_name))
279                             ;
280                         else if (yaz_match_xsd_element(p2, "terms"))
281                             p_terms = p2;
282                     }
283                     if (index_name)
284                     {
285                         Z_AttributeList *al =
286                             (Z_AttributeList*) odr_malloc(o, sizeof(*al));
287                         Z_ComplexAttribute *ca =
288                             (Z_ComplexAttribute *) odr_malloc(o, sizeof(*ca));
289                         Z_AttributeElement *ae =
290                             (Z_AttributeElement *) odr_malloc(o, sizeof(*ae));
291                         al->num_attributes = 1;
292                         al->attributes = (Z_AttributeElement **)
293                             odr_malloc(o, sizeof(*al->attributes));
294                         al->attributes[0] = ae;
295                         ae->attributeSet = 0;
296                         ae->attributeType = odr_intdup(o, 1);
297                         ae->which = Z_AttributeValue_complex;
298                         ae->value.complex = ca;
299                         ca->num_semanticAction = 0;
300                         ca->semanticAction = 0;
301                         ca->num_list = 1;
302                         ca->list = (Z_StringOrNumeric **)
303                             odr_malloc(o, sizeof(*ca->list));
304                         ca->list[0] = (Z_StringOrNumeric *)
305                             odr_malloc(o, sizeof(**ca->list));
306                         ca->list[0]->which = Z_StringOrNumeric_string;
307                         ca->list[0]->u.string = index_name;
308                         ff->attributes = al;
309                     }
310                     if (p_terms)
311                     {
312                         xmlNode *p;
313                         int i = 0;
314                         for (p = p_terms->children; p; p = p->next)
315                         {
316                             if (yaz_match_xsd_element(p, "term"))
317                                 ff->num_terms++;
318                         }
319                         if (ff->num_terms)
320                             ff->terms = (Z_FacetTerm **)
321                                 odr_malloc(o,
322                                            sizeof(*ff->terms) * ff->num_terms);
323                         for (p = p_terms->children; p; p = p->next)
324                         {
325                             if (yaz_match_xsd_element(p, "term"))
326                             {
327                                 char *cstr = 0;
328                                 Odr_int *count = 0;
329                                 xmlNode *p2 = p->children;
330                                 for (; p2; p2 = p2->next)
331                                 {
332                                     if (yaz_match_xsd_string(p2, "actualTerm", o,
333                                                          &cstr))
334                                         ;
335                                     else if (yaz_match_xsd_integer(p2, "count", o,
336                                                                &count))
337                                         ;
338                                 }
339                                 if (cstr && count)
340                                 {
341                                     ff->terms[i++] =
342                                         facet_term_create_cstr(o, cstr, *count);
343                                 }
344                             }
345                         }
346                         ff->num_terms = i;
347                         if (ff->num_terms == 0)
348                             ff->terms = 0;
349                     }
350                 }
351
352         }
353     }
354 }
355
356 #endif
357
358
359 /*
360  * Local variables:
361  * c-basic-offset: 4
362  * c-file-style: "Stroustrup"
363  * indent-tabs-mode: nil
364  * End:
365  * vim: shiftwidth=4 tabstop=8 expandtab
366  */
367