aboutsummaryrefslogtreecommitdiffstats
path: root/container-search
diff options
context:
space:
mode:
authorArne Juul <arnej@verizonmedia.com>2020-07-08 12:55:09 +0000
committerArne Juul <arnej@verizonmedia.com>2020-07-15 15:39:20 +0000
commitc50ae024b2ff3f4b1890373ed30fa5a8c90db177 (patch)
tree958fc77db774fc4d3188535297e8db98d1733737 /container-search
parentaacf9c9ccbcd558e58086ee657c5061e038a62f0 (diff)
test geoLocation parsing and generation
Diffstat (limited to 'container-search')
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/SelectParser.java39
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java9
-rw-r--r--container-search/src/main/java/com/yahoo/search/yql/YqlParser.java20
-rw-r--r--container-search/src/test/java/com/yahoo/prelude/searcher/test/PosSearcherTestCase.java3
-rw-r--r--container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java8
-rw-r--r--container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java18
-rw-r--r--container-search/src/test/java/com/yahoo/select/SelectTestCase.java12
7 files changed, 97 insertions, 12 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/query/SelectParser.java b/container-search/src/main/java/com/yahoo/search/query/SelectParser.java
index 4ae2e4ceddd..330f9a1fef2 100644
--- a/container-search/src/main/java/com/yahoo/search/query/SelectParser.java
+++ b/container-search/src/main/java/com/yahoo/search/query/SelectParser.java
@@ -3,6 +3,8 @@ package com.yahoo.search.query;
import com.google.common.base.Preconditions;
import com.yahoo.collections.LazyMap;
+import com.yahoo.geo.ParseDegree;
+import com.yahoo.geo.ParseDistance;
import com.yahoo.language.Language;
import com.yahoo.language.process.Normalizer;
import com.yahoo.prelude.IndexFacts;
@@ -49,6 +51,7 @@ import com.yahoo.slime.ArrayTraverser;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.ObjectTraverser;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.slime.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -156,7 +159,7 @@ public class SelectParser implements Parser {
}
private QueryTree buildTree() {
- Inspector inspector = SlimeUtils.jsonToSlime(this.query.getSelect().getWhereString().getBytes()).get();
+ Inspector inspector = SlimeUtils.jsonToSlime(this.query.getSelect().getWhereString()).get();
if (inspector.field("error_message").valid()) {
throw new QueryException("Illegal query: " + inspector.field("error_message").asString() +
" at: '" + new String(inspector.field("offending_input").asData(), StandardCharsets.UTF_8) + "'");
@@ -216,7 +219,7 @@ public class SelectParser implements Parser {
/** Translates a list of grouping requests on JSON form to a list in the grouping language form */
private List<String> toGroupingRequests(String groupingJson) {
- Inspector inspector = SlimeUtils.jsonToSlime(groupingJson.getBytes()).get();
+ Inspector inspector = SlimeUtils.jsonToSlime(groupingJson).get();
if (inspector.field("error_message").valid()) {
throw new QueryException("Illegal query: " + inspector.field("error_message").asString() +
" at: '" + new String(inspector.field("offending_input").asData(), StandardCharsets.UTF_8) + "'");
@@ -417,10 +420,36 @@ public class SelectParser implements Parser {
private Item buildGeoLocation(String key, Inspector value) {
HashMap<Integer, Inspector> children = childMap(value);
- Preconditions.checkArgument(children.size() == 2, "Expected 2 arguments, got %s.", children.size());
+ Preconditions.checkArgument(children.size() == 4, "Expected 4 arguments, got %s.", children.size());
String field = children.get(0).asString();
- String location = children.get(1).asString();
- GeoLocationItem item = new GeoLocationItem(new Location(location), field);
+ var arg1 = children.get(1);
+ var arg2 = children.get(2);
+ var arg3 = children.get(3);
+ var loc = new Location();
+ double radius = -1;
+ if (arg3.type() == Type.STRING) {
+ radius = new ParseDistance(arg3.asString()).degrees;
+ } else if (arg3.type() == Type.LONG) {
+ radius = new ParseDistance(String.valueOf(arg3.asLong())).degrees;
+ } else {
+ throw new IllegalArgumentException("Invalid geoLocation radius type "+arg3.type()+" for "+arg3);
+ }
+ if (arg1.type() == Type.STRING && arg2.type() == Type.STRING) {
+ var coord_1 = new ParseDegree(true, children.get(1).asString());
+ var coord_2 = new ParseDegree(false, children.get(2).asString());
+ if (coord_1.foundLatitude && coord_2.foundLongitude) {
+ loc.setGeoCircle(coord_1.latitude, coord_2.longitude, radius);
+ } else if (coord_2.foundLatitude && coord_1.foundLongitude) {
+ loc.setGeoCircle(coord_2.latitude, coord_1.longitude, radius);
+ } else {
+ throw new IllegalArgumentException("Invalid geoLocation coordinates '"+coord_1+"' and '"+coord_2+"'");
+ }
+ } else if (arg1.type() == Type.DOUBLE && arg2.type() == Type.DOUBLE) {
+ loc.setGeoCircle(arg1.asDouble(), arg2.asDouble(), radius);
+ } else {
+ throw new IllegalArgumentException("Invalid geoLocation coordinate types "+arg1.type()+" and "+arg2.type());
+ }
+ var item = new GeoLocationItem(loc, field);
Inspector annotations = getAnnotations(value);
if (annotations != null){
annotations.traverse((ObjectTraverser) (annotation_name, annotation_value) -> {
diff --git a/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java b/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
index 77250ecd439..22328fb026e 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/VespaSerializer.java
@@ -701,9 +701,12 @@ public class VespaSerializer {
destination.append("([{").append(annotations).append("}]");
}
destination.append(GEO_LOCATION).append('(');
- destination.append(item.getIndexName()).append(", ").append('"');
- escape(item.getIndexedString(), destination);
- destination.append('"').append(')');
+ destination.append(item.getIndexName()).append(", ");
+ var loc = item.getLocation();
+ destination.append(loc.degNS()).append(", ");
+ destination.append(loc.degEW()).append(", ");
+ destination.append('"').append(loc.degRadius()).append(" deg").append('"');
+ destination.append(')');
return false;
}
}
diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
index a0bb4efb70b..446e855cdce 100644
--- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
+++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java
@@ -19,6 +19,8 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.yahoo.collections.LazyMap;
import com.yahoo.collections.LazySet;
+import com.yahoo.geo.ParseDegree;
+import com.yahoo.geo.ParseDistance;
import com.yahoo.language.Language;
import com.yahoo.language.detect.Detector;
import com.yahoo.language.process.Normalizer;
@@ -420,10 +422,20 @@ public class YqlParser implements Parser {
private Item buildGeoLocation(OperatorNode<ExpressionOperator> ast) {
List<OperatorNode<ExpressionOperator>> args = ast.getArgument(1);
- Preconditions.checkArgument(args.size() == 2, "Expected 2 arguments, got %s.", args.size());
+ Preconditions.checkArgument(args.size() == 4, "Expected 4 arguments, got %s.", args.size());
String field = fetchFieldRead(args.get(0));
- String location = fetchFieldRead(args.get(1));
- GeoLocationItem item = new GeoLocationItem(new Location(location), field);
+ var coord_1 = new ParseDegree(true, fetchFieldRead(args.get(1)));
+ var coord_2 = new ParseDegree(false, fetchFieldRead(args.get(2)));
+ var radius = new ParseDistance(fetchFieldRead(args.get(3)));
+ var loc = new Location();
+ if (coord_1.foundLatitude && coord_2.foundLongitude) {
+ loc.setGeoCircle(coord_1.latitude, coord_2.longitude, radius.degrees);
+ } else if (coord_2.foundLatitude && coord_1.foundLongitude) {
+ loc.setGeoCircle(coord_2.latitude, coord_1.longitude, radius.degrees);
+ } else {
+ throw new IllegalArgumentException("Invalid geoLocation coordinates '"+coord_1+"' and '"+coord_2+"'");
+ }
+ var item = new GeoLocationItem(loc, field);
String label = getAnnotation(ast, LABEL, String.class, null, "item label");
if (label != null) {
item.setLabel(label);
@@ -920,6 +932,8 @@ public class YqlParser implements Parser {
private static String fetchFieldRead(OperatorNode<ExpressionOperator> ast) {
switch (ast.getOperator()) {
+ case LITERAL:
+ return ast.getArgument(0).toString();
case READ_FIELD:
return ast.getArgument(1);
case PROPREF:
diff --git a/container-search/src/test/java/com/yahoo/prelude/searcher/test/PosSearcherTestCase.java b/container-search/src/test/java/com/yahoo/prelude/searcher/test/PosSearcherTestCase.java
index aa3fa53119e..aa48e8494f2 100644
--- a/container-search/src/test/java/com/yahoo/prelude/searcher/test/PosSearcherTestCase.java
+++ b/container-search/src/test/java/com/yahoo/prelude/searcher/test/PosSearcherTestCase.java
@@ -123,7 +123,8 @@ public class PosSearcherTestCase {
q.properties().set("pos.ll", "N0;E0");
q.properties().set("pos.radius", "-1");
doSearch(searcher, q, 0, 10);
- assertEquals("(2,0,0,536870912,0,1,0,4294967295)", q.getRanking().getLocation().toString());
+ assertEquals("(2,0,0,-1,0,1,0,4294967295)", q.getRanking().getLocation().toString());
+ assertEquals("(2,0,0,536870912,0,1,0,4294967295)", q.getRanking().getLocation().backendString());
}
/**
diff --git a/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
index d770b08d31a..f8e930fa19d 100644
--- a/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/yql/VespaSerializerTestCase.java
@@ -119,6 +119,14 @@ public class VespaSerializerTestCase {
}
@Test
+ public void testGeoLocation() {
+ parseAndConfirm("geoLocation(workplace, 63.418417, 10.433033, \"0.5 deg\")");
+ parseAndConfirm("geoLocation(headquarters, 37.41638, -122.024683, \"180.0 deg\")");
+ parseAndConfirm("geoLocation(home, -17.0, 42.0, \"0.0 deg\")");
+ parseAndConfirm("geoLocation(workplace, -12.0, -34.0, \"-1.0 deg\")");
+ }
+
+ @Test
public void testNear() {
parseAndConfirm("title contains near(\"a\", \"b\")");
parseAndConfirm("title contains ([{\"distance\": 50}]near(\"a\", \"b\"))");
diff --git a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
index a151244525a..75199eeec55 100644
--- a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java
@@ -548,6 +548,24 @@ public class YqlParserTestCase {
}
@Test
+ public void testGeoLocation() {
+ assertParse("select foo from bar where geoLocation(workplace, 63.418417, 10.433033, 500000);",
+ "GEO_LOCATION workplace:(2,10433033,63418417,500000,0,1,0,1921876103)");
+ assertParse("select foo from bar where geoLocation(headquarters, \"37.416383\", \"-122.024683\", \"100 miles\");",
+ "GEO_LOCATION headquarters:(2,-122024683,37416383,1450561,0,1,0,3411238761)");
+ assertParse("select foo from bar where geoLocation(home, \"E10.433033\", \"N63.418417\", \"5km\");",
+ "GEO_LOCATION home:(2,10433033,63418417,45066,0,1,0,1921876103)");
+
+ assertParseFail("select foo from bar where geoLocation(qux, 1, 2);",
+ new IllegalArgumentException("Expected 4 arguments, got 3."));
+ assertParseFail("select foo from bar where geoLocation(qux, 1.0, \"N1.0\", 100);",
+ new IllegalArgumentException(
+ "Invalid geoLocation coordinates '1.0 -> latitude(1.0)' and 'N1.0 -> latitude(1.0)'"));
+ assertParse("select foo from bar where geoLocation(workplace, -12, -34, -77);",
+ "GEO_LOCATION workplace:(2,-34000000,-12000000,-1,0,1,0,4201111954)");
+ }
+
+ @Test
public void testNearestNeighbor() {
assertParse("select foo from bar where nearestNeighbor(semantic_embedding, my_vector);",
"NEAREST_NEIGHBOR {field=semantic_embedding,queryTensorName=my_vector,hnsw.exploreAdditionalHits=0,approximate=true,targetHits=0}");
diff --git a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
index 4691ef42e55..af1c1e48011 100644
--- a/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
+++ b/container-search/src/test/java/com/yahoo/select/SelectTestCase.java
@@ -522,6 +522,18 @@ public class SelectTestCase {
}
@Test
+ public void testGeoLocation() {
+ assertParse("{ \"geoLocation\": [ \"workplace\", 63.418417, 10.433033, 500000 ] }",
+ "GEO_LOCATION workplace:(2,10433033,63418417,500000,0,1,0,1921876103)");
+ assertParse("{ \"geoLocation\": [ \"headquarters\", \"37.416383\", \"-122.024683\", \"100 miles\" ] }",
+ "GEO_LOCATION headquarters:(2,-122024683,37416383,1450561,0,1,0,3411238761)");
+ assertParse("{ \"geoLocation\": [ \"home\", \"E10.433033\", \"N63.418417\", \"5km\" ] }",
+ "GEO_LOCATION home:(2,10433033,63418417,45066,0,1,0,1921876103)");
+ assertParse("{ \"geoLocation\": [ \"workplace\", -12.0, -34.0, -77 ] }",
+ "GEO_LOCATION workplace:(2,-34000000,-12000000,-1,0,1,0,4201111954)");
+ }
+
+ @Test
public void testNearestNeighbor() {
assertParse("{ \"nearestNeighbor\": [ \"f1field\", \"q2prop\" ] }",
"NEAREST_NEIGHBOR {field=f1field,queryTensorName=q2prop,hnsw.exploreAdditionalHits=0,approximate=true,targetHits=0}");