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

Last change on this file since 32619 was 32619, checked in by ak19, 5 years ago

3 significant changes in 1 commit particularly impacting Lucene queries: 1. Instead if GS2LuceneSearch havinga GS2LuceneQuery object member variable for doing each and every search, each query now instantiates its own local GS2LuceneQuery object, configures it for that specific search, runs the search and then the GS2LuceneQuery object expires. This fixes a bug by preventing multiple concurrent searches getting the search configurations of other searches run at the same time. 2. Though GS2LuceneQuery objects need to be instantiated 1 per query over a collection, we don't want to keep reopening a collection's sidx and didx index folders with IndexReader objects for every query. Since IndexReaders support concurrent access, we'd like to use one IndexReader per collection index (one for didx, one for sidx) with the IndexReaders existing for the life of a collection. This meant moving the maintaining of IndexReader objects from GS2LuceneQuery into the GS2LuceneSearch service and turning them into singletons by using a HashMap to maintain index-dir, reader pairs. GS3 Services, e.g. GS2LuceneSearch, are loaded and unloaded on collection activate and deactivate respectively. On deactivate, cleanUp() is called on services and other GS3 modules. When GS2LuceneSearch.cleanUp() is called, we now finally close the singleton IndexReader objects/resources that a collection's GS2LuceneSearch object maintains. 3. Redid previous bugfix (then committed to GS2LuceneQuery): Point 2 again solves the filelocking problem of multiple handles to the index being opened and not all being closed on deactivate, but it's solved in a different and better/more optimal way than in the previous commit.

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