4e0a74a4683bc88a066ebdb18f8aa11d4ee01c13
[yaz-moved-to-github.git] / src / solr.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file srwutil.c
7  * \brief Implements SRW/SRU utilities.
8  */
9
10 #include <stdlib.h>
11 #include <assert.h>
12 #include <yaz/srw.h>
13 #include <yaz/matchstr.h>
14 #include <yaz/yaz-iconv.h>
15 #include <yaz/log.h>
16 #include <yaz/facet.h>
17
18 #include "sru-p.h"
19
20 #if YAZ_HAVE_XML2
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
23
24
25 const char *xml_node_attribute_value_get(xmlNodePtr ptr, const char *node_name, const char *attribute_name) {
26
27     struct _xmlAttr *attr;
28     // check if the node name matches
29     if (strcmp((const char*) ptr->name, node_name))
30         return 0;
31     // check if the attribute name and return the value
32     for (attr = ptr->properties; attr; attr = attr->next)
33         if (attr->children && attr->children->type == XML_TEXT_NODE) {
34             if (!strcmp((const char *) attr->name, attribute_name))
35                 return (const char *) attr->children->content;
36         }
37     return 0;
38 }
39
40
41 static int match_xml_node_attribute(xmlNodePtr ptr, const char *node_name, const char *attribute_name, const char *value)
42 {
43     const char *attribute_value;
44     // check if the node name matches
45     if (strcmp((const char*) ptr->name, node_name))
46         return 0;
47     attribute_value = xml_node_attribute_value_get(ptr, node_name, attribute_name);
48     if (attribute_value && !strcmp(attribute_value, value))
49         return 1;
50     return 0;
51 }
52
53 static void yaz_solr_decode_result_docs(ODR o, xmlNodePtr ptr, Odr_int start, Z_SRW_searchRetrieveResponse *sr) {
54     xmlNodePtr node;
55     int offset = 0;
56     int i = 0;
57
58     sr->num_records = 0;
59     for (node = ptr->children; node; node = node->next)
60         if (node->type == XML_ELEMENT_NODE)
61             sr->num_records++;
62
63     sr->records = odr_malloc(o, sizeof(*sr->records) * sr->num_records);
64
65     for (node = ptr->children; node; node = node->next)
66     {
67         if (node->type == XML_ELEMENT_NODE)
68         {
69             Z_SRW_record *record = sr->records + i;
70             xmlBufferPtr buf = xmlBufferCreate();
71             xmlNode *tmp = xmlCopyNode(node, 1);
72
73             xmlNodeDump(buf, tmp->doc, tmp, 0, 0);
74
75             xmlFreeNode(tmp);
76
77             record->recordSchema = 0;
78             record->recordPacking = Z_SRW_recordPacking_XML;
79             record->recordData_len = buf->use;
80             record->recordData_buf = odr_malloc(o, buf->use + 1);
81             memcpy(record->recordData_buf, buf->content, buf->use);
82             record->recordData_buf[buf->use] = '\0';
83             // TODO Solve the real problem in zoom-sru, that doesnt work with 0-based indexes.
84             // Work-around: Making the recordPosition 1-based.
85             record->recordPosition = odr_intdup(o, start + offset + 1);
86
87             xmlBufferFree(buf);
88
89             offset++;
90             i++;
91         }
92     }
93 }
94
95 static void yaz_solr_decode_result(ODR o, xmlNodePtr ptr, Z_SRW_searchRetrieveResponse *sr) {
96     Odr_int start = 0;
97     struct _xmlAttr *attr;
98     for (attr = ptr->properties; attr; attr = attr->next)
99         if (attr->children && attr->children->type == XML_TEXT_NODE) {
100             if (!strcmp((const char *) attr->name, "numFound")) {
101                 sr->numberOfRecords = odr_intdup(o, odr_atoi(
102                         (const char *) attr->children->content));
103             } else if (!strcmp((const char *) attr->name, "start")) {
104                 start = odr_atoi((const char *) attr->children->content);
105             }
106         }
107     yaz_solr_decode_result_docs(o, ptr, start, sr);
108 }
109
110 static Z_AttributeList *yaz_solr_use_atttribute_create(ODR o, const char *name) {
111     // TODO IMPLEMENT
112     return 0;
113 }
114
115
116 static const char *get_facet_term_count(xmlNodePtr node, int *freq) {
117     // TODO implement
118     return 0;
119 }
120
121 Z_FacetField *yaz_solr_decode_facet_field(ODR o, xmlNodePtr ptr, Z_SRW_searchRetrieveResponse *sr)
122 {
123     // USE attribute
124     const char* name = xml_node_attribute_value_get(ptr, "lst", "name");
125     Z_AttributeList *list = yaz_solr_use_atttribute_create(o, name);
126     Z_FacetField *facet_field;
127     int num_terms = 0;
128     int index = 0;
129     xmlNodePtr node;
130     for (node = ptr->children; node; node = node->next) {
131         num_terms++;
132     }
133     facet_field = facet_field_create(o, list, num_terms);
134     index = 0;
135     for (node = ptr->children; node; node = node->next) {
136         int count = 0;
137         const char *term = get_facet_term_count(node, &count);
138         facet_field_term_set(o, facet_field, facet_term_create(o, term_create(o, term), count), index);
139         index++;
140     }
141     return facet_field;
142 }
143
144 static void yaz_solr_decode_facet_counts(ODR o, xmlNodePtr root, Z_SRW_searchRetrieveResponse *sr) {
145     xmlNodePtr ptr;
146     for (ptr = root->children; ptr; ptr = ptr->next)
147     {
148         if (match_xml_node_attribute(ptr, "lst", "name", "facet_fields"))
149         {
150             xmlNodePtr node;
151             Z_FacetList *facet_list;
152             int num_facets = 0;
153             for (node = ptr->children; node; node= node->next)
154             {
155                 num_facets++;
156             }
157             facet_list = facet_list_create(o, num_facets);
158             num_facets = 0;
159             for (node = ptr->children; node; node= node->next)
160             {
161                 facet_list_field_set(o, facet_list, yaz_solr_decode_facet_field(o, node, sr), num_facets);
162                 num_facets++;
163             }
164             sr->facet_list = facet_list;
165             break;
166         }
167     }
168 }
169
170 static void yaz_solr_decode_facets(ODR o, xmlNodePtr ptr, Z_SRW_searchRetrieveResponse *sr) {
171     if (match_xml_node_attribute(ptr, "lst", "name", "facet_counts"))
172         yaz_solr_decode_facet_counts(o, ptr->children, sr);
173 }
174 #endif
175
176 int yaz_solr_decode_response(ODR o, Z_HTTP_Response *hres, Z_SRW_PDU **pdup)
177 {
178 #if YAZ_HAVE_XML2
179     const char *content_buf = hres->content_buf;
180     int content_len = hres->content_len;
181     xmlDocPtr doc = xmlParseMemory(content_buf, content_len);
182     int ret = 0;
183     xmlNodePtr ptr = 0;
184     Z_SRW_PDU *pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
185     Z_SRW_searchRetrieveResponse *sr = pdu->u.response;
186
187     if (!doc)
188     {
189         ret = -1;
190     }
191     if (doc)
192     {
193         xmlNodePtr root = xmlDocGetRootElement(doc);
194         if (!root)
195         {
196             ret = -1;
197         }
198         else if (strcmp((const char *) root->name, "response"))
199         {
200             ret = -1;
201         }
202         else
203         {
204             /** look for result node */
205             for (ptr = root->children; ptr; ptr = ptr->next)
206             {
207                 if (ptr->type == XML_ELEMENT_NODE &&
208                     !strcmp((const char *) ptr->name, "result"))
209                         yaz_solr_decode_result(o, ptr, sr);
210                 if (match_xml_node_attribute(ptr, "lst", "name", "facet_counts"))
211                         yaz_solr_decode_facet_counts(o, ptr, sr);
212             }
213             if (!ptr)
214             {
215                 ret = -1;
216             }
217         }
218     }
219     if (doc)
220         xmlFreeDoc(doc);
221     if (ret == 0)
222         *pdup = pdu;
223     return ret;
224 #else
225     return -1;
226 #endif
227 }
228
229 void solr_encode_facet_field(ODR encode, Z_FacetField *facet_field) {
230
231 }
232
233 int yaz_solr_encode_request(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
234                             ODR encode, const char *charset)
235 {
236     const char *solr_op = 0;
237     //TODO Change. not a nice hard coded, unchecked limit.
238     int max = 100;
239     char *name[100], *value[100];
240     char *uri_args;
241     char *path;
242     int i = 0;
243
244     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
245                                  srw_pdu->username, srw_pdu->password);
246
247     switch (srw_pdu->which)
248     {
249     case Z_SRW_searchRetrieve_request: {
250         Z_SRW_searchRetrieveRequest *request = srw_pdu->u.request;
251         solr_op = "select";
252         switch(srw_pdu->u.request->query_type)
253         {
254         case Z_SRW_query_type_pqf:
255             yaz_add_name_value_str(encode, name, value, &i,
256                                    "q", request->query.pqf);
257             break;
258         case Z_SRW_query_type_cql:
259             yaz_add_name_value_str(encode, name, value, &i,
260                                    "q", request->query.cql);
261             break;
262         default:
263             return -1;
264         }
265         if (srw_pdu->u.request->startRecord)
266         {
267             Odr_int start = *request->startRecord - 1;
268             yaz_add_name_value_int(encode, name, value, &i,
269                                    "start", &start);
270         }
271         yaz_add_name_value_int(encode, name, value, &i,
272                                "rows", request->maximumRecords);
273         yaz_add_name_value_str(encode, name, value, &i,
274                                "fl", request->recordSchema);
275
276         if (request->facet_list) {
277             int index;
278             Z_FacetList *facet_list = request->facet_list;
279             yaz_add_name_value_str(encode, name, value, &i, "facet", "true");
280             for (index = 0; index < facet_list->num; index++) {
281                 //TODO impl
282                 //solr_encode_facet_field(encode, name, value, &i, facet_list->elements[index]);
283             }
284         }
285         break;
286     }
287     default:
288         return -1;
289     }
290     name[i] = 0;
291     yaz_array_to_uri(&uri_args, encode, name, value);
292     
293     hreq->method = "GET";
294     
295     path = (char *)
296         odr_malloc(encode, strlen(hreq->path) +
297                    strlen(uri_args) + strlen(solr_op) + 4);
298
299     sprintf(path, "%s/%s?%s", hreq->path, solr_op, uri_args);
300     hreq->path = path;
301
302     z_HTTP_header_add_content_type(encode, &hreq->headers,
303                                    "text/xml", charset);
304     return 0;
305 }
306
307
308 /*
309  * Local variables:
310  * c-basic-offset: 4
311  * c-file-style: "Stroustrup"
312  * indent-tabs-mode: nil
313  * End:
314  * vim: shiftwidth=4 tabstop=8 expandtab
315  */
316