source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractGS2FieldSearch.java@ 25852

Last change on this file since 25852 was 25850, checked in by sjm84, 12 years ago

Advanced search and Form search now do paging. Also add facets to the results if they were used.

  • Property svn:keywords set to Author Date Id Revision
File size: 27.4 KB
Line 
1/*
2 * AbstractGS2FieldSearch.java
3 * Copyright (C) 2006 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18package org.greenstone.gsdl3.service;
19
20// Greenstone classes
21import org.greenstone.gsdl3.util.*;
22
23// XML classes
24import org.w3c.dom.Document;
25import org.w3c.dom.Element;
26import org.w3c.dom.NodeList;
27
28// java classes
29import java.util.Iterator;
30import java.util.Set;
31import java.util.HashMap;
32import java.util.Map;
33import java.util.ArrayList;
34import java.util.Vector;
35import java.io.File;
36import java.io.Serializable;
37
38import org.apache.log4j.*;
39import org.apache.solr.client.solrj.response.FacetField;
40
41abstract public class AbstractGS2FieldSearch extends AbstractGS2TextSearch
42{
43 // extra services offered by mgpp collections
44 protected static final String FIELD_QUERY_SERVICE = "FieldQuery";
45 protected static final String ADVANCED_FIELD_QUERY_SERVICE = "AdvancedFieldQuery";
46 protected static final String RAW_QUERY_SERVICE = "RawQuery";
47
48 // extra parameters used
49 protected static final String LEVEL_PARAM = "level";
50 protected static final String RANK_PARAM = "sortBy";
51 protected static final String RANK_PARAM_RANK = "1";
52 protected static final String RANK_PARAM_NONE = "0";
53 protected static final String SIMPLE_FIELD_PARAM = "simpleField";
54 protected static final String ADVANCED_FIELD_PARAM = "complexField";
55
56 protected static final String RAW_PARAM = "rawquery";
57
58 // more params for field query
59 protected static final String FIELD_QUERY_PARAM = "fqv";
60 protected static final String FIELD_STEM_PARAM = "fqs";
61 protected static final String FIELD_CASE_PARAM = "fqc";
62 protected static final String FIELD_ACCENT_PARAM = "fqa";
63 protected static final String FIELD_FIELD_PARAM = "fqf";
64 protected static final String FIELD_COMBINE_PARAM = "fqk";
65 protected static final String FIELD_COMBINE_PARAM_AND = "0";
66 protected static final String FIELD_COMBINE_PARAM_OR = "1";
67 protected static final String FIELD_COMBINE_PARAM_NOT = "2";
68
69 // some stuff for config files
70 protected static final String SEARCH_TYPE_ELEM = "searchType";
71 protected static final String SEARCH_TYPE_PLAIN = "plain";
72 protected static final String SEARCH_TYPE_SIMPLE_FORM = "simpleform";
73 protected static final String SEARCH_TYPE_ADVANCED_FORM = "advancedform";
74 protected static final String SEARCH_TYPE_RAW = "raw";
75
76 protected static final String DEFAULT_LEVEL_ELEM = "defaultLevel";
77 protected static final String DEFAULT_DB_LEVEL_ELEM = "defaultDBLevel";
78 protected static final String LEVEL_ELEM = "level";
79 protected static final String FIELD_ATT = "field";
80
81 protected static final int TEXT_QUERY = 0;
82 protected static final int SIMPLE_QUERY = 1;
83 protected static final int ADVANCED_QUERY = 2;
84 protected static final int RAW_QUERY = 3;
85
86 protected String AND_OPERATOR = "&";
87 protected String OR_OPERATOR = "|";
88 protected String NOT_OPERATOR = "!";
89
90 // the default level for searching
91 protected String default_level = null;
92 // default level for collection db
93 protected String default_db_level = null;
94 // which search services will we offer??
95 protected boolean plain_search = false;
96 protected boolean simple_form_search = false;
97 protected boolean advanced_form_search = false;
98 protected boolean raw_search = false;
99
100 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractGS2FieldSearch.class.getName());
101
102 /** constructor */
103 public AbstractGS2FieldSearch()
104 {
105 }
106
107 public void cleanUp()
108 {
109 super.cleanUp();
110 }
111
112 /** configure this service */
113 public boolean configure(Element info, Element extra_info)
114 {
115 if (!super.configure(info, extra_info))
116 {
117 return false;
118 }
119
120 // Get the default level out of <defaultLevel> (buildConfig.xml)
121 Element def = (Element) GSXML.getChildByTagName(info, DEFAULT_LEVEL_ELEM);
122 if (def != null)
123 {
124 this.default_level = def.getAttribute(GSXML.SHORTNAME_ATT);
125 }
126 if (this.default_level == null || this.default_level.equals(""))
127 {
128 logger.error("default level not specified!, assuming Doc");
129 this.default_level = "Doc";
130 }
131
132 // Get the default DB level
133 def = (Element) GSXML.getChildByTagName(info, DEFAULT_DB_LEVEL_ELEM);
134 if (def != null)
135 {
136 this.default_db_level = def.getAttribute(GSXML.SHORTNAME_ATT);
137 }
138 if (this.default_db_level == null || this.default_db_level.equals(""))
139 {
140 logger.error("default database level (defaultDBLevel) not specified!, assuming Sec");
141 this.default_db_level = "Sec";
142 }
143
144 // get stuff from from extra info (which is collectionConfig.xml)
145 if (extra_info != null)
146 {
147
148 // the search element
149 Element config_search = (Element) GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
150
151 NodeList search_types = config_search.getElementsByTagName(SEARCH_TYPE_ELEM);
152 if (search_types == null)
153 {
154 // none specified, assume plain only
155 this.plain_search = true;
156 }
157 else
158 {
159 for (int i = 0; i < search_types.getLength(); i++)
160 {
161 Element t = (Element) search_types.item(i);
162
163 String type_name = t.getAttribute(GSXML.NAME_ATT);
164 if (type_name.equals(SEARCH_TYPE_PLAIN))
165 {
166 this.plain_search = true;
167 }
168 else if (type_name.equals(SEARCH_TYPE_SIMPLE_FORM))
169 {
170 this.simple_form_search = true;
171 }
172 else if (type_name.equals(SEARCH_TYPE_ADVANCED_FORM))
173 {
174 this.advanced_form_search = true;
175 }
176 else if (type_name.equals(SEARCH_TYPE_RAW))
177 {
178 this.raw_search = true;
179 }
180 }
181 }
182
183 // AbstractGS2TextSearch has set up the TextQuery service, but we may not want it
184 if (!this.plain_search)
185 {
186 // need to remove the TextQuery service
187 Element tq_service = GSXML.getNamedElement(short_service_info, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, QUERY_SERVICE);
188 short_service_info.removeChild(tq_service);
189 }
190
191 Document owner = info.getOwnerDocument();
192
193 NodeList levels = info.getElementsByTagName(GSXML.LEVEL_ELEM);
194
195 for (int i = 0; i < levels.getLength(); i++)
196 {
197 Element lev = (Element) levels.item(i);
198 String name = lev.getAttribute(GSXML.NAME_ATT);
199 Element node_extra = GSXML.getNamedElement(config_search, GSXML.LEVEL_ELEM, GSXML.NAME_ATT, name);
200 if (node_extra == null)
201 {
202 logger.error("haven't found extra info for level named " + name);
203 continue;
204 }
205
206 // get the display elements if any - displayName
207 NodeList display_names = node_extra.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
208 if (display_names != null)
209 {
210 for (int j = 0; j < display_names.getLength(); j++)
211 {
212 Element e = (Element) display_names.item(j);
213 lev.appendChild(owner.importNode(e, true));
214 }
215 }
216 } // for each level
217 }
218 else
219 {
220 // for soem reason we don't have the collectionConfig file. assume plain only
221 this.plain_search = true;
222 }
223
224 // the format info is the same for all services
225 Element format_info = (Element) format_info_map.get(QUERY_SERVICE);
226
227 // set up the extra services which are available for this collection
228 if (this.simple_form_search)
229 {
230 // set up short_service_info_ - for now just has id and type - name will be added in on the fly
231 Element fq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
232 fq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
233 fq_service.setAttribute(GSXML.NAME_ATT, FIELD_QUERY_SERVICE);
234 this.short_service_info.appendChild(fq_service);
235
236 if (format_info != null)
237 {
238 this.format_info_map.put(FIELD_QUERY_SERVICE, format_info);
239 }
240 }
241
242 if (this.advanced_form_search)
243 {
244 Element afq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
245 afq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
246 afq_service.setAttribute(GSXML.NAME_ATT, ADVANCED_FIELD_QUERY_SERVICE);
247 this.short_service_info.appendChild(afq_service);
248
249 if (format_info != null)
250 {
251 this.format_info_map.put(ADVANCED_FIELD_QUERY_SERVICE, format_info);
252 }
253 }
254
255 if (this.raw_search)
256 {
257 Element rq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
258 rq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
259 rq_service.setAttribute(GSXML.NAME_ATT, RAW_QUERY_SERVICE);
260 this.short_service_info.appendChild(rq_service);
261
262 if (format_info != null)
263 {
264 this.format_info_map.put(RAW_QUERY_SERVICE, format_info);
265 }
266 }
267
268 return true;
269 }
270
271 protected Element getServiceDescription(String service_id, String lang, String subset)
272 {
273 // should we check that the service is actually on offer? presumably we wont get asked for services that we haven't advertised previously.
274
275 if (!service_id.equals(FIELD_QUERY_SERVICE) && !service_id.equals(ADVANCED_FIELD_QUERY_SERVICE) && !service_id.equals(RAW_QUERY_SERVICE))
276 {
277 return super.getServiceDescription(service_id, lang, subset);
278 }
279
280 Element service = this.doc.createElement(GSXML.SERVICE_ELEM);
281 service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
282 service.setAttribute(GSXML.NAME_ATT, service_id);
283 if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
284 {
285 service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service_id + ".name", lang)));
286 service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service_id + ".submit", lang)));
287 service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service_id + ".description", lang)));
288
289 }
290 if (subset == null || subset.equals(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER))
291 {
292 Element param_list = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
293 service.appendChild(param_list);
294 if (service_id.equals(FIELD_QUERY_SERVICE))
295 {
296
297 addCustomQueryParams(param_list, lang);
298 createParameter(MAXDOCS_PARAM, param_list, lang);
299 if (!default_index_subcollection.equals(""))
300 {
301 createParameter(INDEX_SUBCOLLECTION_PARAM, param_list, lang);
302 }
303 if (!default_index_language.equals(""))
304 {
305 createParameter(INDEX_LANGUAGE_PARAM, param_list, lang);
306 }
307
308 if (does_paging)
309 {
310 createParameter(HITS_PER_PAGE_PARAM, param_list, lang);
311 createParameter(START_PAGE_PARAM, param_list, lang);
312 }
313
314 // create a multi param for the fields etc
315 // text box, field
316 Element multiparam = null;
317 Element param = null;
318 multiparam = GSXML.createParameterDescription(this.doc, SIMPLE_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
319 multiparam.setAttribute("occurs", "4");
320 param_list.appendChild(multiparam);
321
322 // the components
323
324 createParameter(FIELD_QUERY_PARAM, multiparam, lang);
325 createParameter(FIELD_FIELD_PARAM, multiparam, lang);
326
327 }
328 else if (service_id.equals(ADVANCED_FIELD_QUERY_SERVICE))
329 {
330 createParameter(LEVEL_PARAM, param_list, lang);
331 createParameter(RANK_PARAM, param_list, lang);
332 createParameter(MAXDOCS_PARAM, param_list, lang);
333 if (!default_index_subcollection.equals(""))
334 {
335 createParameter(INDEX_SUBCOLLECTION_PARAM, param_list, lang);
336 }
337 if (!default_index_language.equals(""))
338 {
339 createParameter(INDEX_LANGUAGE_PARAM, param_list, lang);
340 }
341
342 // create a multi param for the fields etc
343 // text box, stem, case, field
344
345 Element multiparam = null;
346 Element param = null;
347
348 multiparam = GSXML.createParameterDescription(this.doc, ADVANCED_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
349 multiparam.setAttribute("occurs", "4");
350 param_list.appendChild(multiparam);
351
352 createParameter(FIELD_COMBINE_PARAM, multiparam, lang);
353 createParameter(FIELD_QUERY_PARAM, multiparam, lang);
354 if (this.does_case)
355 {
356 createParameter(FIELD_CASE_PARAM, multiparam, lang);
357 }
358 if (this.does_stem)
359 {
360 createParameter(FIELD_STEM_PARAM, multiparam, lang);
361 }
362 if (this.does_accent)
363 {
364 createParameter(FIELD_ACCENT_PARAM, multiparam, lang);
365 }
366 if (does_paging)
367 {
368 createParameter(HITS_PER_PAGE_PARAM, param_list, lang);
369 createParameter(START_PAGE_PARAM, param_list, lang);
370 }
371 createParameter(FIELD_FIELD_PARAM, multiparam, lang);
372
373 }
374 else if (service_id.equals(RAW_QUERY_SERVICE))
375 {
376 if (does_chunking)
377 {
378 createParameter(MAXDOCS_PARAM, param_list, lang);
379 }
380 if (does_paging)
381 {
382 createParameter(HITS_PER_PAGE_PARAM, param_list, lang);
383 createParameter(START_PAGE_PARAM, param_list, lang);
384 }
385 createParameter(RAW_PARAM, param_list, lang);
386 service.appendChild(param_list);
387 }
388 }
389 return service;
390
391 }
392
393 /** add in the level params to TextQuery */
394 protected void addCustomQueryParams(Element param_list, String lang)
395 {
396 createParameter(LEVEL_PARAM, param_list, lang);
397 super.addCustomQueryParams(param_list, lang);
398 }
399
400 /** create a param and add to the list */
401 protected void createParameter(String name, Element param_list, String lang)
402 {
403 Element param = null;
404 if (name.equals(LEVEL_PARAM))
405 {
406 ArrayList<String> level_ids = new ArrayList<String>();
407 ArrayList<String> level_names = new ArrayList<String>();
408 getLevelData(level_ids, level_names, lang);
409 if (level_ids.size() > 1)
410 {
411 // the first one is the default
412 //param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param."+LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)level_ids.get(0), level_ids, level_names);
413 param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param." + LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, this.default_level, level_ids, level_names);
414 }
415 else
416 {
417 // we need to set the level, but hidden, in case there is an invalid level saved
418 //param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, (String)level_ids.get(0), null, null);
419 param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, this.default_level, null, null);
420 }
421 }
422 else if (name.equals(RANK_PARAM))
423 {
424 String[] vals1 = { RANK_PARAM_RANK, RANK_PARAM_NONE };
425 String[] vals1_texts = { getTextString("param." + RANK_PARAM + "." + RANK_PARAM_RANK, lang), getTextString("param." + RANK_PARAM + "." + RANK_PARAM_NONE, lang) };
426
427 param = GSXML.createParameterDescription(this.doc, RANK_PARAM, getTextString("param." + RANK_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, RANK_PARAM_RANK, vals1, vals1_texts);
428
429 }
430 else if (name.equals(FIELD_QUERY_PARAM))
431 {
432 param = GSXML.createParameterDescription(this.doc, FIELD_QUERY_PARAM, getTextString("param." + FIELD_QUERY_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
433
434 }
435 else if (name.equals(FIELD_CASE_PARAM) || name.equals(FIELD_STEM_PARAM) || name.equals(FIELD_ACCENT_PARAM))
436 {
437 String[] bool_ops = { "0", "1" };
438 String[] bool_texts = { getTextString("param.boolean.off", lang, "AbstractTextSearch"), getTextString("param.boolean.on", lang, "AbstractTextSearch") };
439 param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_BOOLEAN, BOOLEAN_PARAM_ON, bool_ops, bool_texts);
440
441 }
442 else if (name.equals(FIELD_FIELD_PARAM))
443 {
444 ArrayList<String> fields = new ArrayList<String>();
445 ArrayList<String> field_names = new ArrayList<String>();
446 getIndexData(fields, field_names, lang);
447 // the field list - read from config file
448
449 // Fix for http://trac.greenstone.org/ticket/245 "java crash, index out of bounds"
450 // org.greenstone.gsdl3.service.AbstractGS2FieldSearch.createParameter(AbstractGS2FieldSearch.java:362)
451 // Changed from:
452 // param = GSXML.createParameterDescription2(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)fields.get(0), fields, field_names );
453 String default_value = (fields.size() > 0) ? fields.get(0) : null;
454 // don't want to access element 0 if fields.size()==0, and
455 // GSXML.createParameterDescription2 checks for default_value==null condition
456 param = GSXML.createParameterDescription2(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, default_value, fields, field_names);
457
458 }
459 else if (name.equals(FIELD_COMBINE_PARAM))
460 {
461
462 String[] vals = { FIELD_COMBINE_PARAM_AND, FIELD_COMBINE_PARAM_OR, FIELD_COMBINE_PARAM_NOT };
463 String[] val_texts = { getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_AND, lang), getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_OR, lang), getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_NOT, lang) };
464
465 param = GSXML.createParameterDescription(this.doc, FIELD_COMBINE_PARAM, "", GSXML.PARAM_TYPE_ENUM_SINGLE, FIELD_COMBINE_PARAM_AND, vals, val_texts);
466 param.setAttribute(GSXML.PARAM_IGNORE_POS_ATT, "0");
467 }
468
469 if (param != null)
470 {
471 param_list.appendChild(param);
472 }
473 else
474 {
475 super.createParameter(name, param_list, lang);
476 }
477 }
478
479 // should cache some of this
480 protected void getLevelData(ArrayList<String> level_ids, ArrayList<String> level_names, String lang)
481 {
482 Element level_list = (Element) GSXML.getChildByTagName(this.config_info, LEVEL_ELEM + GSXML.LIST_MODIFIER);
483 NodeList levels = level_list.getElementsByTagName(LEVEL_ELEM);
484 for (int i = 0; i < levels.getLength(); i++)
485 {
486 Element level = (Element) levels.item(i);
487 String shortname = level.getAttribute(GSXML.SHORTNAME_ATT);
488 if (shortname.equals(""))
489 {
490 continue;
491 }
492 level_ids.add(shortname);
493 String display_name = GSXML.getDisplayText(level, GSXML.DISPLAY_TEXT_NAME, lang, "en");
494 if (display_name.equals(""))
495 {
496 // we'll use the name, and the dictionary
497 display_name = level.getAttribute(GSXML.NAME_ATT);
498 if (display_name.equals(""))
499 {
500 display_name = shortname;
501 }
502 else
503 {
504 display_name = getTextString("level." + display_name, lang);
505 }
506 }
507 level_names.add(display_name);
508 }
509 }
510
511 // the following three functions are needed so the base class can
512 // call the process+SERVICE_NAME methods
513 /** process a text query */
514 protected Element processTextQuery(Element request)
515 {
516 return processAnyQuery(request, TEXT_QUERY);
517 }
518
519 /** process a field query */
520 protected Element processFieldQuery(Element request)
521 {
522 return processAnyQuery(request, SIMPLE_QUERY);
523 }
524
525 /** process an advanced field query */
526 protected Element processAdvancedFieldQuery(Element request)
527 {
528 return processAnyQuery(request, ADVANCED_QUERY);
529 }
530
531 protected Element processRawQuery(Element request)
532 {
533 return processAnyQuery(request, RAW_QUERY);
534 }
535
536 /** process a query */
537 protected Element processAnyQuery(Element request, int query_type)
538 {
539 String service_name = null;
540 String empty_query_test_param = null;
541 // set up the type specific bits
542 switch (query_type)
543 {
544 case TEXT_QUERY:
545 service_name = QUERY_SERVICE;
546 empty_query_test_param = QUERY_PARAM;
547 break;
548 case SIMPLE_QUERY:
549 service_name = FIELD_QUERY_SERVICE;
550 empty_query_test_param = FIELD_QUERY_PARAM;
551 break;
552 case ADVANCED_QUERY:
553 service_name = ADVANCED_FIELD_QUERY_SERVICE;
554 empty_query_test_param = FIELD_QUERY_PARAM;
555 break;
556 case RAW_QUERY:
557 service_name = RAW_QUERY_SERVICE;
558 empty_query_test_param = RAW_PARAM;
559 break;
560 default:
561 // should never get here
562 logger.error("wrong query type!!");
563 return null;
564 }
565
566 // Create a new (empty) result message
567 Element result = this.doc.createElement(GSXML.RESPONSE_ELEM);
568 result.setAttribute(GSXML.FROM_ATT, service_name);
569 result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
570
571 // Get the parameters of the request
572 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
573 if (param_list == null)
574 {
575 logger.error("TextQuery request had no paramList.");
576 return result; // Return the empty result
577 }
578
579 // Process the request parameters
580 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
581
582 // Make sure a query has been specified
583 String query = (String) params.get(empty_query_test_param);
584 if (query == null || query.equals(""))
585 {
586 return result; // Return the empty result
587 }
588
589 // If a field hasn't been specified, use the default - for textQuery
590 String field = (String) params.get(INDEX_PARAM);
591 if (field == null)
592 {
593 field = default_index;
594 }
595
596 // set up the appropriate query system
597 if (!setUpQueryer(params))
598 {
599 return result;
600 }
601
602 // if field search, create the query string
603 switch (query_type)
604 {
605 case TEXT_QUERY:
606 query = addFieldInfo(query, field);
607 break;
608 case SIMPLE_QUERY:
609 query = parseFieldQueryParams(params);
610 break;
611 case ADVANCED_QUERY:
612 query = parseAdvancedFieldQueryParams(params);
613 break;
614 }
615
616 // run the query
617 Object query_result = runQuery(query);
618
619 // build up the response
620
621 // Create a metadata list to store information about the query results
622 // should we be using metadataList? or something else?
623 Element metadata_list = this.doc.createElement(GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
624 result.appendChild(metadata_list);
625
626 // Add a metadata element specifying the number of matching documents
627 long totalDocs = numDocsMatched(query_result);
628
629 GSXML.addMetadata(this.doc, metadata_list, "numDocsMatched", "" + totalDocs);
630
631 // Create a document list to store the matching documents, and add them
632 String[] docs = getDocIDs(query_result);
633 String[] doc_ranks = getDocRanks(query_result);
634
635 // add a metadata item to specify docs returned
636 int docs_returned = docs.length;
637 if (does_paging)
638 {
639 String maxdocs_str = (String) params.get(MAXDOCS_PARAM);
640 if (maxdocs_str != null)
641 {
642 int maxdocs = Integer.parseInt(maxdocs_str);
643 docs_returned = (maxdocs < (int) totalDocs ? maxdocs : (int) totalDocs);
644 }
645 }
646 GSXML.addMetadata(this.doc, metadata_list, "numDocsReturned", "" + docs_returned);
647
648 // add a metadata item to specify what actual query was done - eg if stuff was stripped out etc. and then we can use the query later, cos we don't know which parameter was the query
649 GSXML.addMetadata(this.doc, metadata_list, "query", query);
650 if (docs.length > 0)
651 {
652 Element document_list = this.doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
653 result.appendChild(document_list);
654 for (int d = 0; d < docs.length; d++)
655 {
656 String doc_id = internalNum2OID(docs[d]);
657 Element doc_node = createDocNode(doc_id, doc_ranks[d]);
658 document_list.appendChild(doc_node);
659 }
660 }
661
662 // Create a term list to store the term information, and add it
663 Element term_list = this.doc.createElement(GSXML.TERM_ELEM + GSXML.LIST_MODIFIER);
664 result.appendChild(term_list);
665 addTermInfo(term_list, params, query_result);
666
667 if (query_result instanceof SolrQueryResult)
668 {
669 Element facet_list = this.doc.createElement(GSXML.FACET_ELEM + GSXML.LIST_MODIFIER);
670 result.appendChild(facet_list);
671
672 if(((SolrQueryResult) query_result).getFacetResults() != null)
673 {
674 for (FacetField facet : ((SolrQueryResult) query_result).getFacetResults())
675 {
676 Element facet_elem = this.doc.createElement(GSXML.FACET_ELEM);
677 facet_elem.setAttribute(GSXML.NAME_ATT, facet.getName());
678
679 for (FacetField.Count count : facet.getValues())
680 {
681 if (count.getCount() > 0)
682 {
683 Element count_elem = this.doc.createElement(GSXML.COUNT_ELEM);
684 count_elem.setAttribute(GSXML.NAME_ATT, count.getName());
685 count_elem.setTextContent("" + count.getCount());
686
687 facet_elem.appendChild(count_elem);
688 }
689 }
690 facet_list.appendChild(facet_elem);
691 }
692 }
693 }
694
695 return result;
696
697 }
698
699 /** methods to handle actually doing the query */
700 /** do any initialisation of the query object */
701 abstract protected boolean setUpQueryer(HashMap<String, Serializable> params);
702
703 /** do the query */
704 abstract protected Object runQuery(String query);
705
706 /** get the total number of docs that match */
707 abstract protected long numDocsMatched(Object query_result);
708
709 /** get the list of doc ids */
710 abstract protected String[] getDocIDs(Object query_result);
711
712 /** get the list of doc ranks */
713 abstract protected String[] getDocRanks(Object query_result);
714
715 /** add in term info if available */
716 abstract protected boolean addTermInfo(Element term_list, HashMap<String, Serializable> params, Object query_result);
717
718 /**
719 * combines all the field params into a single query - for simple field
720 * query
721 */
722 /** We assume the combination (AND/OR) is done by the match param */
723 protected String parseFieldQueryParams(HashMap<String, Serializable> params)
724 {
725
726 StringBuffer final_query = new StringBuffer(256);
727 String text_line = (String) params.get(FIELD_QUERY_PARAM);
728 String[] texts = text_line.split(",", -1);
729 String field_line = (String) params.get(FIELD_FIELD_PARAM);
730 String[] fields = field_line.split(",", -1);
731
732 for (int i = 0; i < texts.length; i++)
733 {
734 String q = texts[i].trim();
735 if (!q.equals(""))
736 {
737 final_query.append(" " + addFieldInfo(q, fields[i]));
738 }
739 }
740
741 return final_query.toString();
742 }
743
744 abstract protected String addFieldInfo(String query, String field);
745
746 /**
747 * combines all the field params into a single query - for advanced field
748 * query
749 */
750 protected String parseAdvancedFieldQueryParams(HashMap<String, Serializable> params)
751 {
752
753 StringBuffer final_query = new StringBuffer(256);
754 String text_line = (String) params.get(FIELD_QUERY_PARAM);
755 String[] texts = text_line.split(",", -1);
756 String field_line = (String) params.get(FIELD_FIELD_PARAM);
757 String[] fields = field_line.split(",", -1);
758 String[] cases = null;
759 String[] stems = null;
760 String[] accents = null;
761 if (does_case)
762 {
763 String case_line = (String) params.get(FIELD_CASE_PARAM);
764 if (case_line != null)
765 cases = case_line.split(",", -1);
766 }
767 if (does_stem)
768 {
769 String stem_line = (String) params.get(FIELD_STEM_PARAM);
770 if (stem_line != null)
771 stems = stem_line.split(",", -1);
772 }
773 if (does_accent)
774 {
775 String accent_line = (String) params.get(FIELD_ACCENT_PARAM);
776 if (accent_line != null)
777 accents = accent_line.split(",", -1);
778 }
779 String combine_line = (String) params.get(FIELD_COMBINE_PARAM);
780 String[] combines = combine_line.split(",", -1);
781 String combine = "";
782 for (int i = 0; i < texts.length; i++)
783 {
784 if (i == 0)
785 {// assume first one is blank
786 combine = "";
787 }
788 else
789 {
790 String x = combines[i];
791 if (x.equals(FIELD_COMBINE_PARAM_AND))
792 {
793 combine = AND_OPERATOR;
794 }
795 else if (x.equals(FIELD_COMBINE_PARAM_OR))
796 {
797 combine = OR_OPERATOR;
798 }
799 else if (x.equals(FIELD_COMBINE_PARAM_NOT))
800 {
801 combine = NOT_OPERATOR;
802 }
803
804 }
805
806 String q = texts[i].trim();
807 boolean modified = false;
808 if (!q.equals(""))
809 {
810 String c = null;
811 String s = null;
812 String a = null;
813 if (does_case)
814 {
815 modified = true;
816 c = cases[i];
817 }
818 if (does_stem)
819 {
820 modified = true;
821 s = stems[i];
822 }
823 if (does_accent)
824 {
825 modified = true;
826 a = accents[i];
827 }
828 if (modified)
829 {
830 q = addStemOptions(q, s, c, a);
831 }
832 addQueryElem(final_query, q, fields[i], combine);
833 }
834 }
835 return final_query.toString();
836 }
837
838 abstract protected void addQueryElem(StringBuffer final_query, String query, String field, String combine);
839
840 abstract protected String addStemOptions(String query, String stem, String casef, String accent);
841
842}
Note: See TracBrowser for help on using the repository browser.