source: trunk/gsdl3/src/java/org/greenstone/gsdl3/service/AbstractSearch.java@ 9435

Last change on this file since 9435 was 9435, checked in by kjdon, 19 years ago

if we have found some format in the info element, don't look for it in extra_info elem. this way you can put some format stuff in buildConfig for other search services, otherwise they are all trying to use the same format

  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/*
2 * AbstractSearch.java
3 * Copyright (C) 2005 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.service;
20
21// Greenstone classes
22import org.greenstone.gsdl3.util.GSXML;
23import org.greenstone.gsdl3.util.GSPath;
24
25// XML classes
26import org.w3c.dom.Document;
27import org.w3c.dom.Element;
28import org.w3c.dom.NodeList;
29
30// java classes
31import java.util.ArrayList;
32import java.util.HashMap;
33
34/** Partially implements a generic search service
35 *
36 * @author <a href="mailto:[email protected]">Katherine Don</a>
37 */
38
39public abstract class AbstractSearch
40 extends ServiceRack
41{
42
43 // the search service
44 protected static final String TEXT_QUERY_SERVICE = "TextQuery";
45
46 // compulsory params
47 protected static final String INDEX_PARAM = "index";
48 protected static final String QUERY_PARAM = "query";
49
50 // optional standard params - some of these have to be implemented
51 protected static final String MAXDOCS_PARAM = "maxDocs";
52 protected static final String HITS_PER_PAGE_PARAM = "hitsPerPage";
53 protected static final String START_PAGE_PARAM = "startPage";
54
55 // some other common params that may be used
56 protected static final String CASE_PARAM = "case";
57 protected static final String STEM_PARAM = "stem";
58 protected static final String BOOLEAN_PARAM_ON = "1";
59 protected static final String BOOLEAN_PARAM_OFF = "0";
60 protected static final String MATCH_PARAM = "matchMode";
61 protected static final String MATCH_PARAM_ALL = "all";
62 protected static final String MATCH_PARAM_SOME = "some";
63
64 /** can more than one index be searched at the smae time? */
65 protected boolean does_multi_index_search = false;
66 /** does this service support paging of results? */
67 protected boolean does_paging = false;
68 /** does this service support asking for a subset of results? */
69 protected boolean does_chunking = false;
70 /** the default document type - use if all documents are the same type
71 */
72 protected String default_document_type = null;
73
74 public AbstractSearch()
75 {
76 }
77
78 /** sets up the short service info for TextQuery. If other services
79 * will be provided, should be added in the subclass configure
80 * also looks for search format info, and document format info
81 */
82 public boolean configure(Element info, Element extra_info)
83 {
84 System.out.println("Configuring AbstractSearch...");
85
86 this.config_info = info;
87
88 // set up short_service_info_ - for now just has id and type. the name (lang dependent) will be added in if the list is requested.
89 Element tq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
90 tq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
91 tq_service.setAttribute(GSXML.NAME_ATT, TEXT_QUERY_SERVICE);
92 this.short_service_info.appendChild(tq_service);
93
94 // add some format info to service map if there is any - look in extra info
95 // first look in buildConfig
96 Element format = (Element)GSXML.getChildByTagName(info, GSXML.FORMAT_ELEM);
97
98 if (format==null) {
99 String path = GSPath.appendLink(GSXML.SEARCH_ELEM, GSXML.FORMAT_ELEM);
100 format = (Element) GSXML.getNodeByPath(extra_info, path);
101 }
102 //Element format = (Element) GSXML.getChildByTagName(info, GSXML.FORMAT_ELEM);
103 if (format != null) {
104 this.format_info_map.put(TEXT_QUERY_SERVICE, this.doc.importNode(format, true));
105 }
106
107 // look for document display format - for documentType
108 String path = GSPath.appendLink(GSXML.DISPLAY_ELEM, GSXML.FORMAT_ELEM);
109 Element display_format = (Element)GSXML.getNodeByPath(extra_info, path);
110 if (display_format != null) {
111 // check for docType option.
112 Element doc_type_opt = GSXML.getNamedElement(display_format, "gsf:option", GSXML.NAME_ATT, "documentType");
113 if (doc_type_opt != null) {
114 String value = doc_type_opt.getAttribute(GSXML.VALUE_ATT);
115 if (!value.equals("")) {
116 this.default_document_type = value;
117 }
118 }
119 }
120
121 return true;
122 }
123
124 /** returns the description of the TextQuery service. If a subclass
125 * provides other services they need to provides their own descriptions */
126 protected Element getServiceDescription(String service, String lang, String subset)
127 {
128 if (!service.equals(TEXT_QUERY_SERVICE)) {
129 return null;
130 }
131
132 Element tq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
133 tq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
134 tq_service.setAttribute(GSXML.NAME_ATT, TEXT_QUERY_SERVICE);
135 if (subset==null || subset.equals(GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER)) {
136 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getServiceName(TEXT_QUERY_SERVICE, lang) ));
137 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getServiceSubmit(TEXT_QUERY_SERVICE, lang) ));
138 tq_service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getServiceDescription(TEXT_QUERY_SERVICE, lang)));
139 }
140 if (subset==null || subset.equals(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER)) {
141 Element param_list = this.doc.createElement(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
142 addCustomQueryParams(param_list, lang);
143 addStandardQueryParams(param_list, lang);
144 tq_service.appendChild(param_list);
145 }
146 return tq_service;
147
148 }
149
150 // perhaps these should be changed to search down the class hierarchy for
151 // values - do can just put the info in the resource bundle to use it
152 /** returns the default name for the TextQuery service */
153 protected String getServiceName(String service_id, String lang) {
154 return getTextString(service_id+".name", lang);
155 }
156
157 /** returns the default description for the TextQuery service */
158 protected String getServiceDescription(String service_id, String lang) {
159 return getTextString(service_id+".description", lang);
160 }
161
162 /** returns the default submit button text for the TextQuery service */
163 protected String getServiceSubmit(String service_id, String lang) {
164 return getTextString(service_id+".submit", lang);
165
166 }
167 /** adds the standard query params into the service description */
168 protected void addStandardQueryParams(Element param_list, String lang)
169 {
170 createParameter(INDEX_PARAM, param_list, lang);
171 if (does_chunking) {
172 createParameter(MAXDOCS_PARAM, param_list, lang);
173 }
174 if (does_paging) {
175 createParameter(HITS_PER_PAGE_PARAM, param_list, lang);
176 createParameter(START_PAGE_PARAM, param_list, lang);
177 }
178 createParameter(QUERY_PARAM, param_list, lang);
179 }
180
181 /** adds any service specific query params into the service
182 * default implementation: add nothing. subclasses may need to
183 * override this to add in their specific parameters
184 */
185 protected void addCustomQueryParams(Element param_list, String lang)
186 {
187 // default behaviour, do nothing
188 }
189
190 /** default implementations for the standard parameters plus some
191 * other common ones
192 * index, maxDocs, hitsPerPage, startPage, query, case, stem,
193 */
194 protected void createParameter(String name, Element param_list, String lang) {
195 Element param = null;
196 if (name.equals(QUERY_PARAM)) {
197 param = GSXML.createParameterDescription(this.doc, QUERY_PARAM, getTextString("param."+QUERY_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
198 param_list.appendChild(param);
199 } else if (name.equals(INDEX_PARAM)) {
200
201 // should we make these class fields?
202 ArrayList index_ids = new ArrayList();
203 ArrayList index_names = new ArrayList();
204 getIndexData(index_ids, index_names, lang);
205 String param_type = GSXML.PARAM_TYPE_ENUM_SINGLE;
206 if (does_multi_index_search) {
207 param_type = GSXML.PARAM_TYPE_ENUM_MULTI;
208 }
209 param = GSXML.createParameterDescription2(this.doc, INDEX_PARAM, getTextString("param."+INDEX_PARAM, lang), param_type, /*this.default_index*/"", index_ids, index_names);
210 param_list.appendChild(param);
211 } else if (name.equals(MAXDOCS_PARAM)) {
212 param = GSXML.createParameterDescription(this.doc, MAXDOCS_PARAM, getTextString("param."+MAXDOCS_PARAM, lang), GSXML.PARAM_TYPE_INTEGER, "10", null, null);
213 param_list.appendChild(param);
214 } else if (name.equals(CASE_PARAM) || name.equals(STEM_PARAM)) {
215 String[] bool_ops = {"0", "1"};
216 String[] bool_texts = {getTextString("param.boolean.off", lang),getTextString("param.boolean.on", lang)};
217 param = GSXML.createParameterDescription(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_BOOLEAN, BOOLEAN_PARAM_ON, bool_ops, bool_texts);
218 param_list.appendChild(param);
219 } else if (name.equals(MATCH_PARAM)) {
220 String[] vals = {MATCH_PARAM_SOME, MATCH_PARAM_ALL };
221 String[] val_texts = {getTextString("param."+MATCH_PARAM+"."+MATCH_PARAM_SOME, lang), getTextString("param."+MATCH_PARAM+"."+MATCH_PARAM_ALL, lang)};
222 param = GSXML.createParameterDescription(this.doc, MATCH_PARAM, getTextString("param."+MATCH_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, MATCH_PARAM_SOME, vals, val_texts);
223 param_list.appendChild(param);
224 } else if (name.equals(HITS_PER_PAGE_PARAM)) {
225 // hits per page
226 String [] hits_options = {"10", "30", "50"};
227 param = GSXML.createParameterDescription(this.doc, HITS_PER_PAGE_PARAM, getTextString("param."+HITS_PER_PAGE_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, "10", hits_options, hits_options);
228 param_list.appendChild(param);
229 } else if (name.equals(START_PAGE_PARAM)) {
230 // start page - set to 1 for the search page
231 param = GSXML.createParameterDescription(this.doc, START_PAGE_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, "1", null, null);
232 param_list.appendChild(param);
233 }
234
235
236 }
237 /** create an element to go into the search results list. A node element
238 * has the form
239 * <docNode nodeId='xxx' nodeType='leaf' docType='hierarchy' rank='0.23'/>
240 */
241 protected Element createDocNode(String node_id, String rank) {
242 Element node = this.doc.createElement(GSXML.DOC_NODE_ELEM);
243 node.setAttribute(GSXML.NODE_ID_ATT, node_id);
244 node.setAttribute(GSXML.NODE_RANK_ATT, rank);
245 String doc_type = null;
246 if (default_document_type != null) {
247 doc_type = default_document_type;
248 } else {
249 doc_type = getDocType(node_id);
250 }
251 node.setAttribute(GSXML.DOC_TYPE_ATT, doc_type);
252 String node_type = getNodeType(node_id, doc_type);
253 node.setAttribute(GSXML.NODE_TYPE_ATT, node_type);
254 return node;
255 }
256
257 /** returns the node type of the specified node.
258 should be one of
259 GSXML.NODE_TYPE_LEAF,
260 GSXML.NODE_TYPE_INTERNAL,
261 GSXML.NODE_TYPE_ROOT
262 */
263 protected String getNodeType(String node_id, String doc_type) {
264 if (doc_type.equals(GSXML.DOC_TYPE_SIMPLE)) {
265 return GSXML.NODE_TYPE_LEAF;
266 }
267
268 if (!hasParent(node_id)) {
269 return GSXML.NODE_TYPE_ROOT;
270 }
271 if (doc_type.equals(GSXML.DOC_TYPE_PAGED)) {
272 return GSXML.NODE_TYPE_LEAF;
273 }
274 if (!hasChildren(node_id)) {
275 return GSXML.NODE_TYPE_LEAF;
276 }
277 return GSXML.NODE_TYPE_INTERNAL;
278
279 }
280
281
282 /** returns the document type of the doc that the specified node
283 belongs to. should be one of
284 GSXML.DOC_TYPE_SIMPLE,
285 GSXML.DOC_TYPE_PAGED,
286 GSXML.DOC_TYPE_HIERARCHY
287 default implementation returns GSXML.DOC_TYPE_SIMPLE, over ride
288 if documents can be hierarchical
289 */
290 protected String getDocType(String node_id) {
291 return GSXML.DOC_TYPE_SIMPLE;
292 }
293
294 /** returns true if the node has child nodes
295 * default implementation returns false, over ride if documents can be
296 * hierarchical
297 */
298 protected boolean hasChildren(String node_id) {
299 return false;
300 }
301 /** returns true if the node has a parent
302 * default implementation returns false, over ride if documents can be
303 * hierarchical*/
304 protected boolean hasParent(String node_id) {
305 return false;
306 }
307
308 /** do the actual query
309 * must be implemented by subclass */
310 abstract protected Element processTextQuery(Element request);
311
312 /** get the details about the indexes available
313 * must be implemented by subclass
314 * there must be at least one index */
315 abstract protected void getIndexData(ArrayList index_ids, ArrayList index_names, String lang);
316
317}
318
Note: See TracBrowser for help on using the repository browser.