source: main/trunk/greenstone2/common-src/indexers/lucene-gs/src/org/greenstone/LuceneWrapper3/GS2LuceneQuery.java@ 27086

Last change on this file since 27086 was 27086, checked in by kjdon, 11 years ago

added reverse sort option to lucene sorting of search results

File size: 24.2 KB
Line 
1/**********************************************************************
2 *
3 * GS2LuceneQuery.java
4 *
5 * Copyright 2004 The New Zealand Digital Library Project
6 *
7 * A component of the Greenstone digital library software
8 * from the New Zealand Digital Library Project at the
9 * University of Waikato, New Zealand.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 *********************************************************************/
26package org.greenstone.LuceneWrapper3;
27
28
29import java.io.*;
30import java.util.*;
31import java.util.regex.*;
32
33import org.apache.lucene.analysis.Analyzer;
34import org.apache.lucene.analysis.standard.StandardAnalyzer;
35import org.apache.lucene.document.Document;
36import org.apache.lucene.index.IndexReader;
37import org.apache.lucene.index.Term;
38import org.apache.lucene.index.TermDocs;
39import org.apache.lucene.queryParser.ParseException;
40import org.apache.lucene.queryParser.QueryParser;
41import org.apache.lucene.search.BooleanQuery; // for the TooManyClauses exception
42import org.apache.lucene.search.Filter;
43import org.apache.lucene.search.IndexSearcher;
44import org.apache.lucene.search.MultiTermQuery;
45import org.apache.lucene.search.MultiTermQuery.ConstantScoreAutoRewrite;
46import org.apache.lucene.search.Query;
47import org.apache.lucene.search.TermRangeFilter;
48import org.apache.lucene.search.Searcher;
49import org.apache.lucene.search.ScoreDoc;
50import org.apache.lucene.search.Sort;
51import org.apache.lucene.search.SortField;
52import org.apache.lucene.search.TopFieldDocs;
53
54import org.apache.lucene.store.Directory;
55import org.apache.lucene.store.FSDirectory;
56import org.apache.lucene.util.Version;
57
58
59public class GS2LuceneQuery extends SharedSoleneQuery
60{
61 protected String full_indexdir="";
62
63 protected boolean reverse_sort = false;
64 protected Sort sorter=new Sort();
65 protected Filter filter = null;
66
67 protected static Version matchVersion = Version.LUCENE_24;
68
69 protected QueryParser query_parser = null;
70 protected QueryParser query_parser_no_stop_words = null;
71 protected Searcher searcher = null;
72 protected IndexReader reader = null;
73
74 public GS2LuceneQuery() {
75 super();
76
77 // Create one query parser with the standard set of stop words, and one with none
78
79 query_parser = new QueryParser(matchVersion, TEXTFIELD, new GS2Analyzer()); // uses built-in stop_words_set
80 query_parser_no_stop_words = new QueryParser(matchVersion, TEXTFIELD, new GS2Analyzer(new String[] { }));
81 }
82
83
84 public boolean initialise() {
85
86 if (!super.initialise()) {
87 return false;
88 }
89
90
91 if (full_indexdir==null || full_indexdir.length()==-1){
92 utf8out.println("Index directory is not indicated ");
93 utf8out.flush();
94 return false;
95 }
96
97 try {
98 Directory full_indexdir_dir = FSDirectory.open(new File(full_indexdir));
99 searcher = new IndexSearcher(full_indexdir_dir,true);
100 reader = ((IndexSearcher) searcher).getIndexReader();
101
102 }
103 catch (IOException exception) {
104 exception.printStackTrace();
105 return false;
106 }
107 return true;
108
109 }
110
111 public void setIndexDir(String full_indexdir) {
112 this.full_indexdir = full_indexdir;
113 }
114
115 public void setSortField(String sort_field) {
116 super.setSortField(sort_field);
117
118 if (sort_field == null) {
119 this.sorter = new Sort();
120 } else {
121 this.sorter = new Sort(new SortField(sort_field,SortField.STRING, this.reverse_sort)); // **** can do better than this?!?
122 }
123 }
124 public void setReverseSort() {
125 this.reverse_sort = true;
126 if (this.sort_field != null) {
127 this.sorter = new Sort(new SortField(this.sort_field, SortField.STRING, this.reverse_sort));
128 }
129 }
130 public boolean getReverseSort() {
131 return this.reverse_sort;
132 }
133
134 public void setFilterString(String filter_string) {
135 super.setFilterString(filter_string);
136 this.filter = parseFilterString(filter_string);
137 }
138
139 public Filter getFilter() {
140 return this.filter;
141 }
142
143
144 public LuceneQueryResult runQuery(String query_string) {
145
146 if (query_string == null || query_string.equals("")) {
147 utf8out.println("The query word is not indicated ");
148 utf8out.flush();
149 return null;
150 }
151
152 LuceneQueryResult lucene_query_result=new LuceneQueryResult();
153 lucene_query_result.clear();
154
155 try {
156 Query query_including_stop_words = query_parser_no_stop_words.parse(query_string);
157 query_including_stop_words = query_including_stop_words.rewrite(reader);
158
159 // System.err.println("********* query_string " + query_string + "****");
160
161 Query query = parseQuery(reader, query_parser, query_string, fuzziness);
162
163 // GS2's LuceneWrapper uses lucene-2.3.2. GS3's LuceneWrapper3 works with lucene-3.3.0.
164 // This change in lucene core library for GS3 (present since after version 2.4.1) had the
165 // side-effect that searching on "econom*" didn't display what terms it was searching for,
166 // whereas it had done so in GS2.
167
168 // The details of this problem and its current solution are explained in the ticket
169 // http://trac.greenstone.org/ticket/845
170
171 // We need to change the settings for the rewriteMethod in order to get searches on wildcards
172 // to produce search terms again when the query gets rewritten.
173
174 // We try, in order:
175 // 1. RewriteMethod set to BooleanQuery, to get it working as in GS2 which uses lucene-2.3.2
176 // it will expand wildcard searches to its terms when searching at both section AND doc level.
177 // If that throws a TooManyClauses exception (like when searching for "a*" over lucene demo collection)
178 // 2. Then try a custom rewriteMethod which sets termCountCutoff=350 and docCountPercent cutoff=0.1%
179 // If that throws a TooManyClauses exception (could perhaps happen if the collection has a huge number of docs
180 // 3. Then try the default apache rewriteMethod with its optimum defaults of
181 // termCountCutoff=350 and docCountPercent cutoff=0.1%
182 // See http://lucene.apache.org/core/3_6_1/api/core/org/apache/lucene/search/MultiTermQuery.html
183
184 if(query instanceof MultiTermQuery) {
185 MultiTermQuery multiTermQuery = (MultiTermQuery)query;
186 multiTermQuery.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE);
187 // less CPU intensive than MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE)
188 }
189
190 try {
191 query = query.rewrite(reader);
192 }
193 catch(BooleanQuery.TooManyClauses clauseException) {
194 // Example test case: try searching the lucene demo collection for "a*"
195 // and you'll hit this exception
196
197 lucene_query_result.setError(LuceneQueryResult.TOO_MANY_CLAUSES_ERROR);
198
199 if(query instanceof MultiTermQuery) {
200
201 // CustomRewriteMethod: setting the docCountPercent cutoff to a custom 100%.
202 // This will at least expand the query to its terms when searching with wildcards at section-level
203 // (though it doesn't seem to work for doc-level searches, no matter what the cutoffs are set to).
204
205 MultiTermQuery.ConstantScoreAutoRewrite customRewriteMethod = new MultiTermQuery.ConstantScoreAutoRewrite();
206 customRewriteMethod.setDocCountPercent(100.0);
207 customRewriteMethod.setTermCountCutoff(350); // same as default
208
209 MultiTermQuery multiTermQuery = (MultiTermQuery)query;
210 multiTermQuery.setRewriteMethod(customRewriteMethod);
211 try {
212 query = query.rewrite(reader);
213 }
214 catch(BooleanQuery.TooManyClauses clauseExceptionAgain) {
215
216 // do what the code originally did: use the default rewriteMethod which
217 // uses a default docCountPercent=0.1 (%) and termCountCutoff=350
218
219 multiTermQuery.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT);
220 query = query.rewrite(reader);
221 }
222 }
223 }
224
225 // Get the list of expanded query terms and their frequencies
226 // num docs matching, and total frequency
227 HashSet terms = new HashSet();
228 query.extractTerms(terms);
229
230 HashMap doc_term_freq_map = new HashMap();
231
232 Iterator iter = terms.iterator();
233 while (iter.hasNext()) {
234
235 Term term = (Term) iter.next();
236
237 // Get the term frequency over all the documents
238 TermDocs term_docs = reader.termDocs(term);
239 int term_freq = 0;
240 int match_docs = 0;
241 while (term_docs.next())
242 {
243 if (term_docs.freq() != 0)
244 {
245 term_freq += term_docs.freq();
246 match_docs++;
247
248 // Calculate the document-level term frequency as well
249 Integer lucene_doc_num_obj = new Integer(term_docs.doc());
250 int doc_term_freq = 0;
251 if (doc_term_freq_map.containsKey(lucene_doc_num_obj))
252 {
253 doc_term_freq = ((Integer) doc_term_freq_map.get(lucene_doc_num_obj)).intValue();
254 }
255 doc_term_freq += term_docs.freq();
256
257 doc_term_freq_map.put(lucene_doc_num_obj, new Integer(doc_term_freq));
258 }
259 }
260
261 // Create a term
262 lucene_query_result.addTerm(term.text(), term.field(), match_docs, term_freq);
263 }
264
265 // Get the list of stop words removed from the query
266 HashSet terms_including_stop_words = new HashSet();
267 query_including_stop_words.extractTerms(terms_including_stop_words);
268 Iterator terms_including_stop_words_iter = terms_including_stop_words.iterator();
269 while (terms_including_stop_words_iter.hasNext()) {
270 Term term = (Term) terms_including_stop_words_iter.next();
271 if (!terms.contains(term)) {
272 lucene_query_result.addStopWord(term.text());
273 }
274 }
275
276 // do the query
277 // Simple case for getting all the matching documents
278 if (end_results == Integer.MAX_VALUE) {
279 // Perform the query (filter and sorter may be null)
280 TopFieldDocs hits = searcher.search(query, filter, end_results, sorter);
281 lucene_query_result.setTotalDocs(hits.totalHits);
282
283 // Output the matching documents
284 lucene_query_result.setStartResults(start_results);
285 lucene_query_result.setEndResults(hits.totalHits);
286
287 for (int i = start_results; i <= hits.totalHits; i++) {
288 int lucene_doc_num = hits.scoreDocs[i - 1].doc;
289 Document doc = reader.document(lucene_doc_num);
290 int doc_term_freq = 0;
291 Integer doc_term_freq_object = (Integer) doc_term_freq_map.get(new Integer(lucene_doc_num));
292 if (doc_term_freq_object != null)
293 {
294 doc_term_freq = doc_term_freq_object.intValue();
295 }
296 lucene_query_result.addDoc(doc.get("docOID").trim(), hits.scoreDocs[i-1].score, doc_term_freq);
297 }
298 }
299
300 // Slightly more complicated case for returning a subset of the matching documents
301 else {
302 // Perform the query (filter may be null)
303 TopFieldDocs hits = searcher.search(query, filter, end_results, sorter);
304 lucene_query_result.setTotalDocs(hits.totalHits);
305
306 lucene_query_result.setStartResults(start_results);
307 lucene_query_result.setEndResults(end_results < hits.scoreDocs.length ? end_results: hits.scoreDocs.length);
308
309 // Output the matching documents
310 for (int i = start_results; (i <= hits.scoreDocs.length && i <= end_results); i++) {
311 int lucene_doc_num = hits.scoreDocs[i - 1].doc;
312 Document doc = reader.document(lucene_doc_num);
313 int doc_term_freq = 0;
314 Integer doc_term_freq_object = (Integer) doc_term_freq_map.get(new Integer(lucene_doc_num));
315 if (doc_term_freq_object != null)
316 {
317 doc_term_freq = doc_term_freq_object.intValue();
318 }
319 lucene_query_result.addDoc(doc.get("docOID").trim(), hits.scoreDocs[i-1].score, doc_term_freq);
320 }
321 }
322 }
323
324 catch (ParseException parse_exception) {
325 lucene_query_result.setError(LuceneQueryResult.PARSE_ERROR);
326 }
327 catch (BooleanQuery.TooManyClauses too_many_clauses_exception) {
328 lucene_query_result.setError(LuceneQueryResult.TOO_MANY_CLAUSES_ERROR);
329 }
330 catch (IOException exception) {
331 lucene_query_result.setError(LuceneQueryResult.IO_ERROR);
332 exception.printStackTrace();
333 }
334 catch (Exception exception) {
335 lucene_query_result.setError(LuceneQueryResult.OTHER_ERROR);
336 exception.printStackTrace();
337 }
338 return lucene_query_result;
339 }
340
341 public void setDefaultConjunctionOperator(String default_conjunction_operator) {
342 super.setDefaultConjunctionOperator(default_conjunction_operator);
343
344 if (default_conjunction_operator.equals("AND")) {
345 query_parser.setDefaultOperator(query_parser.AND_OPERATOR);
346 query_parser_no_stop_words.setDefaultOperator(query_parser.AND_OPERATOR);
347 } else { // default is OR
348 query_parser.setDefaultOperator(query_parser.OR_OPERATOR);
349 query_parser_no_stop_words.setDefaultOperator(query_parser.OR_OPERATOR);
350 }
351 }
352
353
354 public void cleanUp() {
355 super.cleanUp();
356 try {
357 if (searcher != null) {
358 searcher.close();
359 }
360 } catch (IOException exception) {
361 exception.printStackTrace();
362 }
363 }
364
365
366 protected Query parseQuery(IndexReader reader, QueryParser query_parser, String query_string, String fuzziness)
367 throws java.io.IOException, org.apache.lucene.queryParser.ParseException
368 {
369 // Split query string into the search terms and the filter terms
370 // * The first +(...) term contains the search terms so count
371 // up '(' and stop when we finish matching ')'
372 int offset = 0;
373 int paren_count = 0;
374 boolean seen_paren = false;
375 while (offset < query_string.length() && (!seen_paren || paren_count > 0)) {
376 if (query_string.charAt(offset) == '(') {
377 paren_count++;
378 seen_paren = true;
379 }
380 if (query_string.charAt(offset) == ')') {
381 paren_count--;
382 }
383 offset++;
384 }
385 String query_prefix = query_string.substring(0, offset);
386 String query_suffix = query_string.substring(offset);
387
388 ///ystem.err.println("Prefix: " + query_prefix);
389 ///ystem.err.println("Suffix: " + query_suffix);
390
391 Query query = query_parser.parse(query_prefix);
392 query = query.rewrite(reader);
393
394 // If this is a fuzzy search, then we need to add the fuzzy
395 // flag to each of the query terms
396 if (fuzziness != null && query.toString().length() > 0) {
397
398 // Revert the query to a string
399 System.err.println("Rewritten query: " + query.toString());
400 // Search through the string for TX:<term> query terms
401 // and append the ~ operator. Note that this search will
402 // not change phrase searches (TX:"<term> <term>") as
403 // fuzzy searching is not possible for these entries.
404 // Yahoo! Time for a state machine!
405 StringBuffer mutable_query_string = new StringBuffer(query.toString());
406 int o = 0; // Offset
407 // 0 = BASE, 1 = SEEN_T, 2 = SEEN_TX, 3 = SEEN_TX:
408 int s = 0; // State
409 while(o < mutable_query_string.length()) {
410 char c = mutable_query_string.charAt(o);
411 if (s == 0 && c == TEXTFIELD.charAt(0)) {
412 ///ystem.err.println("Found T!");
413 s = 1;
414 }
415 else if (s == 1) {
416 if (c == TEXTFIELD.charAt(1)) {
417 ///ystem.err.println("Found X!");
418 s = 2;
419 }
420 else {
421 s = 0; // Reset
422 }
423 }
424 else if (s == 2) {
425 if (c == ':') {
426 ///ystem.err.println("Found TX:!");
427 s = 3;
428 }
429 else {
430 s = 0; // Reset
431 }
432 }
433 else if (s == 3) {
434 // Don't process phrases
435 if (c == '"') {
436 ///ystem.err.println("Stupid phrase...");
437 s = 0; // Reset
438 }
439 // Found the end of the term... add the
440 // fuzzy search indicator
441 // Nor outside the scope of parentheses
442 else if (Character.isWhitespace(c) || c == ')') {
443 ///ystem.err.println("Yahoo! Found fuzzy term.");
444 mutable_query_string.insert(o, '~' + fuzziness);
445 o++;
446 s = 0; // Reset
447 }
448 }
449 o++;
450 }
451 // If we were in the state of looking for the end of a
452 // term - then we just found it!
453 if (s == 3) {
454
455 mutable_query_string.append('~' + fuzziness);
456 }
457 // Reparse the query
458 ///ystem.err.println("Fuzzy query: " + mutable_query_string.toString() + query_suffix);
459 query = query_parser.parse(mutable_query_string.toString() + query_suffix);
460 }
461 else {
462 query = query_parser.parse(query_prefix + query_suffix);
463 }
464
465 return query;
466 }
467
468 protected Filter parseFilterString(String filter_string)
469 {
470 Filter result = null;
471 Pattern pattern = Pattern.compile("\\s*\\+(\\w+)\\:([\\{\\[])(\\d+)\\s+TO\\s+(\\d+)([\\}\\]])\\s*");
472 Matcher matcher = pattern.matcher(filter_string);
473 if (matcher.matches()) {
474 String field_name = matcher.group(1);
475 boolean include_lower = matcher.group(2).equals("[");
476 String lower_term = matcher.group(3);
477 String upper_term = matcher.group(4);
478 boolean include_upper = matcher.group(5).equals("]");
479 result = new TermRangeFilter(field_name, lower_term, upper_term, include_lower, include_upper);
480 }
481 else {
482 System.err.println("Error: Could not understand filter string \"" + filter_string + "\"");
483 }
484 return result;
485 }
486
487
488 /** command line program and auxiliary methods */
489
490 // Fairly self-explanatory I should hope
491 static protected boolean query_result_caching_enabled = false;
492
493
494 static public void main (String args[])
495 {
496 if (args.length == 0) {
497 System.out.println("Usage: GS2LuceneQuery <index directory> [-fuzziness value] [-filter filter_string] [-sort sort_field] [-reverse_sort][-dco AND|OR] [-startresults number -endresults number] [query]");
498 return;
499 }
500
501 try {
502 String index_directory = args[0];
503
504 GS2LuceneQuery queryer = new GS2LuceneQuery();
505 queryer.setIndexDir(index_directory);
506
507 // Prepare the index cache directory, if query result caching is enabled
508 if (query_result_caching_enabled) {
509 // Make the index cache directory if it doesn't already exist
510 File index_cache_directory = new File(index_directory, "cache");
511 if (!index_cache_directory.exists()) {
512 index_cache_directory.mkdir();
513 }
514
515 // Disable caching if the index cache directory isn't available
516 if (!index_cache_directory.exists() || !index_cache_directory.isDirectory()) {
517 query_result_caching_enabled = false;
518 }
519 }
520
521 String query_string = null;
522
523 // Parse the command-line arguments
524 for (int i = 1; i < args.length; i++) {
525 if (args[i].equals("-sort")) {
526 i++;
527 queryer.setSortField(args[i]);
528 }
529 else if (args[i].equals("-reverse_sort")) {
530 queryer.setReverseSort();
531 }
532 else if (args[i].equals("-filter")) {
533 i++;
534 queryer.setFilterString(args[i]);
535 }
536 else if (args[i].equals("-dco")) {
537 i++;
538 queryer.setDefaultConjunctionOperator(args[i]);
539 }
540 else if (args[i].equals("-fuzziness")) {
541 i++;
542 queryer.setFuzziness(args[i]);
543 }
544 else if (args[i].equals("-startresults")) {
545 i++;
546 if (args[i].matches("\\d+")) {
547 queryer.setStartResults(Integer.parseInt(args[i]));
548 }
549 }
550 else if (args[i].equals("-endresults")) {
551 i++;
552 if (args[i].matches("\\d+")) {
553 queryer.setEndResults(Integer.parseInt(args[i]));
554 }
555 }
556 else {
557 query_string = args[i];
558 }
559 }
560
561 if (!queryer.initialise()) {
562 return;
563 }
564
565 // The query string has been specified as a command-line argument
566 if (query_string != null) {
567 runQueryCaching(index_directory, queryer, query_string);
568 }
569
570 // Read queries from STDIN
571 else {
572 BufferedReader in = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
573 while (true) {
574 // Read the query from STDIN
575 query_string = in.readLine();
576 if (query_string == null || query_string.length() == -1) {
577 break;
578 }
579
580 runQueryCaching(index_directory, queryer, query_string);
581
582 }
583 }
584 queryer.cleanUp();
585 }
586 catch (IOException exception) {
587 exception.printStackTrace();
588 }
589 }
590
591 protected static void runQueryCaching(String index_directory, GS2LuceneQuery queryer, String query_string)
592 throws IOException
593 {
594 StringBuffer query_results_xml = new StringBuffer();
595
596 // Check if this query result has been cached from a previous search (if it's enabled)
597 File query_result_cache_file = null;
598 if (query_result_caching_enabled) {
599 // Generate the cache file name from the query options
600 String query_result_cache_file_name = query_string + "-";
601 String fuzziness = queryer.getFuzziness();
602 query_result_cache_file_name += ((fuzziness != null) ? fuzziness : "") + "-";
603 String filter_string = queryer.getFilterString();
604 query_result_cache_file_name += ((filter_string != null) ? filter_string : "") + "-";
605 String sort_string = queryer.getSortField();
606 query_result_cache_file_name += ((sort_string != null) ? sort_string : "") + "-";
607 String reverse_sort_string = (queryer.getReverseSort() ? "1" : "0");
608 query_result_cache_file_name += reverse_sort_string + "-";
609 String default_conjunction_operator = queryer.getDefaultConjunctionOperator();
610 query_result_cache_file_name += default_conjunction_operator + "-";
611 int start_results = queryer.getStartResults();
612 int end_results = queryer.getEndResults();
613 query_result_cache_file_name += start_results + "-" + end_results;
614 query_result_cache_file_name = fileSafe(query_result_cache_file_name);
615
616 // If the query result cache file exists, just return its contents and we're done
617 File index_cache_directory = new File(index_directory, "cache");
618 query_result_cache_file = new File(index_cache_directory, query_result_cache_file_name);
619 if (query_result_cache_file.exists() && query_result_cache_file.isFile()) {
620 FileInputStream fis = new FileInputStream(query_result_cache_file);
621 InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
622 BufferedReader buffered_reader = new BufferedReader(isr);
623 String line = "";
624 while ((line = buffered_reader.readLine()) != null) {
625 query_results_xml.append(line + "\n");
626 }
627 String query_results_xml_string = query_results_xml.toString();
628 query_results_xml_string = query_results_xml_string.replaceFirst("cached=\"false\"", "cached=\"true\"");
629
630 utf8out.print(query_results_xml_string);
631 utf8out.flush();
632
633 return;
634 }
635 }
636
637 // not cached
638 query_results_xml.append("<ResultSet cached=\"false\">\n");
639 query_results_xml.append("<QueryString>" + LuceneQueryResult.xmlSafe(query_string) + "</QueryString>\n");
640 Filter filter = queryer.getFilter();
641 if (filter != null) {
642 query_results_xml.append("<FilterString>" + filter.toString() + "</FilterString>\n");
643 }
644
645 LuceneQueryResult query_result = queryer.runQuery(query_string);
646 if (query_result == null) {
647 System.err.println("Couldn't run the query");
648 return;
649 }
650
651 if (query_result.getError() != LuceneQueryResult.NO_ERROR) {
652 query_results_xml.append("<Error type=\""+query_result.getErrorString()+"\" />\n");
653 } else {
654 query_results_xml.append(query_result.getXMLString());
655 }
656 query_results_xml.append("</ResultSet>\n");
657
658 utf8out.print(query_results_xml);
659 utf8out.flush();
660
661 // Cache this query result, if desired
662 if (query_result_caching_enabled) {
663 // Catch any exceptions thrown trying to write the query result cache file and warn about them, but don't
664 // bother with the full stack trace. It won't affect the functionality if we can't write some cache
665 // files, it will just affect the speed of subsequent requests.
666 // Example exceptions are "permission denied" errors, or "filename too long" errors (the filter string
667 // can get very long in some collections)
668 try
669 {
670 FileWriter query_result_cache_file_writer = new FileWriter(query_result_cache_file);
671 query_result_cache_file_writer.write(query_results_xml.toString());
672 query_result_cache_file_writer.close();
673 }
674 catch (Exception exception)
675 {
676 System.err.println("Warning: Exception occurred trying to write query result cache file (" + exception + ")");
677 }
678 }
679 }
680
681 protected static String fileSafe(String text)
682 {
683 StringBuffer file_safe_text = new StringBuffer();
684 for (int i = 0; i < text.length(); i++) {
685 char character = text.charAt(i);
686 if ((character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || (character >= '0' && character <= '9') || character == '-') {
687 file_safe_text.append(character);
688 }
689 else {
690 file_safe_text.append('%');
691 file_safe_text.append((int) character);
692 }
693 }
694 return file_safe_text.toString();
695 }
696
697
698}
699
700
Note: See TracBrowser for help on using the repository browser.