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