summaryrefslogtreecommitdiffstats
path: root/searchlib
diff options
context:
space:
mode:
Diffstat (limited to 'searchlib')
-rw-r--r--searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp1
-rw-r--r--searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp50
-rw-r--r--searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp33
-rw-r--r--searchlib/src/tests/common/location/CMakeLists.txt7
-rw-r--r--searchlib/src/tests/common/location/geo_location_test.cpp136
-rw-r--r--searchlib/src/tests/common/location/location_test.cpp121
-rw-r--r--searchlib/src/tests/query/query_visitor_test.cpp2
-rw-r--r--searchlib/src/tests/query/querybuilder_test.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp21
-rw-r--r--searchlib/src/vespa/searchlib/common/CMakeLists.txt3
-rw-r--r--searchlib/src/vespa/searchlib/common/documentlocations.cpp4
-rw-r--r--searchlib/src/vespa/searchlib/common/documentlocations.h4
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location.cpp184
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location.h90
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location_parser.cpp209
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location_parser.h47
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location_spec.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/common/geo_location_spec.h21
-rw-r--r--searchlib/src/vespa/searchlib/common/location.cpp196
-rw-r--r--searchlib/src/vespa/searchlib/common/location.h48
-rw-r--r--searchlib/src/vespa/searchlib/common/locationiterators.cpp28
-rw-r--r--searchlib/src/vespa/searchlib/features/distancefeature.cpp77
-rw-r--r--searchlib/src/vespa/searchlib/features/distancefeature.h7
-rw-r--r--searchlib/src/vespa/searchlib/fef/iqueryenvironment.h4
-rw-r--r--searchlib/src/vespa/searchlib/fef/location.h2
-rw-r--r--searchlib/src/vespa/searchlib/fef/phrase_splitter_query_env.h4
-rw-r--r--searchlib/src/vespa/searchlib/fef/test/queryenvironment.h6
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/parse.h2
-rw-r--r--searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/querynode.cpp7
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/location.cpp93
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/location.h25
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/point.h12
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/rectangle.h16
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h11
36 files changed, 936 insertions, 544 deletions
diff --git a/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp b/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
index a64704a08e9..cf1506a9118 100644
--- a/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
+++ b/searchlib/src/tests/attribute/document_weight_iterator/document_weight_iterator_test.cpp
@@ -17,7 +17,6 @@
#include <vespa/searchlib/attribute/singlestringattribute.h>
#include <vespa/searchlib/index/dummyfileheadercontext.h>
#include <vespa/searchlib/query/tree/location.h>
-#include <vespa/searchlib/query/tree/point.h>
#include <vespa/searchlib/query/tree/simplequery.h>
#include <vespa/searchlib/queryeval/document_weight_search_iterator.h>
#include <vespa/searchlib/test/searchiteratorverifier.h>
diff --git a/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp b/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
index 2eafeab20bd..87aea2e3e8c 100644
--- a/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
+++ b/searchlib/src/tests/attribute/searchable/attribute_searchable_adapter_test.cpp
@@ -339,29 +339,43 @@ TEST("requireThatPrefixTermsWork") {
TEST("requireThatLocationTermsWork") {
// 0xcc is z-curve for (10, 10).
MyAttributeManager attribute_manager = makeAttributeManager(int64_t(0xcc));
-
- SimpleLocationTerm node(Location(Point(10, 10), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(100, 100), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(!search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(13, 13), 4, 0), field, 0, Weight(0));
- EXPECT_TRUE(!search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(10, 13), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(search(node, attribute_manager));
+ {
+ SimpleLocationTerm node(Location(Point{10, 10}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(search(node, attribute_manager));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{100, 100}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!search(node, attribute_manager));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{13, 13}, 4, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!search(node, attribute_manager));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{10, 13}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(search(node, attribute_manager));
+ }
}
TEST("requireThatOptimizedLocationTermsWork") {
// 0xcc is z-curve for (10, 10).
MyAttributeManager attribute_manager = makeFastSearchLongAttributeManager(int64_t(0xcc));
-
- SimpleLocationTerm node(Location(Point(10, 10), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(search(node, attribute_manager, true));
- node = SimpleLocationTerm(Location(Point(100, 100), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(!search(node, attribute_manager, true));
- node = SimpleLocationTerm(Location(Point(13, 13), 4, 0), field, 0, Weight(0));
- EXPECT_TRUE(!search(node, attribute_manager, true));
- node = SimpleLocationTerm(Location(Point(10, 13), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(search(node, attribute_manager, true));
+ {
+ SimpleLocationTerm node(Location(Point{10, 10}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(search(node, attribute_manager, true));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{100, 100}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!search(node, attribute_manager, true));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{13, 13}, 4, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!search(node, attribute_manager, true));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{10, 13}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(search(node, attribute_manager, true));
+ }
}
TEST("require that optimized location search works with wrapped bounding box (no hits)") {
diff --git a/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp b/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
index 5e633dcc97d..3098232b443 100644
--- a/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
+++ b/searchlib/src/tests/attribute/searchable/attributeblueprint_test.cpp
@@ -254,15 +254,22 @@ TEST(AttributeBlueprintTest, require_that_location_terms_work)
{
// 0xcc is z-curve for (10, 10).
auto attribute_manager = makeAttributeManager(int64_t(0xcc));
-
- SimpleLocationTerm node(Location(Point(10, 10), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(do_search(node, attribute_manager, false));
- node = SimpleLocationTerm(Location(Point(100, 100), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(!do_search(node, attribute_manager, false));
- node = SimpleLocationTerm(Location(Point(13, 13), 4, 0), field, 0, Weight(0));
- EXPECT_TRUE(!do_search(node, attribute_manager, false));
- node = SimpleLocationTerm(Location(Point(10, 13), 3, 0), field, 0, Weight(0));
- EXPECT_TRUE(do_search(node, attribute_manager, false));
+ {
+ SimpleLocationTerm node(Location(Point{10, 10}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(do_search(node, attribute_manager, false));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{100, 100}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!do_search(node, attribute_manager, false));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{13, 13}, 4, 0), field, 0, Weight(0));
+ EXPECT_TRUE(!do_search(node, attribute_manager, false));
+ }
+ {
+ SimpleLocationTerm node(Location(Point{10, 13}, 3, 0), field, 0, Weight(0));
+ EXPECT_TRUE(do_search(node, attribute_manager, false));
+ }
}
TEST(AttributeBlueprintTest, require_that_fast_search_location_terms_work)
@@ -270,14 +277,14 @@ TEST(AttributeBlueprintTest, require_that_fast_search_location_terms_work)
// 0xcc is z-curve for (10, 10).
auto attribute_manager = makeFastSearchLongAttribute(int64_t(0xcc));
- SimpleLocationTerm node(Location(Point(10, 10), 3, 0), field, 0, Weight(0));
+ SimpleLocationTerm node(Location(Point{10, 10}, 3, 0), field, 0, Weight(0));
#if 0
EXPECT_TRUE(search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(100, 100), 3, 0),field, 0, Weight(0));
+ node = SimpleLocationTerm(Location(Point{100, 100}, 3, 0),field, 0, Weight(0));
EXPECT_TRUE(!search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(13, 13), 4, 0),field, 0, Weight(0));
+ node = SimpleLocationTerm(Location(Point{13, 13}, 4, 0),field, 0, Weight(0));
EXPECT_TRUE(!search(node, attribute_manager));
- node = SimpleLocationTerm(Location(Point(10, 13), 3, 0),field, 0, Weight(0));
+ node = SimpleLocationTerm(Location(Point{10, 13}, 3, 0),field, 0, Weight(0));
EXPECT_TRUE(search(node, attribute_manager));
#endif
}
diff --git a/searchlib/src/tests/common/location/CMakeLists.txt b/searchlib/src/tests/common/location/CMakeLists.txt
index 64a894096d5..ea0d96529e1 100644
--- a/searchlib/src/tests/common/location/CMakeLists.txt
+++ b/searchlib/src/tests/common/location/CMakeLists.txt
@@ -1,8 +1,9 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(searchlib_location_test_app TEST
+vespa_add_executable(searchlib_geo_location_test_app TEST
SOURCES
- location_test.cpp
+ geo_location_test.cpp
DEPENDS
searchlib
+ GTest::GTest
)
-vespa_add_test(NAME searchlib_location_test_app COMMAND searchlib_location_test_app)
+vespa_add_test(NAME searchlib_geo_location_test_app COMMAND searchlib_geo_location_test_app)
diff --git a/searchlib/src/tests/common/location/geo_location_test.cpp b/searchlib/src/tests/common/location/geo_location_test.cpp
new file mode 100644
index 00000000000..8093ea61697
--- /dev/null
+++ b/searchlib/src/tests/common/location/geo_location_test.cpp
@@ -0,0 +1,136 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <stdio.h>
+#include <vespa/searchlib/common/geo_location.h>
+#include <vespa/searchlib/common/geo_location_spec.h>
+#include <vespa/searchlib/common/geo_location_parser.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using search::common::GeoLocation;
+using search::common::GeoLocationParser;
+
+bool is_parseable(const char *str) {
+ GeoLocationParser parser;
+ return parser.parseOldFormat(str);
+}
+
+GeoLocation parse(const char *str) {
+ GeoLocationParser parser;
+ EXPECT_TRUE(parser.parseOldFormat(str));
+ return parser.getGeoLocation();
+}
+
+TEST(GeoLocationTest, malformed_bounding_boxes_are_not_parseable) {
+ EXPECT_TRUE(is_parseable("[2,10,20,30,40]"));
+ EXPECT_FALSE(is_parseable("[2,10,20,30,40][2,10,20,30,40]"));
+ EXPECT_FALSE(is_parseable("[1,10,20,30,40]"));
+ EXPECT_FALSE(is_parseable("[3,10,20,30,40]"));
+ EXPECT_FALSE(is_parseable("[2, 10, 20, 30, 40]"));
+ EXPECT_FALSE(is_parseable("[2,10,20,30,40"));
+ EXPECT_FALSE(is_parseable("[2,10,20,30]"));
+ EXPECT_FALSE(is_parseable("[10,20,30,40]"));
+}
+
+TEST(GeoLocationTest, malformed_circles_are_not_parseable) {
+ EXPECT_TRUE(is_parseable("(2,10,20,5,0,0,0)"));
+ EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0)(2,10,20,5,0,0,0)"));
+ EXPECT_FALSE(is_parseable("(1,10,20,5,0,0,0)"));
+ EXPECT_FALSE(is_parseable("(3,10,20,5,0,0,0)"));
+ EXPECT_FALSE(is_parseable("(2, 10, 20, 5, 0, 0, 0)"));
+ EXPECT_FALSE(is_parseable("(2,10,20,5)"));
+ EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0"));
+ EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0,1000"));
+ EXPECT_FALSE(is_parseable("(10,20,5)"));
+}
+
+TEST(GeoLocationTest, bounding_boxes_can_be_parsed) {
+ auto loc = parse("[2,10,20,30,40]");
+ EXPECT_EQ(false, loc.has_point);
+ EXPECT_EQ(true, loc.bounding_box.active());
+ EXPECT_EQ(0u, loc.x_aspect.multiplier);
+ EXPECT_EQ(0, loc.point.x);
+ EXPECT_EQ(0, loc.point.y);
+ EXPECT_EQ(std::numeric_limits<uint32_t>::max(), loc.radius);
+ EXPECT_EQ(10, loc.bounding_box.x.low);
+ EXPECT_EQ(20, loc.bounding_box.y.low);
+ EXPECT_EQ(30, loc.bounding_box.x.high);
+ EXPECT_EQ(40, loc.bounding_box.y.high);
+}
+
+TEST(GeoLocationTest, circles_can_be_parsed) {
+ auto loc = parse("(2,10,20,5,0,0,0)");
+ EXPECT_EQ(true, loc.has_point);
+ EXPECT_EQ(true, loc.bounding_box.active());
+ EXPECT_EQ(0u, loc.x_aspect.multiplier);
+ EXPECT_EQ(10, loc.point.x);
+ EXPECT_EQ(20, loc.point.y);
+ EXPECT_EQ(5u, loc.radius);
+ EXPECT_EQ(5, loc.bounding_box.x.low);
+ EXPECT_EQ(15, loc.bounding_box.y.low);
+ EXPECT_EQ(15, loc.bounding_box.x.high);
+ EXPECT_EQ(25, loc.bounding_box.y.high);
+}
+
+TEST(GeoLocationTest, circles_can_have_aspect_ratio) {
+ auto loc = parse("(2,10,20,5,0,0,0,2147483648)");
+ EXPECT_EQ(true, loc.has_point);
+ EXPECT_EQ(true, loc.bounding_box.active());
+ EXPECT_EQ(2147483648u, loc.x_aspect.multiplier);
+ EXPECT_EQ(10, loc.point.x);
+ EXPECT_EQ(20, loc.point.y);
+ EXPECT_EQ(5u, loc.radius);
+ EXPECT_EQ(-1, loc.bounding_box.x.low);
+ EXPECT_EQ(15, loc.bounding_box.y.low);
+ EXPECT_EQ(21, loc.bounding_box.x.high);
+ EXPECT_EQ(25, loc.bounding_box.y.high);
+}
+
+TEST(GeoLocationTest, bounding_box_can_be_specified_after_circle) {
+ auto loc = parse("(2,10,20,5,0,0,0)[2,10,20,30,40]");
+ EXPECT_EQ(true, loc.has_point);
+ EXPECT_EQ(true, loc.bounding_box.active());
+ EXPECT_EQ(0u, loc.x_aspect.multiplier);
+ EXPECT_EQ(10, loc.point.x);
+ EXPECT_EQ(20, loc.point.y);
+ EXPECT_EQ(5u, loc.radius);
+ EXPECT_EQ(10, loc.bounding_box.x.low);
+ EXPECT_EQ(20, loc.bounding_box.y.low);
+ EXPECT_EQ(15, loc.bounding_box.x.high);
+ EXPECT_EQ(25, loc.bounding_box.y.high);
+}
+
+TEST(GeoLocationTest, circles_can_be_specified_after_bounding_box) {
+ auto loc = parse("[2,10,20,30,40](2,10,20,5,0,0,0)");
+ EXPECT_EQ(true, loc.has_point);
+ EXPECT_EQ(true, loc.bounding_box.active());
+ EXPECT_EQ(0u, loc.x_aspect.multiplier);
+ EXPECT_EQ(10, loc.point.x);
+ EXPECT_EQ(20, loc.point.y);
+ EXPECT_EQ(5u, loc.radius);
+ EXPECT_EQ(10, loc.bounding_box.x.low);
+ EXPECT_EQ(20, loc.bounding_box.y.low);
+ EXPECT_EQ(15, loc.bounding_box.x.high);
+ EXPECT_EQ(25, loc.bounding_box.y.high);
+}
+
+TEST(GeoLocationTest, santa_search_gives_non_wrapped_bounding_box) {
+ auto loc = parse("(2,122163600,89998536,290112,4,2000,0,109704)");
+ EXPECT_GE(loc.bounding_box.x.high, loc.bounding_box.x.low);
+ EXPECT_GE(loc.bounding_box.y.high, loc.bounding_box.y.low);
+}
+
+TEST(GeoLocationTest, near_boundary_search_gives_non_wrapped_bounding_box) {
+ auto loc1 = parse("(2,2000000000,2000000000,3000000000,0,1,0)");
+ EXPECT_GE(loc1.bounding_box.x.high, loc1.bounding_box.x.low);
+ EXPECT_GE(loc1.bounding_box.y.high, loc1.bounding_box.y.low);
+ EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high);
+ EXPECT_EQ(std::numeric_limits<int32_t>::max(), loc1.bounding_box.y.high);
+
+ auto loc2 = parse("(2,-2000000000,-2000000000,3000000000,0,1,0)");
+ EXPECT_GE(loc2.bounding_box.x.high, loc2.bounding_box.x.low);
+ EXPECT_GE(loc2.bounding_box.y.high, loc2.bounding_box.y.low);
+ EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.x.low);
+ EXPECT_EQ(std::numeric_limits<int32_t>::min(), loc2.bounding_box.y.low);
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/common/location/location_test.cpp b/searchlib/src/tests/common/location/location_test.cpp
deleted file mode 100644
index d781e5b7275..00000000000
--- a/searchlib/src/tests/common/location/location_test.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/vespalib/testkit/test_kit.h>
-#include <vespa/searchlib/common/location.h>
-#include <vespa/searchlib/attribute/attributeguard.h>
-
-
-using search::common::Location;
-
-bool is_parseable(const char *str) {
- Location loc;
- return loc.parse(str);
-}
-
-Location parse(const char *str) {
- Location loc;
- if (!EXPECT_TRUE(loc.parse(str))) {
- fprintf(stderr, " parse error: %s\n", loc.getParseError());
- }
- return loc;
-}
-
-TEST("require that malformed bounding boxes are not parseable") {
- EXPECT_TRUE(is_parseable("[2,10,20,30,40]"));
- EXPECT_FALSE(is_parseable("[2,10,20,30,40][2,10,20,30,40]"));
- EXPECT_FALSE(is_parseable("[1,10,20,30,40]"));
- EXPECT_FALSE(is_parseable("[3,10,20,30,40]"));
- EXPECT_FALSE(is_parseable("[2, 10, 20, 30, 40]"));
- EXPECT_FALSE(is_parseable("[2,10,20,30,40"));
- EXPECT_FALSE(is_parseable("[2,10,20,30]"));
- EXPECT_FALSE(is_parseable("[10,20,30,40]"));
-}
-
-TEST("require that malformed circles are not parseable") {
- EXPECT_TRUE(is_parseable("(2,10,20,5,0,0,0)"));
- EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0)(2,10,20,5,0,0,0)"));
- EXPECT_FALSE(is_parseable("(1,10,20,5,0,0,0)"));
- EXPECT_FALSE(is_parseable("(3,10,20,5,0,0,0)"));
- EXPECT_FALSE(is_parseable("(2, 10, 20, 5, 0, 0, 0)"));
- EXPECT_FALSE(is_parseable("(2,10,20,5)"));
- EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0"));
- EXPECT_FALSE(is_parseable("(2,10,20,5,0,0,0,1000"));
- EXPECT_FALSE(is_parseable("(10,20,5)"));
-}
-
-TEST("require that bounding boxes can be parsed") {
- Location loc = parse("[2,10,20,30,40]");
- EXPECT_EQUAL(false, loc.getRankOnDistance());
- EXPECT_EQUAL(true, loc.getPruneOnDistance());
- EXPECT_EQUAL(0u, loc.getXAspect());
- EXPECT_EQUAL(0, loc.getX());
- EXPECT_EQUAL(0, loc.getY());
- EXPECT_EQUAL(std::numeric_limits<uint32_t>::max(), loc.getRadius());
- EXPECT_EQUAL(10, loc.getMinX());
- EXPECT_EQUAL(20, loc.getMinY());
- EXPECT_EQUAL(30, loc.getMaxX());
- EXPECT_EQUAL(40, loc.getMaxY());
-}
-
-TEST("require that circles can be parsed") {
- Location loc = parse("(2,10,20,5,0,0,0)");
- EXPECT_EQUAL(true, loc.getRankOnDistance());
- EXPECT_EQUAL(true, loc.getPruneOnDistance());
- EXPECT_EQUAL(0u, loc.getXAspect());
- EXPECT_EQUAL(10, loc.getX());
- EXPECT_EQUAL(20, loc.getY());
- EXPECT_EQUAL(5u, loc.getRadius());
- EXPECT_EQUAL(5, loc.getMinX());
- EXPECT_EQUAL(15, loc.getMinY());
- EXPECT_EQUAL(15, loc.getMaxX());
- EXPECT_EQUAL(25, loc.getMaxY());
-}
-
-TEST("require that circles can have aspect ratio") {
- Location loc = parse("(2,10,20,5,0,0,0,2147483648)");
- EXPECT_EQUAL(true, loc.getRankOnDistance());
- EXPECT_EQUAL(true, loc.getPruneOnDistance());
- EXPECT_EQUAL(2147483648u, loc.getXAspect());
- EXPECT_EQUAL(10, loc.getX());
- EXPECT_EQUAL(20, loc.getY());
- EXPECT_EQUAL(5u, loc.getRadius());
- EXPECT_EQUAL(-1, loc.getMinX());
- EXPECT_EQUAL(15, loc.getMinY());
- EXPECT_EQUAL(21, loc.getMaxX());
- EXPECT_EQUAL(25, loc.getMaxY());
-}
-
-TEST("require that bounding box can be specified after circle") {
- Location loc = parse("(2,10,20,5,0,0,0)[2,10,20,30,40]");
- EXPECT_EQUAL(true, loc.getRankOnDistance());
- EXPECT_EQUAL(true, loc.getPruneOnDistance());
- EXPECT_EQUAL(0u, loc.getXAspect());
- EXPECT_EQUAL(10, loc.getX());
- EXPECT_EQUAL(20, loc.getY());
- EXPECT_EQUAL(5u, loc.getRadius());
- EXPECT_EQUAL(10, loc.getMinX());
- EXPECT_EQUAL(20, loc.getMinY());
- EXPECT_EQUAL(15, loc.getMaxX());
- EXPECT_EQUAL(25, loc.getMaxY());
-}
-
-TEST("require that circles can be specified after bounding box") {
- Location loc = parse("[2,10,20,30,40](2,10,20,5,0,0,0)");
- EXPECT_EQUAL(true, loc.getRankOnDistance());
- EXPECT_EQUAL(true, loc.getPruneOnDistance());
- EXPECT_EQUAL(0u, loc.getXAspect());
- EXPECT_EQUAL(10, loc.getX());
- EXPECT_EQUAL(20, loc.getY());
- EXPECT_EQUAL(5u, loc.getRadius());
- EXPECT_EQUAL(10, loc.getMinX());
- EXPECT_EQUAL(20, loc.getMinY());
- EXPECT_EQUAL(15, loc.getMaxX());
- EXPECT_EQUAL(25, loc.getMaxY());
-}
-
-TEST("require that santa search gives non-wrapped bounding box") {
- Location loc = parse("(2,122163600,89998536,290112,4,2000,0,109704)");
- EXPECT_GREATER_EQUAL(loc.getMaxX(), loc.getMinX());
- EXPECT_GREATER_EQUAL(loc.getMaxY(), loc.getMinY());
-}
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchlib/src/tests/query/query_visitor_test.cpp b/searchlib/src/tests/query/query_visitor_test.cpp
index edbc29be784..8441dc2227f 100644
--- a/searchlib/src/tests/query/query_visitor_test.cpp
+++ b/searchlib/src/tests/query/query_visitor_test.cpp
@@ -90,7 +90,7 @@ void Test::requireThatAllNodesCanBeVisited() {
checkVisit<WandTerm>(new SimpleWandTerm("field", 0, Weight(42), 57, 67, 77.7));
checkVisit<Rank>(new SimpleRank);
checkVisit<NumberTerm>(new SimpleNumberTerm("0.42", "field", 0, Weight(0)));
- const Location location(Point(10, 10), 20, 0);
+ const Location location(Point{10, 10}, 20, 0);
checkVisit<LocationTerm>(new SimpleLocationTerm(location, "field", 0, Weight(0)));
checkVisit<PrefixTerm>(new SimplePrefixTerm("t", "field", 0, Weight(0)));
checkVisit<RangeTerm>(new SimpleRangeTerm(Range(0, 1), "field", 0, Weight(0)));
diff --git a/searchlib/src/tests/query/querybuilder_test.cpp b/searchlib/src/tests/query/querybuilder_test.cpp
index d093bc4242e..269600d26d4 100644
--- a/searchlib/src/tests/query/querybuilder_test.cpp
+++ b/searchlib/src/tests/query/querybuilder_test.cpp
@@ -32,7 +32,7 @@ const size_t distance = 4;
const string int1 = "42";
const string float1 = "3.14";
const Range range(32, 64);
-const Point position(100, 100);
+const Point position{100, 100};
const int max_distance = 20;
const uint32_t x_aspect = 0;
const Location location(position, max_distance, x_aspect);
diff --git a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
index 4a34a07a773..cb587d77133 100644
--- a/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
+++ b/searchlib/src/vespa/searchlib/attribute/attribute_blueprint_factory.cpp
@@ -248,11 +248,14 @@ public:
LocationPostFilterBlueprint(const FieldSpec &field, const IAttributeVector &attribute, const Location &loc)
: ComplexLeafBlueprint(field),
_attribute(attribute),
- _location()
+ _location(loc)
{
- _location.setVec(attribute);
- _location.parse(loc.getLocationString());
- uint32_t estHits = _attribute.getNumDocs();
+ uint32_t estHits = 0;
+ if (loc.valid()) {
+ _location.setVec(attribute);
+ estHits = _attribute.getNumDocs();
+ }
+ LOG(debug, "location %s in attribute with numdocs %u", loc.getOldFormatString().c_str(), estHits);
HitEstimate estimate(estHits, estHits == 0);
setEstimate(estimate);
}
@@ -272,14 +275,16 @@ Blueprint::UP
make_location_blueprint(const FieldSpec &field, const IAttributeVector &attribute, const Location &loc) {
auto post_filter = std::make_unique<LocationPostFilterBlueprint>(field, attribute, loc);
const common::Location &location = post_filter->location();
- if (location.getMinX() > location.getMaxX() ||
- location.getMinY() > location.getMaxY())
+ if (location.bounding_box.x.low > location.bounding_box.x.high ||
+ location.bounding_box.y.low > location.bounding_box.y.high)
{
return std::make_unique<queryeval::EmptyBlueprint>(field);
}
ZCurve::RangeVector rangeVector = ZCurve::find_ranges(
- location.getMinX(), location.getMinY(),
- location.getMaxX(), location.getMaxY());
+ location.bounding_box.x.low,
+ location.bounding_box.y.low,
+ location.bounding_box.x.high,
+ location.bounding_box.y.high);
auto pre_filter = std::make_unique<LocationPreFilterBlueprint>(field, attribute, rangeVector);
if (!pre_filter->should_use()) {
return post_filter;
diff --git a/searchlib/src/vespa/searchlib/common/CMakeLists.txt b/searchlib/src/vespa/searchlib/common/CMakeLists.txt
index 5d30260a169..7e1cfd8fec5 100644
--- a/searchlib/src/vespa/searchlib/common/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/common/CMakeLists.txt
@@ -12,6 +12,9 @@ vespa_add_library(searchlib_common OBJECT
featureset.cpp
fileheadercontext.cpp
gatecallback.cpp
+ geo_location.cpp
+ geo_location_spec.cpp
+ geo_location_parser.cpp
growablebitvector.cpp
indexmetainfo.cpp
location.cpp
diff --git a/searchlib/src/vespa/searchlib/common/documentlocations.cpp b/searchlib/src/vespa/searchlib/common/documentlocations.cpp
index b03176f0ad2..b8f05581b41 100644
--- a/searchlib/src/vespa/searchlib/common/documentlocations.cpp
+++ b/searchlib/src/vespa/searchlib/common/documentlocations.cpp
@@ -21,5 +21,9 @@ DocumentLocations::setVecGuard(std::unique_ptr<search::AttributeGuard> guard) {
setVec(*_vec_guard.get()->get());
}
+DocumentLocations::DocumentLocations(DocumentLocations &&) = default;
+DocumentLocations & DocumentLocations::operator = (DocumentLocations &&) = default;
+
+
} // namespace common
} // namespace search
diff --git a/searchlib/src/vespa/searchlib/common/documentlocations.h b/searchlib/src/vespa/searchlib/common/documentlocations.h
index 1dab68ca11f..51d5be76e65 100644
--- a/searchlib/src/vespa/searchlib/common/documentlocations.h
+++ b/searchlib/src/vespa/searchlib/common/documentlocations.h
@@ -25,8 +25,8 @@ private:
const search::attribute::IAttributeVector *_vec;
public:
- DocumentLocations(DocumentLocations &&) = default;
- DocumentLocations & operator = (DocumentLocations &&) = default;
+ DocumentLocations(DocumentLocations &&);
+ DocumentLocations & operator = (DocumentLocations &&);
DocumentLocations();
virtual ~DocumentLocations();
diff --git a/searchlib/src/vespa/searchlib/common/geo_location.cpp b/searchlib/src/vespa/searchlib/common/geo_location.cpp
new file mode 100644
index 00000000000..6dd7b83ae37
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location.cpp
@@ -0,0 +1,184 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "geo_location.h"
+
+using vespalib::geo::ZCurve;
+
+namespace search::common {
+
+namespace {
+
+ZCurve::BoundingBox to_z(GeoLocation::Box box) {
+ return ZCurve::BoundingBox(box.x.low, box.x.high,
+ box.y.low, box.y.high);
+}
+
+GeoLocation::Box
+adjust_bounding_box(GeoLocation::Box orig, GeoLocation::Point point, uint32_t radius, GeoLocation::Aspect x_aspect)
+{
+ if (radius == GeoLocation::radius_inf) {
+ // only happens if GeoLocation is explicitly constructed with "infinite" radius
+ return orig;
+ }
+ uint32_t maxdx = radius;
+ if (x_aspect.active()) {
+ // x_aspect is a 32-bit fixed-point number in range [0,1]
+ // so this implements maxdx = ceil(radius/x_aspect)
+ uint64_t maxdx2 = ((static_cast<uint64_t>(radius) << 32) + 0xffffffffu) / x_aspect.multiplier;
+ if (maxdx2 >= 0xffffffffu) {
+ maxdx = 0xffffffffu;
+ } else {
+ maxdx = static_cast<uint32_t>(maxdx2);
+ }
+ }
+ // implied limits from radius and point:
+ int64_t implied_max_x = int64_t(point.x) + int64_t(maxdx);
+ int64_t implied_min_x = int64_t(point.x) - int64_t(maxdx);
+
+ int64_t implied_max_y = int64_t(point.y) + int64_t(radius);
+ int64_t implied_min_y = int64_t(point.y) - int64_t(radius);
+
+ int32_t max_x = orig.x.high;
+ int32_t min_x = orig.x.low;
+
+ int32_t max_y = orig.y.high;
+ int32_t min_y = orig.y.low;
+
+ if (implied_max_x < max_x) max_x = implied_max_x;
+ if (implied_min_x > min_x) min_x = implied_min_x;
+
+ if (implied_max_y < max_y) max_y = implied_max_y;
+ if (implied_min_y > min_y) min_y = implied_min_y;
+
+ return GeoLocation::Box{GeoLocation::Range{min_x, max_x},
+ GeoLocation::Range{min_y, max_y}};
+}
+
+} // namespace <unnamed>
+
+GeoLocation::GeoLocation()
+ : has_point(false),
+ point{0, 0},
+ radius(radius_inf),
+ x_aspect(),
+ bounding_box(no_box),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(0,0,0,0)
+{}
+
+GeoLocation::GeoLocation(Point p)
+ : has_point(true),
+ point(p),
+ radius(radius_inf),
+ x_aspect(),
+ bounding_box(no_box),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(0,0,0,0)
+{}
+
+GeoLocation::GeoLocation(Point p, Aspect xa)
+ : has_point(true),
+ point(p),
+ radius(radius_inf),
+ x_aspect(xa),
+ bounding_box(no_box),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(0,0,0,0)
+{}
+
+GeoLocation::GeoLocation(Point p, uint32_t r)
+ : has_point(true),
+ point(p),
+ radius(r),
+ x_aspect(),
+ bounding_box(adjust_bounding_box(no_box, p, r, Aspect())),
+ _sq_radius(uint64_t(r) * uint64_t(r)),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Point p, uint32_t r, Aspect xa)
+ : has_point(true),
+ point(p),
+ radius(r),
+ x_aspect(xa),
+ bounding_box(adjust_bounding_box(no_box, p, r, xa)),
+ _sq_radius(uint64_t(r) * uint64_t(r)),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Box b)
+ : has_point(false),
+ point{0, 0},
+ radius(radius_inf),
+ x_aspect(),
+ bounding_box(b),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Box b, Point p)
+ : has_point(true),
+ point(p),
+ radius(radius_inf),
+ x_aspect(),
+ bounding_box(b),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Box b, Point p, Aspect xa)
+ : has_point(true),
+ point(p),
+ radius(radius_inf),
+ x_aspect(xa),
+ bounding_box(b),
+ _sq_radius(sq_radius_inf),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Box b, Point p, uint32_t r)
+ : has_point(true),
+ point(p),
+ radius(r),
+ x_aspect(),
+ bounding_box(adjust_bounding_box(b, p, r, Aspect())),
+ _sq_radius(uint64_t(r) * uint64_t(r)),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+GeoLocation::GeoLocation(Box b, Point p, uint32_t r, Aspect xa)
+ : has_point(true),
+ point(p),
+ radius(r),
+ x_aspect(xa),
+ bounding_box(adjust_bounding_box(b, p, r, xa)),
+ _sq_radius(uint64_t(r) * uint64_t(r)),
+ _z_bounding_box(to_z(bounding_box))
+{}
+
+uint64_t GeoLocation::sq_distance_to(Point p) const {
+ if (has_point) {
+ uint64_t dx = (p.x > point.x) ? (p.x - point.x) : (point.x - p.x);
+ if (x_aspect.active()) {
+ // x_aspect is a 32-bit fixed-point number in range [0,1]
+ // this implements dx = (dx * x_aspect)
+ dx = (dx * x_aspect.multiplier) >> 32;
+ }
+ uint64_t dy = (p.y > point.y) ? (p.y - point.y) : (point.y - p.y);
+ return dx*dx + dy*dy;
+ }
+ return 0;
+}
+
+bool GeoLocation::inside_limit(Point p) const {
+ if (p.x < bounding_box.x.low) return false;
+ if (p.x > bounding_box.x.high) return false;
+
+ if (p.y < bounding_box.y.low) return false;
+ if (p.y > bounding_box.y.high) return false;
+
+ uint64_t sq_dist = sq_distance_to(p);
+ return sq_dist <= _sq_radius;
+}
+
+} // namespace search::common
diff --git a/searchlib/src/vespa/searchlib/common/geo_location.h b/searchlib/src/vespa/searchlib/common/geo_location.h
new file mode 100644
index 00000000000..5d04a09142a
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location.h
@@ -0,0 +1,90 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <string>
+#include <cstdint>
+#include <limits>
+#include <vespa/vespalib/geo/zcurve.h>
+
+namespace search::common {
+
+/**
+ * An immutable struct for a (geo) location.
+ * Contains a point with optional radius, a bounding box, or both.
+ **/
+struct GeoLocation
+{
+ // contained structs and helper constants:
+ static constexpr int32_t range_low = std::numeric_limits<int32_t>::min();
+ static constexpr int32_t range_high = std::numeric_limits<int32_t>::max();
+ static constexpr uint32_t radius_inf = std::numeric_limits<uint32_t>::max();
+ struct Point {
+ const int32_t x;
+ const int32_t y;
+ Point() = delete;
+ };
+ struct Aspect {
+ uint32_t multiplier;
+ Aspect() : multiplier(0) {}
+ Aspect(uint32_t multiplier_in) : multiplier(multiplier_in) {}
+ bool active() const { return multiplier != 0; }
+ };
+ struct Range {
+ const int32_t low;
+ const int32_t high;
+ bool active() const {
+ return (low != range_low) || (high != range_high);
+ }
+ };
+ static constexpr Range no_range = {range_low, range_high};
+ struct Box {
+ const Range x;
+ const Range y;
+ bool active() const { return x.active() || y.active(); }
+ };
+ static constexpr Box no_box = {no_range, no_range};
+
+ // actual content of struct:
+ const bool has_point;
+ Point point;
+ uint32_t radius;
+ Aspect x_aspect;
+ Box bounding_box;
+ GeoLocation();
+
+ // constructors:
+ GeoLocation(Point p);
+ GeoLocation(Point p, Aspect xa);
+ GeoLocation(Point p, uint32_t r);
+ GeoLocation(Point p, uint32_t r, Aspect xa);
+ GeoLocation(Box b);
+ GeoLocation(Box b, Point p);
+ GeoLocation(Box b, Point p, Aspect xa);
+ GeoLocation(Box b, Point p, uint32_t r);
+ GeoLocation(Box b, Point p, uint32_t r, Aspect xa);
+
+ // helper methods:
+ bool has_radius() const { return radius != radius_inf; }
+ bool valid() const { return has_point || bounding_box.active(); }
+ bool can_limit() const { return bounding_box.active(); }
+
+ uint64_t sq_distance_to(Point p) const;
+ bool inside_limit(Point p) const;
+
+ bool inside_limit(int64_t zcurve_encoded_xy) const {
+ if (_z_bounding_box.getzFailBoundingBoxTest(zcurve_encoded_xy)) return false;
+ int32_t x = 0;
+ int32_t y = 0;
+ vespalib::geo::ZCurve::decode(zcurve_encoded_xy, &x, &y);
+ return inside_limit(Point{x, y});
+ }
+
+private:
+ // constants for implementation of helper methods:
+ static constexpr uint64_t sq_radius_inf = std::numeric_limits<uint64_t>::max();
+ const uint64_t _sq_radius;
+ const vespalib::geo::ZCurve::BoundingBox _z_bounding_box;
+};
+
+} // namespace
diff --git a/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp b/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp
new file mode 100644
index 00000000000..05c53348699
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location_parser.cpp
@@ -0,0 +1,209 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "geo_location_parser.h"
+#include <limits>
+#include <vespa/vespalib/stllike/asciistream.h>
+
+namespace {
+
+int getInt(const char * &p) {
+ int val;
+ bool isminus;
+ val = 0;
+ isminus = false;
+ if (*p == '-') {
+ isminus = true;
+ p++;
+ }
+ while (*p >= '0' && *p <= '9') {
+ val *= 10;
+ val += (*p++ - '0');
+ }
+ return isminus ? - val : val;
+}
+
+} // namespace <unnamed>
+
+namespace search::common {
+
+GeoLocationParser::GeoLocationParser()
+ : _valid(false),
+ _has_point(false),
+ _has_bounding_box(false),
+ _field_name(),
+ _x(0),
+ _y(0),
+ _x_aspect(0u),
+ _radius(std::numeric_limits<uint32_t>::max()),
+ _min_x(std::numeric_limits<int32_t>::min()),
+ _max_x(std::numeric_limits<int32_t>::max()),
+ _min_y(std::numeric_limits<int32_t>::min()),
+ _max_y(std::numeric_limits<int32_t>::max()),
+ _parseError(NULL)
+{}
+
+bool
+GeoLocationParser::correctDimensionalitySkip(const char * &p) {
+ if (*p == '2') {
+ p++;
+ if (*p != ',') {
+ _parseError = "Missing comma after 2D dimensionality";
+ return false;
+ }
+ p++;
+ return true;
+ }
+ _parseError = "Bad dimensionality spec, not 2D";
+ return false;
+}
+
+bool
+GeoLocationParser::parseOldFormatWithField(const std::string &str)
+{
+ auto sep = str.find(':');
+ if (sep == std::string::npos) {
+ _parseError = "Location string lacks field specification.";
+ return false;
+ }
+ _field_name = str.substr(0, sep);
+ std::string only_loc = str.substr(sep + 1);
+ return parseOldFormat(only_loc);
+}
+
+bool
+GeoLocationParser::parseOldFormat(const std::string &locStr)
+{
+ bool foundBoundingBox = false;
+ bool foundLoc = false;
+ const char *p = locStr.c_str();
+ while (*p != '\0') {
+ if (*p == '[') {
+ p++;
+ if (foundBoundingBox) {
+ _parseError = "Duplicate bounding box";
+ return false;
+ }
+ foundBoundingBox = true;
+ if (!correctDimensionalitySkip(p)) {
+ return false;
+ }
+ _min_x = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after minx";
+ return false;
+ }
+ p++;
+ _min_y = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after miny";
+ return false;
+ }
+ p++;
+ _max_x = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after maxx";
+ return false;
+ }
+ p++;
+ _max_y = getInt(p);
+ if (*p != ']') {
+ _parseError = "Missing ']' after maxy";
+ return false;
+ }
+ p++;
+ } else if (*p == '(') {
+ p++;
+ if (foundLoc) {
+ _parseError = "Duplicate location";
+ return false;
+ }
+ foundLoc = true;
+ if (!correctDimensionalitySkip(p)) {
+ return false;
+ }
+ _x = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after x position";
+ return false;
+ }
+ p++;
+ _y = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after y position";
+ return false;
+ }
+ p++;
+ _radius = getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after radius";
+ return false;
+ }
+ p++;
+ /* _tableID = */ (void) getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after tableID";
+ return false;
+ }
+ p++;
+ /* _rankMultiplier = */ (void) getInt(p);
+ if (*p != ',') {
+ _parseError = "Missing ',' after rank multiplier";
+ return false;
+ }
+ p++;
+ /* _rankOnlyOnDistance = */ (void) getInt(p);
+ if (*p == ',') {
+ p++;
+ _x_aspect = getInt(p);
+ if (*p != ')') {
+ _parseError = "Missing ')' after xAspect";
+ return false;
+ }
+ } else {
+ if (*p != ')') {
+ _parseError = "Missing ')' after rankOnlyOnDistance flag";
+ return false;
+ }
+ }
+ p++;
+ } else if (*p == ' ') {
+ p++;
+ } else {
+ _parseError = "Unexpected char in location spec";
+ return false;
+ }
+ }
+ _has_point = foundLoc;
+ _has_bounding_box = foundBoundingBox;
+ _valid = (_has_point || _has_bounding_box);
+ return _valid;
+}
+
+GeoLocation
+GeoLocationParser::getGeoLocation() const
+{
+ GeoLocation::Aspect aspect(_x_aspect);
+ if (_has_bounding_box) {
+ GeoLocation::Range x_range{_min_x, _max_x};
+ GeoLocation::Range y_range{_min_y, _max_y};
+ GeoLocation::Box bounding_box{x_range, y_range};
+ if (_has_point) {
+ GeoLocation::Point point{_x, _y};
+ if (_radius == GeoLocation::radius_inf) {
+ return GeoLocation(bounding_box, point, aspect);
+ }
+ return GeoLocation(bounding_box, point, _radius, aspect);
+ }
+ return GeoLocation(bounding_box);
+ }
+ if (_has_point) {
+ GeoLocation::Point point{_x, _y};
+ if (_radius == GeoLocation::radius_inf) {
+ return GeoLocation(point, aspect);
+ }
+ return GeoLocation(point, _radius, aspect);
+ }
+ return GeoLocation();
+}
+
+} // namespace
diff --git a/searchlib/src/vespa/searchlib/common/geo_location_parser.h b/searchlib/src/vespa/searchlib/common/geo_location_parser.h
new file mode 100644
index 00000000000..8936a620d21
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location_parser.h
@@ -0,0 +1,47 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <string>
+#include <cstdint>
+#include "geo_location.h"
+#include "geo_location_spec.h"
+
+namespace search::common {
+
+/**
+ * Parser for a geo-location string representation.
+ **/
+class GeoLocationParser
+{
+public:
+ GeoLocationParser();
+
+ bool parseOldFormat(const std::string &locStr);
+ bool parseOldFormatWithField(const std::string &str);
+
+ std::string getFieldName() const { return _field_name; }
+ GeoLocation getGeoLocation() const;
+
+ const char * getParseError() const { return _parseError; }
+private:
+ bool _valid;
+ bool _has_point;
+ bool _has_bounding_box;
+
+ std::string _field_name;
+
+ int32_t _x; /* Query X position */
+ int32_t _y; /* Query Y position */
+ uint32_t _x_aspect; /* X distance multiplier fraction */
+ uint32_t _radius; /* Radius for euclidean distance */
+ int32_t _min_x; /* Min X coordinate */
+ int32_t _max_x; /* Max X coordinate */
+ int32_t _min_y; /* Min Y coordinate */
+ int32_t _max_y; /* Max Y coordinate */
+
+ const char *_parseError;
+ bool correctDimensionalitySkip(const char * &p);
+};
+
+} // namespace
diff --git a/searchlib/src/vespa/searchlib/common/geo_location_spec.cpp b/searchlib/src/vespa/searchlib/common/geo_location_spec.cpp
new file mode 100644
index 00000000000..271946e2df6
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location_spec.cpp
@@ -0,0 +1,3 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "geo_location_spec.h"
diff --git a/searchlib/src/vespa/searchlib/common/geo_location_spec.h b/searchlib/src/vespa/searchlib/common/geo_location_spec.h
new file mode 100644
index 00000000000..42c2b8e6c8c
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/common/geo_location_spec.h
@@ -0,0 +1,21 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <string>
+#include <cstdint>
+#include "geo_location.h"
+
+namespace search::common {
+
+/**
+ * Immutable specification of a geo-location query item.
+ **/
+struct GeoLocationSpec
+{
+public:
+ const std::string field_name;
+ const GeoLocation location;
+};
+
+} // namespace
diff --git a/searchlib/src/vespa/searchlib/common/location.cpp b/searchlib/src/vespa/searchlib/common/location.cpp
index 6927d9ab6cb..171dcecaa33 100644
--- a/searchlib/src/vespa/searchlib/common/location.cpp
+++ b/searchlib/src/vespa/searchlib/common/location.cpp
@@ -5,198 +5,6 @@
namespace search::common {
-Location::Location() :
- _zBoundingBox(0,0,0,0),
- _x(0),
- _y(0),
- _xAspect(0u),
- _radius(std::numeric_limits<uint32_t>::max()),
- _minx(std::numeric_limits<int32_t>::min()),
- _maxx(std::numeric_limits<int32_t>::max()),
- _miny(std::numeric_limits<int32_t>::min()),
- _maxy(std::numeric_limits<int32_t>::max()),
- _rankOnDistance(false),
- _pruneOnDistance(false),
- _parseError(NULL)
-{
-}
+Location::Location(const GeoLocation &from) : GeoLocation(from) {}
-
-bool
-Location::getDimensionality(const char **pp)
-{
- if (**pp == '2') {
- (*pp)++;
- if (**pp != ',') {
- _parseError = "Missing comma after 2D dimensionality";
- return false;
- }
- (*pp)++;
- return true;
- }
- _parseError = "Bad dimensionality spec, not 2D";
- return false;
-}
-
-
-int
-Location::getInt(const char **pp)
-{
- const char *p = *pp;
- int val;
- bool isminus;
-
- val = 0;
- isminus = false;
- if (*p == '-') {
- isminus = true;
- p++;
- }
- while (*p >= '0' && *p <= '9')
- val = val * 10 + *p++ - '0';
- *pp = p;
- return isminus ? - val : val;
-}
-
-bool Location::parse(const vespalib::string &locStr)
-{
- bool hadCutoff = false;
- bool hadLoc = false;
- const char *p = locStr.c_str();
- while (*p != '\0') {
- if (*p == '[') {
- p++;
- if (hadCutoff) {
- _parseError = "Duplicate square cutoff";
- return false;
- }
- hadCutoff = true;
- if (!getDimensionality(&p))
- return false;
- _minx = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after minx";
- return false;
- }
- p++;
- _miny = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after miny";
- return false;
- }
- p++;
- _maxx = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after maxx";
- return false;
- }
- p++;
- _maxy = getInt(&p);
- if (*p != ']') {
- _parseError = "Missing ']' after maxy";
- return false;
- }
- p++;
- } else if (*p == '(') {
- p++;
- if (hadLoc) {
- _parseError = "Duplicate location";
- return false;
- }
- hadLoc = true;
- if (!getDimensionality(&p))
- return false;
- _x = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after x position";
- return false;
- }
- p++;
- _y = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after y position";
- return false;
- }
- p++;
- _radius = getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after radius";
- return false;
- }
- p++;
- /* _tableID = */ (void) getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after tableID";
- return false;
- }
- p++;
- /* _rankMultiplier = */ (void) getInt(&p);
- if (*p != ',') {
- _parseError = "Missing ',' after rank multiplier";
- return false;
- }
- p++;
- /* _rankOnlyOnDistance = */ (void) (getInt(&p) != 0);
- if (*p == ',') {
- p++;
- _xAspect = getInt(&p);
- if (*p != ')') {
- _parseError = "Missing ')' after xAspect";
- return false;
- }
- } else {
- if (*p != ')') {
- _parseError = "Missing ')' after rankOnlyOnDistance flag";
- return false;
- }
- }
- p++;
- } else if (*p == ' ')
- p++;
- else {
- _parseError = "Unexpected char in location spec";
- return false;
- }
- }
-
- if (hadLoc) {
- _rankOnDistance = true;
- uint32_t maxdx = _radius;
- if (_xAspect != 0) {
- uint64_t maxdx2 = ((static_cast<uint64_t>(_radius) << 32) + 0xffffffffu) /
- _xAspect;
- if (maxdx2 >= 0xffffffffu)
- maxdx = 0xffffffffu;
- else
- maxdx = static_cast<uint32_t>(maxdx2);
- }
- if (static_cast<int32_t>(_x - maxdx) > _minx &&
- static_cast<int64_t>(_x) - static_cast<int64_t>(maxdx) >
- static_cast<int64_t>(_minx))
- _minx = _x - maxdx;
- if (static_cast<int32_t>(_x + maxdx) < _maxx &&
- static_cast<int64_t>(_x) + static_cast<int64_t>(maxdx) <
- static_cast<int64_t>(_maxx))
- _maxx = _x + maxdx;
- if (static_cast<int32_t>(_y - _radius) > _miny &&
- static_cast<int64_t>(_y) - static_cast<int64_t>(_radius) >
- static_cast<int64_t>(_miny))
- _miny = _y - _radius;
- if (static_cast<int32_t>(_y + _radius) < _maxy &&
- static_cast<int64_t>(_y) + static_cast<int64_t>(_radius) <
- static_cast<int64_t>(_maxy))
- _maxy = _y + _radius;
- }
- if (_minx != std::numeric_limits<int32_t>::min() ||
- _maxx != std::numeric_limits<int32_t>::max() ||
- _miny != std::numeric_limits<int32_t>::min() ||
- _maxy != std::numeric_limits<int32_t>::max())
- {
- _pruneOnDistance = true;
- }
- _zBoundingBox = vespalib::geo::ZCurve::BoundingBox(_minx, _maxx, _miny, _maxy);
-
- return true;
-}
-
-}
+} // namespace
diff --git a/searchlib/src/vespa/searchlib/common/location.h b/searchlib/src/vespa/searchlib/common/location.h
index a00bb83648a..197f92326cd 100644
--- a/searchlib/src/vespa/searchlib/common/location.h
+++ b/searchlib/src/vespa/searchlib/common/location.h
@@ -3,51 +3,19 @@
#pragma once
#include "documentlocations.h"
-#include <vespa/vespalib/geo/zcurve.h>
-
-#include <vespa/vespalib/stllike/string.h>
+#include "geo_location.h"
namespace search::common {
-class Location : public DocumentLocations
+class Location : public DocumentLocations,
+ public GeoLocation
{
-private:
- static int getInt(const char **pp);
- bool getDimensionality(const char **pp);
-
public:
- Location();
- bool getRankOnDistance() const { return _rankOnDistance; }
- bool getPruneOnDistance() const { return _pruneOnDistance; }
- uint32_t getXAspect() const { return _xAspect; }
- int32_t getX() const { return _x; }
- int32_t getY() const { return _y; }
- uint32_t getRadius() const { return _radius; }
- const char * getParseError() const { return _parseError; }
- int32_t getMinX() const { return _minx; }
- int32_t getMinY() const { return _miny; }
- int32_t getMaxX() const { return _maxx; }
- int32_t getMaxY() const { return _maxy; }
- bool getzFailBoundingBoxTest(int64_t docxy) const {
- return _zBoundingBox.getzFailBoundingBoxTest(docxy);
- }
-
- bool parse(const vespalib::string &locStr);
-
-private:
- vespalib::geo::ZCurve::BoundingBox _zBoundingBox;
- int32_t _x; /* Query X position */
- int32_t _y; /* Query Y position */
- uint32_t _xAspect; /* X distance multiplier fraction */
- uint32_t _radius; /* Radius for euclidean distance */
- int32_t _minx; /* Min X coordinate */
- int32_t _maxx; /* Max X coordinate */
- int32_t _miny; /* Min Y coordinate */
- int32_t _maxy; /* Max Y coordinate */
-
- bool _rankOnDistance;
- bool _pruneOnDistance;
- const char *_parseError;
+ Location(const GeoLocation& from);
+ ~Location() {}
+ Location(Location &&) = default;
+ bool getRankOnDistance() const { return has_point; }
+ bool getPruneOnDistance() const { return can_limit(); }
};
}
diff --git a/searchlib/src/vespa/searchlib/common/locationiterators.cpp b/searchlib/src/vespa/searchlib/common/locationiterators.cpp
index 16e465bcd05..d90ed3b41f3 100644
--- a/searchlib/src/vespa/searchlib/common/locationiterators.cpp
+++ b/searchlib/src/vespa/searchlib/common/locationiterators.cpp
@@ -4,6 +4,9 @@
#include <vespa/searchlib/bitcompression/compression.h>
#include <vespa/searchlib/attribute/attributevector.h>
+#include <vespa/log/log.h>
+LOG_SETUP(".searchlib.common.locationiterators");
+
using namespace search::common;
class FastS_2DZLocationIterator : public search::queryeval::SearchIterator
@@ -11,7 +14,6 @@ class FastS_2DZLocationIterator : public search::queryeval::SearchIterator
private:
const unsigned int _numDocs;
const bool _strict;
- const uint64_t _radius2;
const Location & _location;
std::vector<search::AttributeVector::largeint_t> _pos;
@@ -31,7 +33,6 @@ FastS_2DZLocationIterator(unsigned int numDocs,
: SearchIterator(),
_numDocs(numDocs),
_strict(strict),
- _radius2(static_cast<uint64_t>(location.getRadius()) * location.getRadius()),
_location(location),
_pos()
{
@@ -45,6 +46,8 @@ FastS_2DZLocationIterator::~FastS_2DZLocationIterator() = default;
void
FastS_2DZLocationIterator::doSeek(uint32_t docId)
{
+ LOG(debug, "FastS_2DZLocationIterator: seek(%u) with numDocs=%u endId=%u",
+ docId, _numDocs, getEndId());
if (__builtin_expect(docId >= _numDocs, false)) {
setAtEnd();
return;
@@ -62,24 +65,9 @@ FastS_2DZLocationIterator::doSeek(uint32_t docId)
}
for (uint32_t i = 0; i < numValues; i++) {
int64_t docxy(pos[i]);
- if ( ! location.getzFailBoundingBoxTest(docxy)) {
- int32_t docx = 0;
- int32_t docy = 0;
- vespalib::geo::ZCurve::decode(docxy, &docx, &docy);
- uint32_t dx = (location.getX() > docx)
- ? location.getX() - docx
- : docx - location.getX();
- if (location.getXAspect() != 0)
- dx = ((uint64_t) dx * location.getXAspect()) >> 32;
-
- uint32_t dy = (location.getY() > docy)
- ? location.getY() - docy
- : docy - location.getY();
- uint64_t dist2 = (uint64_t) dx * dx + (uint64_t) dy * dy;
- if (dist2 <= _radius2) {
- setDocId(docId);
- return;
- }
+ if (location.inside_limit(docxy)) {
+ setDocId(docId);
+ return;
}
}
diff --git a/searchlib/src/vespa/searchlib/features/distancefeature.cpp b/searchlib/src/vespa/searchlib/features/distancefeature.cpp
index 7a624e64d67..bd2c7becc81 100644
--- a/searchlib/src/vespa/searchlib/features/distancefeature.cpp
+++ b/searchlib/src/vespa/searchlib/features/distancefeature.cpp
@@ -82,7 +82,8 @@ ConvertRawscoreToDistance::execute(uint32_t docId)
feature_t
DistanceExecutor::calculateDistance(uint32_t docId)
{
- if (_location.isValid() && _pos != nullptr) {
+ if ((! _locations.empty()) && (_pos != nullptr)) {
+ LOG(debug, "calculate 2D Z-distance from %zu locations", _locations.size());
return calculate2DZDistance(docId);
}
return DEFAULT_DISTANCE;
@@ -97,35 +98,33 @@ DistanceExecutor::calculate2DZDistance(uint32_t docId)
uint64_t sqabsdist = std::numeric_limits<uint64_t>::max();
int32_t docx = 0;
int32_t docy = 0;
- for (uint32_t i = 0; i < numValues; ++i) {
- vespalib::geo::ZCurve::decode(_intBuf[i], &docx, &docy);
- uint32_t dx;
- uint32_t dy;
- if (_location.getXPosition() > docx) {
- dx = _location.getXPosition() - docx;
- } else {
- dx = docx - _location.getXPosition();
- }
- if (_location.getXAspect() != 0) {
- dx = ((uint64_t) dx * _location.getXAspect()) >> 32;
- }
- if (_location.getYPosition() > docy) {
- dy = _location.getYPosition() - docy;
- } else {
- dy = docy - _location.getYPosition();
- }
- uint64_t sqdist = (uint64_t) dx * dx + (uint64_t) dy * dy;
- if (sqdist < sqabsdist) {
- sqabsdist = sqdist;
+ for (auto loc : _locations) {
+ assert(loc);
+ assert(loc->isValid());
+ int32_t loc_x = loc->getXPosition();
+ int32_t loc_y = loc->getYPosition();
+ uint64_t loc_a = loc->getXAspect();
+ LOG(debug, "location: x=%u, y=%u, aspect=%zu", loc_x, loc_y, loc_a);
+ for (uint32_t i = 0; i < numValues; ++i) {
+ vespalib::geo::ZCurve::decode(_intBuf[i], &docx, &docy);
+ uint32_t dx = (loc_x > docx) ? (loc_x - docx) : (docx - loc_x);
+ if (loc_a != 0) {
+ dx = (uint64_t(dx) * loc_a) >> 32;
+ }
+ uint32_t dy = (loc_y > docy) ? (loc_y - docy) : (docy - loc_y);
+ uint64_t sqdist = (uint64_t) dx * dx + (uint64_t) dy * dy;
+ if (sqdist < sqabsdist) {
+ sqabsdist = sqdist;
+ }
}
}
return static_cast<feature_t>(std::sqrt(static_cast<feature_t>(sqabsdist)));
}
-DistanceExecutor::DistanceExecutor(const Location & location,
+DistanceExecutor::DistanceExecutor(std::vector<const Location *> locations,
const search::attribute::IAttributeVector * pos) :
FeatureExecutor(),
- _location(location),
+ _locations(locations),
_pos(pos),
_intBuf()
{
@@ -231,6 +230,7 @@ DistanceBlueprint::setup(const IIndexEnvironment & env,
return setup_geopos(env, z);
}
if (allow_bad_field) {
+ // TODO remove on Vespa 8
// backwards compatibility fallback:
return setup_geopos(env, arg);
}
@@ -251,11 +251,30 @@ DistanceBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash
if (_use_item_label) {
return stash.create<ConvertRawscoreToDistance>(env, _arg_string);
}
+ // expect geo pos:
const search::attribute::IAttributeVector * pos = nullptr;
- const Location & location = env.getLocation();
- LOG(debug, "DistanceBlueprint::createExecutor location.valid='%s', attribute='%s'",
- location.isValid() ? "true" : "false", _arg_string.c_str());
- if (_use_geo_pos && location.isValid()) {
+ std::vector<const search::fef::Location *> matching_locs;
+ std::vector<const search::fef::Location *> other_locs;
+
+ for (auto loc_ptr : env.getAllLocations()) {
+ if (_use_geo_pos && loc_ptr && loc_ptr->isValid()) {
+ if (loc_ptr->getAttribute() == _arg_string) {
+ LOG(debug, "found loc from query env matching '%s'", _arg_string.c_str());
+ matching_locs.push_back(loc_ptr);
+ } else {
+ LOG(debug, "found loc(%s) from query env not matching arg(%s)",
+ loc_ptr->getAttribute().c_str(), _arg_string.c_str());
+ other_locs.push_back(loc_ptr);
+ }
+ }
+ }
+ if (matching_locs.empty() && other_locs.empty()) {
+ LOG(debug, "createExecutor: no valid locations");
+ return stash.create<DistanceExecutor>(matching_locs, nullptr);
+ }
+ LOG(debug, "createExecutor: valid location, attribute='%s'", _arg_string.c_str());
+
+ if (_use_geo_pos) {
pos = env.getAttributeContext().getAttribute(_arg_string);
if (pos != nullptr) {
if (!pos->isIntegerType()) {
@@ -271,8 +290,8 @@ DistanceBlueprint::createExecutor(const IQueryEnvironment &env, vespalib::Stash
LOG(warning, "The position attribute '%s' was not found. Will use default distance.", _arg_string.c_str());
}
}
-
- return stash.create<DistanceExecutor>(location, pos);
+ LOG(debug, "use '%s' locations with pos=%p", matching_locs.empty() ? "other" : "matching", pos);
+ return stash.create<DistanceExecutor>(matching_locs.empty() ? other_locs : matching_locs, pos);
}
}
diff --git a/searchlib/src/vespa/searchlib/features/distancefeature.h b/searchlib/src/vespa/searchlib/features/distancefeature.h
index 3a8edd5ee94..024b9f37f31 100644
--- a/searchlib/src/vespa/searchlib/features/distancefeature.h
+++ b/searchlib/src/vespa/searchlib/features/distancefeature.h
@@ -12,7 +12,7 @@ namespace search::features {
*/
class DistanceExecutor : public fef::FeatureExecutor {
private:
- const fef::Location & _location;
+ std::vector<const fef::Location *> _locations;
const attribute::IAttributeVector * _pos;
attribute::IntegerContent _intBuf;
@@ -23,10 +23,11 @@ public:
/**
* Constructs an executor for the distance feature.
*
- * @param location the location object associated with the query environment.
+ * @param locations location objects associated with the query environment.
* @param pos the attribute to use for positions (expects zcurve encoding).
*/
- DistanceExecutor(const fef::Location & location, const attribute::IAttributeVector * pos);
+ DistanceExecutor(std::vector<const fef::Location *> locations,
+ const attribute::IAttributeVector * pos);
void execute(uint32_t docId) override;
static const feature_t DEFAULT_DISTANCE;
diff --git a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
index 041e9ec67bc..811e7fd4616 100644
--- a/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
+++ b/searchlib/src/vespa/searchlib/fef/iqueryenvironment.h
@@ -58,9 +58,9 @@ public:
/**
* Obtain the location information associated with this query environment.
*
- * @return location object.
+ * @return pointers to location objects.
**/
- virtual const Location & getLocation() const = 0;
+ virtual std::vector<const Location *> getAllLocations() const = 0;
/**
* Returns the attribute context for this query.
diff --git a/searchlib/src/vespa/searchlib/fef/location.h b/searchlib/src/vespa/searchlib/fef/location.h
index 5be7d1ce822..3bc693e11b4 100644
--- a/searchlib/src/vespa/searchlib/fef/location.h
+++ b/searchlib/src/vespa/searchlib/fef/location.h
@@ -39,7 +39,7 @@ public:
}
/**
- * Returns the name of the attribute to use for x positions.
+ * Returns the name of the attribute to use for positions.
*
* @return the attribute name.
**/
diff --git a/searchlib/src/vespa/searchlib/fef/phrase_splitter_query_env.h b/searchlib/src/vespa/searchlib/fef/phrase_splitter_query_env.h
index b2a3d416f5a..3a8cde99c06 100644
--- a/searchlib/src/vespa/searchlib/fef/phrase_splitter_query_env.h
+++ b/searchlib/src/vespa/searchlib/fef/phrase_splitter_query_env.h
@@ -73,7 +73,9 @@ public:
}
const Properties & getProperties() const override { return _queryEnv.getProperties(); }
- const Location & getLocation() const override { return _queryEnv.getLocation(); }
+ std::vector<const Location *> getAllLocations() const override {
+ return _queryEnv.getAllLocations();
+ }
const attribute::IAttributeContext & getAttributeContext() const override { return _queryEnv.getAttributeContext(); }
double get_average_field_length(const vespalib::string &field_name) const override { return _queryEnv.get_average_field_length(field_name); }
const IIndexEnvironment & getIndexEnvironment() const override { return _queryEnv.getIndexEnvironment(); }
diff --git a/searchlib/src/vespa/searchlib/fef/test/queryenvironment.h b/searchlib/src/vespa/searchlib/fef/test/queryenvironment.h
index 40898281794..4b9bec1ee68 100644
--- a/searchlib/src/vespa/searchlib/fef/test/queryenvironment.h
+++ b/searchlib/src/vespa/searchlib/fef/test/queryenvironment.h
@@ -38,7 +38,11 @@ public:
const Properties &getProperties() const override { return _properties; }
uint32_t getNumTerms() const override { return _terms.size(); }
const ITermData *getTerm(uint32_t idx) const override { return idx < _terms.size() ? &_terms[idx] : NULL; }
- const Location & getLocation() const override { return _location; }
+ std::vector<const Location *> getAllLocations() const override {
+ std::vector<const Location *> retval;
+ retval.push_back(&_location);
+ return retval;
+ }
const search::attribute::IAttributeContext &getAttributeContext() const override { return *_attrCtx; }
double get_average_field_length(const vespalib::string& field_name) const override {
auto itr = _avg_field_lengths.find(field_name);
diff --git a/searchlib/src/vespa/searchlib/parsequery/parse.h b/searchlib/src/vespa/searchlib/parsequery/parse.h
index b4dd9826b84..68e259b92e8 100644
--- a/searchlib/src/vespa/searchlib/parsequery/parse.h
+++ b/searchlib/src/vespa/searchlib/parsequery/parse.h
@@ -53,7 +53,7 @@ public:
ITEM_REGEXP = 24,
ITEM_WORD_ALTERNATIVES = 25,
ITEM_NEAREST_NEIGHBOR = 26,
- ITEM_LOCATION_TERM = 27,
+ ITEM_GEO_LOCATION_TERM = 27,
ITEM_MAX = 28, // Indicates how long tables must be.
ITEM_UNDEF = 31,
};
diff --git a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
index 17cbd6dce1b..1820fb0e969 100644
--- a/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
+++ b/searchlib/src/vespa/searchlib/parsequery/stackdumpiterator.cpp
@@ -207,7 +207,7 @@ SimpleQueryStackDumpIterator::next()
}
break;
case ParseItem::ITEM_NUMTERM:
- case ParseItem::ITEM_LOCATION_TERM:
+ case ParseItem::ITEM_GEO_LOCATION_TERM:
case ParseItem::ITEM_TERM:
case ParseItem::ITEM_PREFIXTERM:
case ParseItem::ITEM_SUBSTRINGTERM:
diff --git a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
index f1599e820ef..66466b030d0 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
+++ b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
@@ -63,8 +63,13 @@ QueryNode::Build(const QueryNode * parent, const QueryNodeResultFactory & factor
}
}
break;
+ case ParseItem::ITEM_GEO_LOCATION_TERM:
+ // TODO implement this:
+ // vespalib::string field = queryRep.getIndexName();
+ // vespalib::stringref location_term = queryRep.getTerm();
+ // qn = std::make_unique<LocationQueryNode> ...something ....
+ // break;
case ParseItem::ITEM_NUMTERM:
- case ParseItem::ITEM_LOCATION_TERM:
case ParseItem::ITEM_TERM:
case ParseItem::ITEM_PREFIXTERM:
case ParseItem::ITEM_REGEXP:
diff --git a/searchlib/src/vespa/searchlib/query/tree/location.cpp b/searchlib/src/vespa/searchlib/query/tree/location.cpp
index 216c5ec5ad0..6e678f9e682 100644
--- a/searchlib/src/vespa/searchlib/query/tree/location.cpp
+++ b/searchlib/src/vespa/searchlib/query/tree/location.cpp
@@ -6,58 +6,71 @@
#include <vespa/vespalib/stllike/asciistream.h>
using vespalib::asciistream;
+using search::common::GeoLocation;
namespace search::query {
-Location::Location(const Point &point, uint32_t max_dist, uint32_t x_aspect) {
- asciistream loc;
- loc << "(2" // dimensionality
- << "," << point.x
- << "," << point.y
- << "," << max_dist
- << "," << "0" // table id.
- << "," << "1" // rank multiplier.
- << "," << "0" // rank only on distance.
- << "," << x_aspect // x aspect.
- << ")";
- _location_string = loc.str();
+static GeoLocation::Box convert(const Rectangle &rect) {
+ GeoLocation::Range x_range{rect.left, rect.right};
+ GeoLocation::Range y_range{rect.top, rect.bottom};
+ return GeoLocation::Box{x_range, y_range};
}
+Location::Location(const Point &p, uint32_t max_dist, uint32_t aspect)
+ : Parent(p, max_dist, GeoLocation::Aspect(aspect))
+{}
+
Location::Location(const Rectangle &rect,
- const Point &point, uint32_t max_dist, uint32_t x_aspect)
-{
- asciistream loc;
- loc << "(2" // dimensionality
- << "," << point.x
- << "," << point.y
- << "," << max_dist
- << "," << "0" // table id.
- << "," << "1" // rank multiplier.
- << "," << "0" // rank only on distance.
- << "," << x_aspect // x aspect.
- << ")";
- loc << "[2," << rect.left
- << "," << rect.top
- << "," << rect.right
- << "," << rect.bottom
- << "]" ;
- _location_string = loc.str();
+ const Point &p, uint32_t max_dist, uint32_t aspect)
+ : Parent(convert(rect), p, max_dist, GeoLocation::Aspect(aspect))
+{}
-}
+Location::Location(const Rectangle &rect)
+ : Parent(convert(rect))
+{}
-Location::Location(const Rectangle &rect) {
- asciistream loc;
- loc << "[2," << rect.left
- << "," << rect.top
- << "," << rect.right
- << "," << rect.bottom
- << "]" ;
- _location_string = loc.str();
+bool
+Location::operator==(const Location &other) const
+{
+ auto me = getOldFormatString();
+ auto it = other.getOldFormatString();
+ if (me == it) {
+ return true;
+ } else {
+ // dump 'me' and 'it' here if unit tests fail
+ return false;
+ }
+}
+
+std::string
+Location::getOldFormatString() const
+{
+ // we need to product what search::common::GeoLocationParser can parse
+ vespalib::asciistream buf;
+ if (has_point) {
+ buf << "(2" // dimensionality
+ << "," << point.x
+ << "," << point.y
+ << "," << radius
+ << "," << "0" // table id.
+ << "," << "1" // rank multiplier.
+ << "," << "0" // rank only on distance.
+ << "," << x_aspect.multiplier // aspect multiplier
+ << ")";
+ }
+ if (bounding_box.active()) {
+ buf << "[2," << bounding_box.x.low
+ << "," << bounding_box.y.low
+ << "," << bounding_box.x.high
+ << "," << bounding_box.y.high
+ << "]" ;
+ }
+ return buf.str();
}
vespalib::asciistream &operator<<(vespalib::asciistream &out, const Location &loc) {
- return out << loc.getLocationString();
+ return out << loc.getOldFormatString();
}
}
diff --git a/searchlib/src/vespa/searchlib/query/tree/location.h b/searchlib/src/vespa/searchlib/query/tree/location.h
index e1826c7184a..6b8090f45e1 100644
--- a/searchlib/src/vespa/searchlib/query/tree/location.h
+++ b/searchlib/src/vespa/searchlib/query/tree/location.h
@@ -2,29 +2,26 @@
#pragma once
-#include <vespa/vespalib/stllike/string.h>
+#include <string>
+#include <vespa/searchlib/common/geo_location_spec.h>
+#include "point.h"
+#include "rectangle.h"
namespace vespalib { class asciistream; }
namespace search::query {
-struct Point;
-struct Rectangle;
-
-class Location {
- vespalib::string _location_string;
+class Location : public search::common::GeoLocation {
+ using Parent = search::common::GeoLocation;
public:
- Location() : _location_string() {}
+ Location() {}
+ Location(const Parent &spec) : Parent(spec) {}
+ ~Location() {}
Location(const Point &p, uint32_t dist, uint32_t x_asp);
Location(const Rectangle &rect);
Location(const Rectangle &rect, const Point &p, uint32_t dist, uint32_t x_asp);
- Location(const vespalib::string &s) : _location_string(s) {}
- bool operator==(const Location &other) const {
- return _location_string == other._location_string;
- }
- const vespalib::string &getLocationString() const {
- return _location_string;
- }
+ bool operator==(const Location &other) const;
+ std::string getOldFormatString() const;
};
vespalib::asciistream &operator<<(vespalib::asciistream &out, const Location &loc);
diff --git a/searchlib/src/vespa/searchlib/query/tree/point.h b/searchlib/src/vespa/searchlib/query/tree/point.h
index 89d0bc1db44..48700681158 100644
--- a/searchlib/src/vespa/searchlib/query/tree/point.h
+++ b/searchlib/src/vespa/searchlib/query/tree/point.h
@@ -3,18 +3,10 @@
#pragma once
#include <cstdint>
+#include <vespa/searchlib/common/geo_location.h>
namespace search::query {
-struct Point {
- int64_t x;
- int64_t y;
- Point() : x(0), y(0) {}
- Point(int64_t x_in, int64_t y_in) : x(x_in), y(y_in) {}
-};
-
-inline bool operator==(const Point &p1, const Point &p2) {
- return p1.x == p2.x && p1.y == p2.y;
-}
+using Point = search::common::GeoLocation::Point;
}
diff --git a/searchlib/src/vespa/searchlib/query/tree/rectangle.h b/searchlib/src/vespa/searchlib/query/tree/rectangle.h
index 97be9ddeb32..358e994aacd 100644
--- a/searchlib/src/vespa/searchlib/query/tree/rectangle.h
+++ b/searchlib/src/vespa/searchlib/query/tree/rectangle.h
@@ -5,20 +5,14 @@
namespace search::query {
struct Rectangle {
- int64_t left;
- int64_t top;
- int64_t right;
- int64_t bottom;
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
Rectangle() : left(0), top(0), right(0), bottom(0) {}
- Rectangle(int64_t l, int64_t t, int64_t r, int64_t b)
+ Rectangle(int32_t l, int32_t t, int32_t r, int32_t b)
: left(l), top(t), right(r), bottom(b) {}
};
-inline bool operator==(const Rectangle &r1, const Rectangle &r2) {
- return r1.left == r2.left && r1.right == r2.right
- && r1.top == r2.top && r1.bottom == r2.bottom;
}
-
-}
-
diff --git a/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp b/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
index f33520d8b0e..82302e4ab48 100644
--- a/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
+++ b/searchlib/src/vespa/searchlib/query/tree/stackdumpcreator.cpp
@@ -228,7 +228,7 @@ class QueryNodeConverter : public QueryVisitor {
}
void visit(LocationTerm &node) override {
- createTerm(node, ParseItem::ITEM_LOCATION_TERM);
+ createTerm(node, ParseItem::ITEM_GEO_LOCATION_TERM);
}
void visit(PrefixTerm &node) override {
diff --git a/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h b/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
index 898db9785f6..a7e00d41555 100644
--- a/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
+++ b/searchlib/src/vespa/searchlib/query/tree/stackdumpquerycreator.h
@@ -6,6 +6,7 @@
#include "querybuilder.h"
#include "term.h"
#include <vespa/searchlib/parsequery/stackdumpiterator.h>
+#include <vespa/searchlib/common/geo_location_parser.h>
#include <vespa/vespalib/objects/hexdump.h>
namespace search::query {
@@ -141,17 +142,15 @@ private:
t = &builder.addStringTerm(term, view, id, weight);
} else if (type == ParseItem::ITEM_SUFFIXTERM) {
t = &builder.addSuffixTerm(term, view, id, weight);
- } else if (type == ParseItem::ITEM_LOCATION_TERM) {
- Location loc(term);
+ } else if (type == ParseItem::ITEM_GEO_LOCATION_TERM) {
+ search::common::GeoLocationParser parser;
+ parser.parseOldFormat(term);
+ Location loc(parser.getGeoLocation());
t = &builder.addLocationTerm(loc, view, id, weight);
} else if (type == ParseItem::ITEM_NUMTERM) {
if (term[0] == '[' || term[0] == '<' || term[0] == '>') {
Range range(term);
t = &builder.addRangeTerm(range, view, id, weight);
- } else if (term[0] == '(') {
- // TODO: handled above, should remove this block
- Location loc(term);
- t = &builder.addLocationTerm(loc, view, id, weight);
} else {
t = &builder.addNumberTerm(term, view, id, weight);
}