package me.osm.gazetteer.web.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import me.osm.gazetteer.web.ESNodeHolder;
import me.osm.gazetteer.web.GazetteerWeb;
import me.osm.gazetteer.web.api.meta.Endpoint;
import me.osm.gazetteer.web.api.query.Query;
import me.osm.gazetteer.web.api.search.SearchBuilder;
import me.osm.gazetteer.web.api.utils.BuildSearchQContext;
import me.osm.gazetteer.web.api.utils.RequestUtils;
import me.osm.gazetteer.web.imp.IndexHolder;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.DisMaxQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.json.JSONArray;
import org.json.JSONObject;
import org.restexpress.Request;
import org.restexpress.Response;
import org.restexpress.domain.metadata.UriMetadata;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
public class SuggestAPI extends SearchAPI {
@Inject
private SearchBuilder searchBuilder;
public SuggestAPI() {
super();
GazetteerWeb.injector().injectMembers(this);
}
@Override
public JSONObject read(Request request, Response response)
throws IOException {
String querryString = StringUtils.stripToNull(request.getHeader(Q_HEADER));
Query query = queryAnalyzer.getQuery(querryString);
boolean addressesOnly = RequestUtils.getBooleanHeader(request, SearchAPI.ADDRESSES_ONLY_HEADER, false);
@SuppressWarnings("unchecked")
List<JSONObject> type = addressesOnly ? Collections.EMPTY_LIST : suggestPoiType(query);
JSONObject answer = super.read(request, response);
answer.put("matched_type", new JSONArray(type));
return answer;
}
@Override
public BoolQueryBuilder getSearchQuerry(Query querry, boolean strict, BuildSearchQContext context) {
BoolQueryBuilder searchQuerry = QueryBuilders.boolQuery();
QueryBuilder prefQ = null;
Query tail = querry.tail();
Query head = querry.head();
if(tail.countNumeric() == 1) {
searchBuilder.mainSearchQ(querry, searchQuerry, strict, context);
}
else {
String prefix = tail.toString();
prefQ = QueryBuilders.disMaxQuery()
.add(QueryBuilders.termQuery("housenumber", prefix))
.add(QueryBuilders.prefixQuery("search", prefix))
.add(QueryBuilders.prefixQuery("name.text", prefix));
LoggerFactory.getLogger(getClass()).info("Prefix: {}", prefix);
if(head == null) {
searchQuerry.must(prefQ)
.mustNot(QueryBuilders.termQuery("weight", 0));
}
else {
searchBuilder.mainSearchQ(head, searchQuerry, strict, context);
searchQuerry.must(prefQ);
}
}
return searchQuerry;
}
private List<JSONObject> suggestPoiType(Query query) {
Client client = ESNodeHolder.getClient();
// TODO: process with replacers
Query filtered = query.filter(new HashSet<String>(Arrays.asList("на", "дом")));
DisMaxQueryBuilder dismax = QueryBuilders.disMaxQuery()
.add(QueryBuilders.prefixQuery("translated_title", filtered.toString()));
dismax.add(
QueryBuilders.boolQuery()
.should(QueryBuilders.prefixQuery("translated_title", query.tail().toString()))
.should(QueryBuilders.prefixQuery("keywords", query.tail().toString()))
.minimumNumberShouldMatch(1)
);
if(!query.listToken().isEmpty()) {
dismax.add(QueryBuilders.prefixQuery("translated_title", query.listToken().get(0).toString()));
}
SearchRequestBuilder searchRequest = client.prepareSearch("gazetteer")
.setTypes(IndexHolder.POI_CLASS)
.setQuery(dismax);
SearchHit[] hits = searchRequest.get().getHits().getHits();
List<JSONObject> types = new ArrayList<JSONObject>(hits.length);
if(hits.length > 0) {
for(SearchHit hit : hits) {
types.add(new JSONObject(hit.getSourceAsString()));
}
}
return types;
}
@Override
public Endpoint getMeta(UriMetadata uriMetadata) {
Endpoint meta = super.getMeta(uriMetadata);
meta.setName("Suggest location");
meta.setDescription("Performs prefix 'search on type' search among locations.");
return meta;
}
}