/gazetteer/src/main/java/org/ala/rest/Search.java

http://alageospatialportal.googlecode.com/ · Java · 165 lines · 121 code · 20 blank · 24 comment · 11 complexity · 5525786992704328aa41eefa9993b01b MD5 · raw file

  1. package org.ala.rest;
  2. import java.util.ArrayList;
  3. import org.apache.lucene.analysis.standard.StandardAnalyzer;
  4. import org.apache.lucene.document.Document;
  5. import org.apache.lucene.search.IndexSearcher;
  6. import org.apache.lucene.search.Query;
  7. import org.apache.lucene.search.ScoreDoc;
  8. import org.apache.lucene.search.TopDocs;
  9. import java.io.File;
  10. import org.apache.lucene.util.Version;
  11. import java.util.List;
  12. import org.apache.lucene.store.FSDirectory;
  13. import org.apache.lucene.document.Fieldable;
  14. import org.vfny.geoserver.global.GeoserverDataDirectory;
  15. import java.io.IOException;
  16. import org.apache.lucene.queryParser.MultiFieldQueryParser;
  17. import org.apache.lucene.queryParser.ParseException;
  18. import java.util.Map;
  19. import java.util.HashMap;
  20. import com.thoughtworks.xstream.annotations.XStreamAlias;
  21. import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
  22. import java.util.Arrays;
  23. import java.util.logging.Logger;
  24. import org.apache.commons.lang.exception.ExceptionUtils;
  25. import org.geoserver.platform.GeoServerExtensions;
  26. import org.geotools.util.logging.Logging;
  27. /***
  28. *
  29. * @author angus
  30. */
  31. @XStreamAlias("search")
  32. public class Search {
  33. private static final Logger logger = Logging.getLogger("org.ala.rest.Search");
  34. @XStreamAlias("results")
  35. ArrayList<SearchResultItem> results;
  36. @XStreamAlias("xmlns:xlink")
  37. @XStreamAsAttribute
  38. String xlink = "http://www.w3.org/1999/xlink";
  39. /***
  40. *
  41. * @return a HashMap representation of the resource - which will be serialized into xml/json
  42. */
  43. public Map getMap() {
  44. HashMap<String, ArrayList<SearchResultItem>> resultsMap = new HashMap<String, ArrayList<SearchResultItem>>();
  45. resultsMap.put("results", this.results);
  46. return resultsMap;
  47. }
  48. public ArrayList<SearchResultItem> getResults() {
  49. return this.results;
  50. }
  51. /**
  52. * If no type is specified, assume that it is a name based search
  53. * @param searchTerms
  54. * @param layers
  55. */
  56. public Search(String searchTerms, String[] layers) {
  57. this(searchTerms, layers, "name");
  58. }
  59. /**
  60. * Searches for a feature within a layer based on name.
  61. * @param searchTerms
  62. * @param layers
  63. * @param type
  64. */
  65. public Search(String searchTerms, String[] layers, String type) {
  66. results = new ArrayList<SearchResultItem>();
  67. GazetteerConfig gc = GeoServerExtensions.bean(GazetteerConfig.class);
  68. String layerSearch = "";
  69. if (layers.length > 0) {
  70. layerSearch = "layerName:(";
  71. int count = 0;
  72. for (String layerName : layers) {
  73. layerSearch += layerName;
  74. count++;
  75. if (count < layers.length) {
  76. layerSearch += " OR ";
  77. }
  78. }
  79. layerSearch += ")";
  80. }
  81. logger.finer("layerSearch is " + layerSearch);
  82. try {
  83. //Get the geoserver data directory from the geoserver instance
  84. File file = new File(GeoserverDataDirectory.getGeoserverDataDirectory(), "gazetteer-index");
  85. IndexSearcher is = new IndexSearcher(FSDirectory.open(file));
  86. String[] searchFields = {"name", "layerName"};
  87. if (type.compareTo("id") == 0) {
  88. searchFields = new String[]{"id", "layerName"};
  89. logger.finer("searching based on id");
  90. logger.finer("search terms are " + searchTerms);
  91. }
  92. searchTerms = searchTerms.replace("_", " ");
  93. //escape lucene reserved symbols
  94. searchTerms = searchTerms.replace("(", "\\(");
  95. searchTerms = searchTerms.replace(")", "\\)");
  96. searchTerms = searchTerms.replace("{", "\\{");
  97. searchTerms = searchTerms.replace("}", "\\}");
  98. searchTerms = searchTerms.replace("+", "\\+");
  99. searchTerms = searchTerms.replace("-", "\\-");
  100. searchTerms = searchTerms.replace("^", "\\^");
  101. //searchTerms = searchTerms.replace("*", "\\*");
  102. MultiFieldQueryParser qp = new MultiFieldQueryParser(Version.LUCENE_CURRENT, searchFields, new StandardAnalyzer(Version.LUCENE_CURRENT));
  103. qp.setDefaultOperator(qp.AND_OPERATOR);
  104. Query nameQuery = qp.parse(searchTerms.toLowerCase() + " AND " + layerSearch);
  105. //TODO: instead of 20 - should be variable and paging?
  106. TopDocs topDocs = is.search(nameQuery, 20);
  107. HashMap scoreDocs = new HashMap();
  108. for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
  109. Document doc = is.doc(scoreDoc.doc);
  110. if (doc.get("name").toLowerCase().startsWith(searchTerms.toLowerCase().replace("*",""))){
  111. scoreDoc.score += 3;
  112. }
  113. if (scoreDocs.containsKey(scoreDoc.score)) {
  114. List docs = (List)scoreDocs.get(scoreDoc.score);
  115. docs.add(scoreDoc);
  116. scoreDocs.put(scoreDoc.score,docs);
  117. }
  118. else {
  119. List docs = new ArrayList();
  120. docs.add(scoreDoc);
  121. scoreDocs.put(scoreDoc.score,docs);
  122. }
  123. }
  124. Object[] keys = scoreDocs.keySet().toArray();
  125. Arrays.sort(keys);
  126. for (int i = keys.length -1; i >= 0;i--) {
  127. for(Object o : (List)scoreDocs.get(keys[i])) {
  128. ScoreDoc scoreDoc = (ScoreDoc)o;
  129. Document doc = is.doc(scoreDoc.doc);
  130. List<Fieldable> fields = doc.getFields();
  131. float score = scoreDoc.score;
  132. results.add(new SearchResultItem(fields, true, score));
  133. }
  134. }
  135. is.close();
  136. } catch (IOException e1) {
  137. //FIXME: Log error - return http error code?
  138. logger.severe("An error occurred in search.");
  139. logger.severe(ExceptionUtils.getFullStackTrace(e1));
  140. } catch (ParseException e3) {
  141. logger.severe("An error occurred parsing search terms.");
  142. logger.severe(ExceptionUtils.getFullStackTrace(e3));
  143. }
  144. }
  145. }