facetSort, facetStart honored.
[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", av.start);
90                         if (av.useattr)
91                             wrbuf_printf(w_start, ":%s", av.useattr);
92                         wrbuf_puts(w_start, ",");
93                     }
94                     if (av.sortorder == 1)
95                     {
96                         /* allow sorting per field */
97                         /* not really according to spec */
98                         wrbuf_printf(w_sort, "alphanumeric");
99                         if (av.useattr)
100                             wrbuf_printf(w_sort, ":%s", av.useattr);
101                         wrbuf_puts(w_sort, ",");
102                     }
103                 }
104             }
105             if (wrbuf_len(w_limit) > 1)
106             {
107                 wrbuf_cut_right(w_limit, 1); /* remove , */
108                 *limit = odr_strdup(o, wrbuf_cstr(w_limit));
109             }
110             if (wrbuf_len(w_start) > 1)
111             {
112                 wrbuf_cut_right(w_start, 1); /* remove , */
113                 *start = odr_strdup(o, wrbuf_cstr(w_start));
114             }
115             if (wrbuf_len(w_sort) > 1)
116             {
117                 wrbuf_cut_right(w_sort, 1); /* remove , */
118                 *sort = odr_strdup(o, wrbuf_cstr(w_sort));
119             }
120             wrbuf_destroy(w_limit);
121             wrbuf_destroy(w_start);
122             wrbuf_destroy(w_sort);
123         }
124     }
125     else if (o->direction == ODR_DECODE)
126     {
127         WRBUF w = wrbuf_alloc();
128
129         if (*limit)
130         {
131             const char *cp = *limit;
132             int nor = 0;
133             int val = 0;
134             while (sscanf(cp, "%d%n", &val, &nor) >= 1 && nor > 0)
135             {
136                 cp += nor;
137                 if (*cp == ':') /* field name follows */
138                 {
139                     char tmp[40];
140                     const char *cp0 = ++cp;
141                     while (*cp && *cp != ',')
142                         cp++;
143                     sprintf(tmp, "@attr 3=%d", val);
144                     insert_field(w, cp0, cp - cp0, tmp);
145                 }
146                 if (*cp != ',')
147                     break;
148                 cp++;
149             }
150         }
151         if (*start)
152         {
153             const char *cp = *start;
154             int nor = 0;
155             int val = 0;
156             while (sscanf(cp, "%d%n", &val, &nor) >= 1 && nor > 0)
157             {
158                 cp += nor;
159                 if (*cp == ':') /* field name follows */
160                 {
161                     char tmp[40];
162                     const char *cp0 = ++cp;
163                     while (*cp && *cp != ',')
164                         cp++;
165                     sprintf(tmp, "@attr 4=%d", val);
166                     insert_field(w, cp0, cp - cp0, tmp);
167                 }
168                 if (*cp != ',')
169                     break;
170                 cp++;
171             }
172         }
173
174         if (*sort)
175         {
176             const char *cp = *sort;
177             while (1)
178             {
179                 int val = 0;
180                 const char *cp0 = cp;
181                 while (*cp && *cp != ':' && *cp != ',')
182                     cp++;
183                 if (!strncmp(cp0, "alphanumeric", cp - cp0))
184                     val = 1;
185                 if (*cp == ':') /* field name follows */
186                 {
187                     char tmp[40];
188                     cp0 = ++cp;
189                     while (*cp && *cp != ',')
190                         cp++;
191                     sprintf(tmp, "@attr 2=%d", val);
192                     insert_field(w, cp0, cp - cp0, tmp);
193                 }
194                 if (*cp != ',')
195                     break;
196                 cp++;
197             }
198         }
199
200         if (wrbuf_len(w))
201             *facetList = yaz_pqf_parse_facet_list(o, wrbuf_cstr(w));
202         else
203             *facetList = 0;
204         wrbuf_destroy(w);
205     }
206 }
207
208 #if YAZ_HAVE_XML2
209 void yaz_sru_facet_response(ODR o, Z_FacetList **facetList, xmlNodePtr n)
210 {
211     if (o->direction == ODR_ENCODE)
212     {
213         Z_FacetList *fl = *facetList;
214         if (fl)
215         {
216             int i;
217             const char *ns =
218                 "http://docs.oasis-open.org/ns/search-ws/facetedResults";
219             xmlNode *p1 = xmlNewChild(n, 0, BAD_CAST "facetedResults", 0);
220             xmlNsPtr ns_fr = xmlNewNs(p1, BAD_CAST ns, BAD_CAST "fr");
221             xmlSetNs(p1, ns_fr);
222             for (i = 0; i < fl->num; i++)
223             {
224                 Z_FacetField *ff = fl->elements[i];
225                 xmlNode *p2 = xmlNewChild(p1, 0, 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