PageRenderTime 52ms CodeModel.GetById 16ms app.highlight 31ms RepoModel.GetById 2ms app.codeStats 0ms

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

Relevant Search: With Applications for Solr and Elasticsearch

'Chapter 5. Basic multifield search'. Satisfy multiple user goals when searching. Search through more than one field at a time. Transform fields derived from your data into search friendly ones. Understand the pros and cons of multifield search strategies. Amazon Affiliate Link
http://alageospatialportal.googlecode.com/
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}