Permalink
Browse files

Replace sorting by distance with scoring by distance (#280)

  • Loading branch information...
fsteeg committed Dec 9, 2016
1 parent 581c491 commit 6cf93d84bb88248573b9714d9107151177809740
Showing with 99 additions and 13 deletions.
  1. +17 −8 app/controllers/Application.java
  2. +4 −5 app/controllers/Index.java
  3. +78 −0 app/controllers/Zero.java
@@ -17,6 +17,8 @@
import org.elasticsearch.index.query.GeoPolygonQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
+import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsBuilder;
@@ -308,22 +310,29 @@ private static String buildDistanceQuery(String q, int from, int size,
}
static SearchResponse executeQuery(int from, int size, QueryBuilder query) {
- SearchRequestBuilder searchRequest =
- Index.CLIENT.prepareSearch(ES_NAME).setTypes(ES_TYPE)//
- .setSearchType(SearchType.QUERY_THEN_FETCH).setQuery(query)//
- .setFrom(from)//
- .setSize(size);
+ SearchRequestBuilder searchRequest = Index.CLIENT.prepareSearch(ES_NAME)
+ .setTypes(ES_TYPE).setSearchType(SearchType.QUERY_THEN_FETCH)
+ .setQuery(preprocess(query)).setFrom(from).setSize(size);
searchRequest = withAggregations(searchRequest, "type.raw",
localizedLabel("classification.label.raw"),
localizedLabel("fundertype.label.raw"),
localizedLabel("collects.extent.label.raw"));
+ return searchRequest.execute().actionGet();
+ }
+
+ private static QueryBuilder preprocess(QueryBuilder query) {
String position = session("position");
if (position != null) {
Logger.info("Sorting by distance to current position {}", position);
- searchRequest.addSort(new GeoDistanceSortBuilder("location.geo")
- .points(new GeoPoint(position)));
+ ScoreFunctionBuilder locationScore = ScoreFunctionBuilders
+ .linearDecayFunction("location.geo", new GeoPoint(position), "3km")
+ .setOffset("0km");
+ return QueryBuilders.functionScoreQuery(query).boostMode("sum")
+ .add(QueryBuilders.existsQuery("location.geo"), locationScore)
+ .add(ScoreFunctionBuilders.scriptFunction(new Script("zero")))
+ .scoreMode("first");
}
- return searchRequest.execute().actionGet();
+ return query;
}
private static SearchRequestBuilder withAggregations(
@@ -60,11 +60,10 @@ public ConfigurableNode(Settings settings,
Application.CONFIG.getString("index.es.port.tcp"))
.put("script.default_lang", "native").build();
- private static Node node =
- new ConfigurableNode(
- nodeBuilder().settings(clientSettings).local(true).getSettings()
- .build(),
- Arrays.asList(BundlePlugin.class, LocationAggregation.class)).start();
+ private static Node node = new ConfigurableNode(
+ nodeBuilder().settings(clientSettings).local(true).getSettings().build(),
+ Arrays.asList(BundlePlugin.class, LocationAggregation.class, Zero.class))
+ .start();
/**
* The Elasticsearch client to be used by all parts of the application
*/
View
@@ -0,0 +1,78 @@
+/* Copyright 2016, hbz. Licensed under the Eclipse Public License 1.0 */
+
+package controllers;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.script.AbstractSearchScript;
+import org.elasticsearch.script.ExecutableScript;
+import org.elasticsearch.script.NativeScriptFactory;
+import org.elasticsearch.script.ScriptModule;
+
+/**
+ * Native script plugin to return 0.
+ *
+ * Yes, this seems completely nuts. Let me explain: <br/>
+ *
+ * We need a script that simply returns 0 as a workaround for location scoring,
+ * see: <br/>
+ *
+ * https://github.com/elastic/elasticsearch/issues/7788#issuecomment-57196342
+ * https://github.com/elastic/elasticsearch/issues/18892#issuecomment-226544977
+ * <br/>
+ *
+ * With Groovy scripting this would be straight-forward, but we run in embedded
+ * mode and use native scripting for security reasons, see: <br/>
+ *
+ * https://github.com/hbz/lobid-organisations/commit/acefce9ba980f2f955c79da71e3ccc8068489d56
+ * https://github.com/hbz/lobid-organisations/commit/1027537db9b26911d7294532d4f1ae44244205ce
+ * <br/>
+ *
+ * So we have a full native script for this very simple functionality.
+ *
+ * @author Fabian Steeg (fsteeg)
+ *
+ */
+public class Zero extends Plugin {
+ @Override
+ public String name() {
+ return "zero";
+ }
+
+ @Override
+ public String description() {
+ return "Native script to return 0";
+ }
+
+ /**
+ * @param scriptModule The scriptModule to register this script on
+ */
+ public void onModule(ScriptModule scriptModule) {
+ scriptModule.registerScript(name(), ZeroScriptFactory.class);
+ }
+
+ /** Factory to construct the script. */
+ public static class ZeroScriptFactory implements NativeScriptFactory {
+ @Override
+ public ExecutableScript newScript(@Nullable Map<String, Object> params) {
+ return new ZeroScript();
+ }
+
+ @Override
+ public boolean needsScores() {
+ return false;
+ }
+ }
+
+ /** The actual script. */
+ public static class ZeroScript extends AbstractSearchScript {
+
+ @Override
+ public Object run() {
+ return 0;
+ }
+ }
+}

0 comments on commit 6cf93d8

Please sign in to comment.