aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib
diff options
context:
space:
mode:
authorTor Egge <Tor.Egge@online.no>2024-01-23 13:39:51 +0100
committerTor Egge <Tor.Egge@online.no>2024-01-23 13:39:51 +0100
commite7988189f70b0a39fe3856768a62b36e290c7ad3 (patch)
treec75a019bb3c311723abda2c6efe57836fe3c0ecd /searchlib
parent056a486a55fd66c39b9b30065865d29655f338f7 (diff)
Move out NearQueryNode, ONearQueryNode, PhraseQueryNode and
SameElementQueryNode to separate files.
Diffstat (limited to 'searchlib')
-rw-r--r--searchlib/src/tests/query/streaming/CMakeLists.txt18
-rw-r--r--searchlib/src/tests/query/streaming/phrase_query_node_test.cpp95
-rw-r--r--searchlib/src/tests/query/streaming/same_element_query_node_test.cpp135
-rw-r--r--searchlib/src/tests/query/streaming_query_test.cpp172
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/CMakeLists.txt4
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/near_query_node.cpp21
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/near_query_node.h26
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/onear_query_node.cpp14
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/onear_query_node.h20
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.cpp82
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.h34
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/query.cpp156
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/query.h63
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/querynode.cpp3
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.cpp65
-rw-r--r--searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.h22
16 files changed, 544 insertions, 386 deletions
diff --git a/searchlib/src/tests/query/streaming/CMakeLists.txt b/searchlib/src/tests/query/streaming/CMakeLists.txt
index d40250fbc3f..257ad24854d 100644
--- a/searchlib/src/tests/query/streaming/CMakeLists.txt
+++ b/searchlib/src/tests/query/streaming/CMakeLists.txt
@@ -17,3 +17,21 @@ vespa_add_executable(searchlib_query_streaming_hit_iterator_pack_test_app TEST
GTest::gtest
)
vespa_add_test(NAME searchlib_query_streaming_hit_iterator_pack_test_app COMMAND searchlib_query_streaming_hit_iterator_pack_test_app)
+
+vespa_add_executable(searchlib_query_streaming_same_element_query_node_test_app TEST
+ SOURCES
+ same_element_query_node_test.cpp
+ DEPENDS
+ searchlib
+ GTest::gtest
+)
+vespa_add_test(NAME searchlib_query_streaming_same_element_query_node_test_app COMMAND searchlib_query_streaming_same_element_query_node_test_app)
+
+vespa_add_executable(searchlib_query_streaming_phrase_query_node_test_app TEST
+ SOURCES
+ phrase_query_node_test.cpp
+ DEPENDS
+ searchlib
+ GTest::gtest
+)
+vespa_add_test(NAME searchlib_query_streaming_phrase_query_node_test_app COMMAND searchlib_query_streaming_phrase_query_node_test_app)
diff --git a/searchlib/src/tests/query/streaming/phrase_query_node_test.cpp b/searchlib/src/tests/query/streaming/phrase_query_node_test.cpp
new file mode 100644
index 00000000000..0caebe74c73
--- /dev/null
+++ b/searchlib/src/tests/query/streaming/phrase_query_node_test.cpp
@@ -0,0 +1,95 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/query/streaming/phrase_query_node.h>
+#include <vespa/searchlib/query/streaming/queryterm.h>
+#include <vespa/searchlib/query/tree/querybuilder.h>
+#include <vespa/searchlib/query/tree/simplequery.h>
+#include <vespa/searchlib/query/tree/stackdumpcreator.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using search::query::QueryBuilder;
+using search::query::Node;
+using search::query::SimpleQueryNodeTypes;
+using search::query::StackDumpCreator;
+using search::query::Weight;
+using search::streaming::HitList;
+using search::streaming::PhraseQueryNode;
+using search::streaming::Query;
+using search::streaming::QueryTerm;
+using search::streaming::QueryNodeRefList;
+using search::streaming::QueryNodeResultFactory;
+using search::streaming::QueryTermList;
+
+TEST(StreamingQueryTest, test_phrase_evaluate)
+{
+ QueryBuilder<SimpleQueryNodeTypes> builder;
+ builder.addPhrase(3, "", 0, Weight(0));
+ {
+ builder.addStringTerm("a", "", 0, Weight(0));
+ builder.addStringTerm("b", "", 0, Weight(0));
+ builder.addStringTerm("c", "", 0, Weight(0));
+ }
+ Node::UP node = builder.build();
+ vespalib::string stackDump = StackDumpCreator::create(*node);
+ QueryNodeResultFactory empty;
+ Query q(empty, stackDump);
+ QueryNodeRefList phrases;
+ q.getPhrases(phrases);
+ QueryTermList terms;
+ q.getLeaves(terms);
+ for (QueryTerm * qt : terms) {
+ qt->resizeFieldId(1);
+ }
+
+ // field 0
+ terms[0]->add(0, 0, 1, 0);
+ terms[1]->add(0, 0, 1, 1);
+ terms[2]->add(0, 0, 1, 2);
+ terms[0]->add(0, 0, 1, 7);
+ terms[1]->add(0, 1, 1, 8);
+ terms[2]->add(0, 0, 1, 9);
+ // field 1
+ terms[0]->add(1, 0, 1, 4);
+ terms[1]->add(1, 0, 1, 5);
+ terms[2]->add(1, 0, 1, 6);
+ // field 2 (not complete match)
+ terms[0]->add(2, 0, 1, 1);
+ terms[1]->add(2, 0, 1, 2);
+ terms[2]->add(2, 0, 1, 4);
+ // field 3
+ terms[0]->add(3, 0, 1, 0);
+ terms[1]->add(3, 0, 1, 1);
+ terms[2]->add(3, 0, 1, 2);
+ // field 4 (not complete match)
+ terms[0]->add(4, 0, 1, 1);
+ terms[1]->add(4, 0, 1, 2);
+ // field 5 (not complete match)
+ terms[0]->add(5, 0, 1, 2);
+ terms[1]->add(5, 0, 1, 1);
+ terms[2]->add(5, 0, 1, 0);
+ HitList hits;
+ auto * p = static_cast<PhraseQueryNode *>(phrases[0]);
+ p->evaluateHits(hits);
+ ASSERT_EQ(3u, hits.size());
+ EXPECT_EQ(0u, hits[0].field_id());
+ EXPECT_EQ(0u, hits[0].element_id());
+ EXPECT_EQ(2u, hits[0].position());
+ EXPECT_EQ(1u, hits[1].field_id());
+ EXPECT_EQ(0u, hits[1].element_id());
+ EXPECT_EQ(6u, hits[1].position());
+ EXPECT_EQ(3u, hits[2].field_id());
+ EXPECT_EQ(0u, hits[2].element_id());
+ EXPECT_EQ(2u, hits[2].position());
+ ASSERT_EQ(4u, p->getFieldInfoSize());
+ EXPECT_EQ(0u, p->getFieldInfo(0).getHitOffset());
+ EXPECT_EQ(1u, p->getFieldInfo(0).getHitCount());
+ EXPECT_EQ(1u, p->getFieldInfo(1).getHitOffset());
+ EXPECT_EQ(1u, p->getFieldInfo(1).getHitCount());
+ EXPECT_EQ(0u, p->getFieldInfo(2).getHitOffset()); // invalid, but will never be used
+ EXPECT_EQ(0u, p->getFieldInfo(2).getHitCount());
+ EXPECT_EQ(2u, p->getFieldInfo(3).getHitOffset());
+ EXPECT_EQ(1u, p->getFieldInfo(3).getHitCount());
+ EXPECT_TRUE(p->evaluate());
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/query/streaming/same_element_query_node_test.cpp b/searchlib/src/tests/query/streaming/same_element_query_node_test.cpp
new file mode 100644
index 00000000000..ece6dc551b2
--- /dev/null
+++ b/searchlib/src/tests/query/streaming/same_element_query_node_test.cpp
@@ -0,0 +1,135 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/searchlib/query/streaming/same_element_query_node.h>
+#include <vespa/searchlib/query/streaming/queryterm.h>
+#include <vespa/searchlib/query/tree/querybuilder.h>
+#include <vespa/searchlib/query/tree/simplequery.h>
+#include <vespa/searchlib/query/tree/stackdumpcreator.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using search::query::QueryBuilder;
+using search::query::Node;
+using search::query::SimpleQueryNodeTypes;
+using search::query::StackDumpCreator;
+using search::query::Weight;
+using search::streaming::HitList;
+using search::streaming::Query;
+using search::streaming::QueryNode;
+using search::streaming::QueryNodeResultFactory;
+using search::streaming::QueryTerm;
+using search::streaming::QueryTermList;
+using search::streaming::SameElementQueryNode;
+
+namespace {
+
+class AllowRewrite : public QueryNodeResultFactory
+{
+public:
+ explicit AllowRewrite(vespalib::stringref index) noexcept : _allowedIndex(index) {}
+ bool allow_float_terms_rewrite(vespalib::stringref index) const noexcept override { return index == _allowedIndex; }
+private:
+ vespalib::string _allowedIndex;
+};
+
+}
+
+TEST(SameElementQueryNodeTest, a_unhandled_sameElement_stack)
+{
+ const char * stack = "\022\002\026xyz_abcdefghij_xyzxyzxQ\001\vxxxxxx_name\034xxxxxx_xxxx_xxxxxxx_xxxxxxxxE\002\005delta\b<0.00393";
+ vespalib::stringref stackDump(stack);
+ EXPECT_EQ(85u, stackDump.size());
+ AllowRewrite empty("");
+ const Query q(empty, stackDump);
+ EXPECT_TRUE(q.valid());
+ const QueryNode & root = q.getRoot();
+ auto sameElement = dynamic_cast<const SameElementQueryNode *>(&root);
+ EXPECT_TRUE(sameElement != nullptr);
+ EXPECT_EQ(2u, sameElement->size());
+ EXPECT_EQ("xyz_abcdefghij_xyzxyzx", sameElement->getIndex());
+ auto term0 = dynamic_cast<const QueryTerm *>((*sameElement)[0].get());
+ EXPECT_TRUE(term0 != nullptr);
+ auto term1 = dynamic_cast<const QueryTerm *>((*sameElement)[1].get());
+ EXPECT_TRUE(term1 != nullptr);
+}
+
+namespace {
+ void verifyQueryTermNode(const vespalib::string & index, const QueryNode *node) {
+ EXPECT_TRUE(dynamic_cast<const QueryTerm *>(node) != nullptr);
+ EXPECT_EQ(index, node->getIndex());
+ }
+}
+
+TEST(SameElementQueryNodeTest, test_same_element_evaluate)
+{
+ QueryBuilder<SimpleQueryNodeTypes> builder;
+ builder.addSameElement(3, "field", 0, Weight(0));
+ {
+ builder.addStringTerm("a", "f1", 0, Weight(0));
+ builder.addStringTerm("b", "f2", 1, Weight(0));
+ builder.addStringTerm("c", "f3", 2, Weight(0));
+ }
+ Node::UP node = builder.build();
+ vespalib::string stackDump = StackDumpCreator::create(*node);
+ QueryNodeResultFactory empty;
+ Query q(empty, stackDump);
+ auto * sameElem = dynamic_cast<SameElementQueryNode *>(&q.getRoot());
+ EXPECT_TRUE(sameElem != nullptr);
+ EXPECT_EQ("field", sameElem->getIndex());
+ EXPECT_EQ(3u, sameElem->size());
+ verifyQueryTermNode("field.f1", (*sameElem)[0].get());
+ verifyQueryTermNode("field.f2", (*sameElem)[1].get());
+ verifyQueryTermNode("field.f3", (*sameElem)[2].get());
+
+ QueryTermList terms;
+ q.getLeaves(terms);
+ EXPECT_EQ(3u, terms.size());
+ for (QueryTerm * qt : terms) {
+ qt->resizeFieldId(3);
+ }
+
+ // field 0
+ terms[0]->add(0, 0, 10, 1);
+ terms[0]->add(0, 1, 20, 2);
+ terms[0]->add(0, 2, 30, 3);
+ terms[0]->add(0, 3, 40, 4);
+ terms[0]->add(0, 4, 50, 5);
+ terms[0]->add(0, 5, 60, 6);
+
+ terms[1]->add(1, 0, 70, 7);
+ terms[1]->add(1, 1, 80, 8);
+ terms[1]->add(1, 2, 90, 9);
+ terms[1]->add(1, 4, 100, 10);
+ terms[1]->add(1, 5, 110, 11);
+ terms[1]->add(1, 6, 120, 12);
+
+ terms[2]->add(2, 0, 130, 13);
+ terms[2]->add(2, 2, 140, 14);
+ terms[2]->add(2, 4, 150, 15);
+ terms[2]->add(2, 5, 160, 16);
+ terms[2]->add(2, 6, 170, 17);
+ HitList hits;
+ sameElem->evaluateHits(hits);
+ EXPECT_EQ(4u, hits.size());
+ EXPECT_EQ(2u, hits[0].field_id());
+ EXPECT_EQ(0u, hits[0].element_id());
+ EXPECT_EQ(130, hits[0].element_weight());
+ EXPECT_EQ(0u, hits[0].position());
+
+ EXPECT_EQ(2u, hits[1].field_id());
+ EXPECT_EQ(2u, hits[1].element_id());
+ EXPECT_EQ(140, hits[1].element_weight());
+ EXPECT_EQ(0u, hits[1].position());
+
+ EXPECT_EQ(2u, hits[2].field_id());
+ EXPECT_EQ(4u, hits[2].element_id());
+ EXPECT_EQ(150, hits[2].element_weight());
+ EXPECT_EQ(0u, hits[2].position());
+
+ EXPECT_EQ(2u, hits[3].field_id());
+ EXPECT_EQ(5u, hits[3].element_id());
+ EXPECT_EQ(160, hits[3].element_weight());
+ EXPECT_EQ(0u, hits[3].position());
+ EXPECT_TRUE(sameElem->evaluate());
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/tests/query/streaming_query_test.cpp b/searchlib/src/tests/query/streaming_query_test.cpp
index 35732eaf7e5..d2be1d453a2 100644
--- a/searchlib/src/tests/query/streaming_query_test.cpp
+++ b/searchlib/src/tests/query/streaming_query_test.cpp
@@ -4,6 +4,7 @@
#include <vespa/searchlib/fef/matchdata.h>
#include <vespa/searchlib/query/streaming/dot_product_term.h>
#include <vespa/searchlib/query/streaming/in_term.h>
+#include <vespa/searchlib/query/streaming/phrase_query_node.h>
#include <vespa/searchlib/query/streaming/query.h>
#include <vespa/searchlib/query/streaming/nearest_neighbor_query_node.h>
#include <vespa/searchlib/query/streaming/wand_term.h>
@@ -428,78 +429,6 @@ TEST(StreamingQueryTest, test_get_query_parts)
}
}
-TEST(StreamingQueryTest, test_phrase_evaluate)
-{
- QueryBuilder<SimpleQueryNodeTypes> builder;
- builder.addPhrase(3, "", 0, Weight(0));
- {
- builder.addStringTerm("a", "", 0, Weight(0));
- builder.addStringTerm("b", "", 0, Weight(0));
- builder.addStringTerm("c", "", 0, Weight(0));
- }
- Node::UP node = builder.build();
- vespalib::string stackDump = StackDumpCreator::create(*node);
- QueryNodeResultFactory empty;
- Query q(empty, stackDump);
- QueryNodeRefList phrases;
- q.getPhrases(phrases);
- QueryTermList terms;
- q.getLeaves(terms);
- for (QueryTerm * qt : terms) {
- qt->resizeFieldId(1);
- }
-
- // field 0
- terms[0]->add(0, 0, 1, 0);
- terms[1]->add(0, 0, 1, 1);
- terms[2]->add(0, 0, 1, 2);
- terms[0]->add(0, 0, 1, 7);
- terms[1]->add(0, 1, 1, 8);
- terms[2]->add(0, 0, 1, 9);
- // field 1
- terms[0]->add(1, 0, 1, 4);
- terms[1]->add(1, 0, 1, 5);
- terms[2]->add(1, 0, 1, 6);
- // field 2 (not complete match)
- terms[0]->add(2, 0, 1, 1);
- terms[1]->add(2, 0, 1, 2);
- terms[2]->add(2, 0, 1, 4);
- // field 3
- terms[0]->add(3, 0, 1, 0);
- terms[1]->add(3, 0, 1, 1);
- terms[2]->add(3, 0, 1, 2);
- // field 4 (not complete match)
- terms[0]->add(4, 0, 1, 1);
- terms[1]->add(4, 0, 1, 2);
- // field 5 (not complete match)
- terms[0]->add(5, 0, 1, 2);
- terms[1]->add(5, 0, 1, 1);
- terms[2]->add(5, 0, 1, 0);
- HitList hits;
- auto * p = static_cast<PhraseQueryNode *>(phrases[0]);
- p->evaluateHits(hits);
- ASSERT_EQ(3u, hits.size());
- EXPECT_EQ(0u, hits[0].field_id());
- EXPECT_EQ(0u, hits[0].element_id());
- EXPECT_EQ(2u, hits[0].position());
- EXPECT_EQ(1u, hits[1].field_id());
- EXPECT_EQ(0u, hits[1].element_id());
- EXPECT_EQ(6u, hits[1].position());
- EXPECT_EQ(3u, hits[2].field_id());
- EXPECT_EQ(0u, hits[2].element_id());
- EXPECT_EQ(2u, hits[2].position());
- ASSERT_EQ(4u, p->getFieldInfoSize());
- EXPECT_EQ(0u, p->getFieldInfo(0).getHitOffset());
- EXPECT_EQ(1u, p->getFieldInfo(0).getHitCount());
- EXPECT_EQ(1u, p->getFieldInfo(1).getHitOffset());
- EXPECT_EQ(1u, p->getFieldInfo(1).getHitCount());
- EXPECT_EQ(0u, p->getFieldInfo(2).getHitOffset()); // invalid, but will never be used
- EXPECT_EQ(0u, p->getFieldInfo(2).getHitCount());
- EXPECT_EQ(2u, p->getFieldInfo(3).getHitOffset());
- EXPECT_EQ(1u, p->getFieldInfo(3).getHitCount());
- EXPECT_TRUE(p->evaluate());
-}
-
TEST(StreamingQueryTest, test_hit)
{
// field id
@@ -774,105 +703,6 @@ TEST(StreamingQueryTest, require_that_we_do_not_break_the_stack_on_bad_query)
EXPECT_FALSE(term.isValid());
}
-TEST(StreamingQueryTest, a_unhandled_sameElement_stack)
-{
- const char * stack = "\022\002\026xyz_abcdefghij_xyzxyzxQ\001\vxxxxxx_name\034xxxxxx_xxxx_xxxxxxx_xxxxxxxxE\002\005delta\b<0.00393";
- vespalib::stringref stackDump(stack);
- EXPECT_EQ(85u, stackDump.size());
- AllowRewrite empty("");
- const Query q(empty, stackDump);
- EXPECT_TRUE(q.valid());
- const QueryNode & root = q.getRoot();
- auto sameElement = dynamic_cast<const SameElementQueryNode *>(&root);
- EXPECT_TRUE(sameElement != nullptr);
- EXPECT_EQ(2u, sameElement->size());
- EXPECT_EQ("xyz_abcdefghij_xyzxyzx", sameElement->getIndex());
- auto term0 = dynamic_cast<const QueryTerm *>((*sameElement)[0].get());
- EXPECT_TRUE(term0 != nullptr);
- auto term1 = dynamic_cast<const QueryTerm *>((*sameElement)[1].get());
- EXPECT_TRUE(term1 != nullptr);
-}
-
-namespace {
- void verifyQueryTermNode(const vespalib::string & index, const QueryNode *node) {
- EXPECT_TRUE(dynamic_cast<const QueryTerm *>(node) != nullptr);
- EXPECT_EQ(index, node->getIndex());
- }
-}
-
-TEST(StreamingQueryTest, test_same_element_evaluate)
-{
- QueryBuilder<SimpleQueryNodeTypes> builder;
- builder.addSameElement(3, "field", 0, Weight(0));
- {
- builder.addStringTerm("a", "f1", 0, Weight(0));
- builder.addStringTerm("b", "f2", 1, Weight(0));
- builder.addStringTerm("c", "f3", 2, Weight(0));
- }
- Node::UP node = builder.build();
- vespalib::string stackDump = StackDumpCreator::create(*node);
- QueryNodeResultFactory empty;
- Query q(empty, stackDump);
- auto * sameElem = dynamic_cast<SameElementQueryNode *>(&q.getRoot());
- EXPECT_TRUE(sameElem != nullptr);
- EXPECT_EQ("field", sameElem->getIndex());
- EXPECT_EQ(3u, sameElem->size());
- verifyQueryTermNode("field.f1", (*sameElem)[0].get());
- verifyQueryTermNode("field.f2", (*sameElem)[1].get());
- verifyQueryTermNode("field.f3", (*sameElem)[2].get());
-
- QueryTermList terms;
- q.getLeaves(terms);
- EXPECT_EQ(3u, terms.size());
- for (QueryTerm * qt : terms) {
- qt->resizeFieldId(3);
- }
-
- // field 0
- terms[0]->add(0, 0, 10, 1);
- terms[0]->add(0, 1, 20, 2);
- terms[0]->add(0, 2, 30, 3);
- terms[0]->add(0, 3, 40, 4);
- terms[0]->add(0, 4, 50, 5);
- terms[0]->add(0, 5, 60, 6);
-
- terms[1]->add(1, 0, 70, 7);
- terms[1]->add(1, 1, 80, 8);
- terms[1]->add(1, 2, 90, 9);
- terms[1]->add(1, 4, 100, 10);
- terms[1]->add(1, 5, 110, 11);
- terms[1]->add(1, 6, 120, 12);
-
- terms[2]->add(2, 0, 130, 13);
- terms[2]->add(2, 2, 140, 14);
- terms[2]->add(2, 4, 150, 15);
- terms[2]->add(2, 5, 160, 16);
- terms[2]->add(2, 6, 170, 17);
- HitList hits;
- sameElem->evaluateHits(hits);
- EXPECT_EQ(4u, hits.size());
- EXPECT_EQ(2u, hits[0].field_id());
- EXPECT_EQ(0u, hits[0].element_id());
- EXPECT_EQ(130, hits[0].element_weight());
- EXPECT_EQ(0u, hits[0].position());
-
- EXPECT_EQ(2u, hits[1].field_id());
- EXPECT_EQ(2u, hits[1].element_id());
- EXPECT_EQ(140, hits[1].element_weight());
- EXPECT_EQ(0u, hits[1].position());
-
- EXPECT_EQ(2u, hits[2].field_id());
- EXPECT_EQ(4u, hits[2].element_id());
- EXPECT_EQ(150, hits[2].element_weight());
- EXPECT_EQ(0u, hits[2].position());
-
- EXPECT_EQ(2u, hits[3].field_id());
- EXPECT_EQ(5u, hits[3].element_id());
- EXPECT_EQ(160, hits[3].element_weight());
- EXPECT_EQ(0u, hits[3].position());
- EXPECT_TRUE(sameElem->evaluate());
-}
-
TEST(StreamingQueryTest, test_nearest_neighbor_query_node)
{
QueryBuilder<SimpleQueryNodeTypes> builder;
diff --git a/searchlib/src/vespa/searchlib/query/streaming/CMakeLists.txt b/searchlib/src/vespa/searchlib/query/streaming/CMakeLists.txt
index 91b8c429800..63d52cbdf9f 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/CMakeLists.txt
+++ b/searchlib/src/vespa/searchlib/query/streaming/CMakeLists.txt
@@ -6,11 +6,15 @@ vespa_add_library(searchlib_query_streaming OBJECT
hit_iterator_pack.cpp
in_term.cpp
multi_term.cpp
+ near_query_node.cpp
nearest_neighbor_query_node.cpp
+ onear_query_node.cpp
+ phrase_query_node.cpp
query.cpp
querynode.cpp
querynoderesultbase.cpp
queryterm.cpp
+ same_element_query_node.cpp
wand_term.cpp
weighted_set_term.cpp
regexp_term.cpp
diff --git a/searchlib/src/vespa/searchlib/query/streaming/near_query_node.cpp b/searchlib/src/vespa/searchlib/query/streaming/near_query_node.cpp
new file mode 100644
index 00000000000..126ed1ff69e
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/near_query_node.cpp
@@ -0,0 +1,21 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "near_query_node.h"
+#include <vespa/vespalib/objects/visit.hpp>
+
+namespace search::streaming {
+
+bool
+NearQueryNode::evaluate() const
+{
+ return AndQueryNode::evaluate();
+}
+
+void
+NearQueryNode::visitMembers(vespalib::ObjectVisitor &visitor) const
+{
+ AndQueryNode::visitMembers(visitor);
+ visit(visitor, "distance", static_cast<uint64_t>(_distance));
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/near_query_node.h b/searchlib/src/vespa/searchlib/query/streaming/near_query_node.h
new file mode 100644
index 00000000000..63b7e748296
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/near_query_node.h
@@ -0,0 +1,26 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "query.h"
+
+namespace search::streaming {
+
+/**
+ N-ary Near operator. All terms must be within the given distance.
+*/
+class NearQueryNode : public AndQueryNode
+{
+public:
+ NearQueryNode() noexcept : AndQueryNode("NEAR"), _distance(0) { }
+ explicit NearQueryNode(const char * opName) noexcept : AndQueryNode(opName), _distance(0) { }
+ bool evaluate() const override;
+ void distance(size_t dist) { _distance = dist; }
+ size_t distance() const { return _distance; }
+ void visitMembers(vespalib::ObjectVisitor &visitor) const override;
+ bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
+private:
+ size_t _distance;
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.cpp b/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.cpp
new file mode 100644
index 00000000000..a06ff0f90e1
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.cpp
@@ -0,0 +1,14 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "onear_query_node.h"
+
+namespace search::streaming {
+
+bool
+ONearQueryNode::evaluate() const
+{
+ bool ok(NearQueryNode::evaluate());
+ return ok;
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.h b/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.h
new file mode 100644
index 00000000000..649496b62d9
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/onear_query_node.h
@@ -0,0 +1,20 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "near_query_node.h"
+
+namespace search::streaming {
+
+/**
+ N-ary Ordered near operator. The terms must be in order and the distance between
+ the first and last must not exceed the given distance.
+*/
+class ONearQueryNode : public NearQueryNode
+{
+public:
+ ONearQueryNode() noexcept : NearQueryNode("ONEAR") { }
+ bool evaluate() const override;
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.cpp b/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.cpp
new file mode 100644
index 00000000000..2d2778417fa
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.cpp
@@ -0,0 +1,82 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "phrase_query_node.h"
+#include "hit_iterator_pack.h"
+#include <cassert>
+
+namespace search::streaming {
+
+bool
+PhraseQueryNode::evaluate() const
+{
+ HitList hl;
+ return ! evaluateHits(hl).empty();
+}
+
+void PhraseQueryNode::getPhrases(QueryNodeRefList & tl) { tl.push_back(this); }
+void PhraseQueryNode::getPhrases(ConstQueryNodeRefList & tl) const { tl.push_back(this); }
+
+void
+PhraseQueryNode::addChild(QueryNode::UP child) {
+ assert(dynamic_cast<const QueryTerm *>(child.get()) != nullptr);
+ AndQueryNode::addChild(std::move(child));
+}
+
+const HitList &
+PhraseQueryNode::evaluateHits(HitList & hl) const
+{
+ hl.clear();
+ _fieldInfo.clear();
+ HitIteratorPack itr_pack(getChildren());
+ if (!itr_pack.all_valid()) {
+ return hl;
+ }
+ auto& last_child = dynamic_cast<const QueryTerm&>(*(*this)[size() - 1]);
+ while (itr_pack.seek_to_matching_field_element()) {
+ uint32_t first_position = itr_pack.front()->position();
+ bool retry_element = true;
+ while (retry_element) {
+ uint32_t position_offset = 0;
+ bool match = true;
+ for (auto& it : itr_pack) {
+ if (!it.seek_in_field_element(first_position + position_offset, itr_pack.get_field_element_ref())) {
+ retry_element = false;
+ match = false;
+ break;
+ }
+ if (it->position() > first_position + position_offset) {
+ first_position = it->position() - position_offset;
+ match = false;
+ break;
+ }
+ ++position_offset;
+ }
+ if (match) {
+ auto h = *itr_pack.back();
+ hl.push_back(h);
+ auto& fi = last_child.getFieldInfo(h.field_id());
+ updateFieldInfo(h.field_id(), hl.size() - 1, fi.getFieldLength());
+ if (!itr_pack.front().step_in_field_element(itr_pack.get_field_element_ref())) {
+ retry_element = false;
+ }
+ }
+ }
+ }
+ return hl;
+}
+
+void
+PhraseQueryNode::updateFieldInfo(size_t fid, size_t offset, size_t fieldLength) const
+{
+ if (fid >= _fieldInfo.size()) {
+ _fieldInfo.resize(fid + 1);
+ // only set hit offset and field length the first time
+ QueryTerm::FieldInfo & fi = _fieldInfo[fid];
+ fi.setHitOffset(offset);
+ fi.setFieldLength(fieldLength);
+ }
+ QueryTerm::FieldInfo & fi = _fieldInfo[fid];
+ fi.setHitCount(fi.getHitCount() + 1);
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.h b/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.h
new file mode 100644
index 00000000000..f28684ffeb4
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/phrase_query_node.h
@@ -0,0 +1,34 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "query.h"
+
+namespace search::streaming {
+
+/**
+ N-ary phrase operator. All terms must be satisfied and have the correct order
+ with distance to next term equal to 1.
+*/
+class PhraseQueryNode : public AndQueryNode
+{
+public:
+ PhraseQueryNode() noexcept : AndQueryNode("PHRASE"), _fieldInfo(32) { }
+ bool evaluate() const override;
+ const HitList & evaluateHits(HitList & hl) const override;
+ void getPhrases(QueryNodeRefList & tl) override;
+ void getPhrases(ConstQueryNodeRefList & tl) const override;
+ const QueryTerm::FieldInfo & getFieldInfo(size_t fid) const { return _fieldInfo[fid]; }
+ size_t getFieldInfoSize() const { return _fieldInfo.size(); }
+ bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
+ void addChild(QueryNode::UP child) override;
+private:
+ mutable std::vector<QueryTerm::FieldInfo> _fieldInfo;
+ void updateFieldInfo(size_t fid, size_t offset, size_t fieldLength) const;
+#if WE_EVER_NEED_TO_CACHE_THIS_WE_MIGHT_WANT_SOME_CODE_HERE
+ HitList _cachedHitList;
+ bool _evaluated;
+#endif
+};
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/query.cpp b/searchlib/src/vespa/searchlib/query/streaming/query.cpp
index c58e1e62e57..679141dbc2f 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/query.cpp
+++ b/searchlib/src/vespa/searchlib/query/streaming/query.cpp
@@ -1,6 +1,9 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "query.h"
-#include "hit_iterator_pack.h"
+#include "near_query_node.h"
+#include "onear_query_node.h"
+#include "phrase_query_node.h"
+#include "same_element_query_node.h"
#include <vespa/searchlib/parsequery/stackdumpiterator.h>
#include <vespa/vespalib/objects/visit.hpp>
#include <cassert>
@@ -166,157 +169,6 @@ EquivQueryNode::evaluate() const
return OrQueryNode::evaluate();
}
-bool
-SameElementQueryNode::evaluate() const {
- HitList hl;
- return ! evaluateHits(hl).empty();
-}
-
-void
-SameElementQueryNode::addChild(QueryNode::UP child) {
- assert(dynamic_cast<const QueryTerm *>(child.get()) != nullptr);
- AndQueryNode::addChild(std::move(child));
-}
-
-const HitList &
-SameElementQueryNode::evaluateHits(HitList & hl) const
-{
- hl.clear();
- if ( !AndQueryNode::evaluate()) return hl;
-
- HitList tmpHL;
- const auto & children = getChildren();
- unsigned int numFields = children.size();
- unsigned int currMatchCount = 0;
- std::vector<unsigned int> indexVector(numFields, 0);
- auto curr = static_cast<const QueryTerm *> (children[currMatchCount].get());
- bool exhausted( curr->evaluateHits(tmpHL).empty());
- for (; !exhausted; ) {
- auto next = static_cast<const QueryTerm *>(children[currMatchCount+1].get());
- unsigned int & currIndex = indexVector[currMatchCount];
- unsigned int & nextIndex = indexVector[currMatchCount+1];
-
- const auto & currHit = curr->evaluateHits(tmpHL)[currIndex];
- uint32_t currElemId = currHit.element_id();
-
- const HitList & nextHL = next->evaluateHits(tmpHL);
-
- size_t nextIndexMax = nextHL.size();
- while ((nextIndex < nextIndexMax) && (nextHL[nextIndex].element_id() < currElemId)) {
- nextIndex++;
- }
- if ((nextIndex < nextIndexMax) && (nextHL[nextIndex].element_id() == currElemId)) {
- currMatchCount++;
- if ((currMatchCount+1) == numFields) {
- Hit h = nextHL[indexVector[currMatchCount]];
- hl.emplace_back(h.field_id(), h.element_id(), h.element_weight(), 0);
- currMatchCount = 0;
- indexVector[0]++;
- }
- } else {
- currMatchCount = 0;
- indexVector[currMatchCount]++;
- }
- curr = static_cast<const QueryTerm *>(children[currMatchCount].get());
- exhausted = (nextIndex >= nextIndexMax) || (indexVector[currMatchCount] >= curr->evaluateHits(tmpHL).size());
- }
- return hl;
-}
-
-bool
-PhraseQueryNode::evaluate() const
-{
- HitList hl;
- return ! evaluateHits(hl).empty();
-}
-
-void PhraseQueryNode::getPhrases(QueryNodeRefList & tl) { tl.push_back(this); }
-void PhraseQueryNode::getPhrases(ConstQueryNodeRefList & tl) const { tl.push_back(this); }
-
-void
-PhraseQueryNode::addChild(QueryNode::UP child) {
- assert(dynamic_cast<const QueryTerm *>(child.get()) != nullptr);
- AndQueryNode::addChild(std::move(child));
-}
-
-const HitList &
-PhraseQueryNode::evaluateHits(HitList & hl) const
-{
- hl.clear();
- _fieldInfo.clear();
- HitIteratorPack itr_pack(getChildren());
- if (!itr_pack.all_valid()) {
- return hl;
- }
- auto& last_child = dynamic_cast<const QueryTerm&>(*(*this)[size() - 1]);
- while (itr_pack.seek_to_matching_field_element()) {
- uint32_t first_position = itr_pack.front()->position();
- bool retry_element = true;
- while (retry_element) {
- uint32_t position_offset = 0;
- bool match = true;
- for (auto& it : itr_pack) {
- if (!it.seek_in_field_element(first_position + position_offset, itr_pack.get_field_element_ref())) {
- retry_element = false;
- match = false;
- break;
- }
- if (it->position() > first_position + position_offset) {
- first_position = it->position() - position_offset;
- match = false;
- break;
- }
- ++position_offset;
- }
- if (match) {
- auto h = *itr_pack.back();
- hl.push_back(h);
- auto& fi = last_child.getFieldInfo(h.field_id());
- updateFieldInfo(h.field_id(), hl.size() - 1, fi.getFieldLength());
- if (!itr_pack.front().step_in_field_element(itr_pack.get_field_element_ref())) {
- retry_element = false;
- }
- }
- }
- }
- return hl;
-}
-
-void
-PhraseQueryNode::updateFieldInfo(size_t fid, size_t offset, size_t fieldLength) const
-{
- if (fid >= _fieldInfo.size()) {
- _fieldInfo.resize(fid + 1);
- // only set hit offset and field length the first time
- QueryTerm::FieldInfo & fi = _fieldInfo[fid];
- fi.setHitOffset(offset);
- fi.setFieldLength(fieldLength);
- }
- QueryTerm::FieldInfo & fi = _fieldInfo[fid];
- fi.setHitCount(fi.getHitCount() + 1);
-}
-
-bool
-NearQueryNode::evaluate() const
-{
- return AndQueryNode::evaluate();
-}
-
-void
-NearQueryNode::visitMembers(vespalib::ObjectVisitor &visitor) const
-{
- AndQueryNode::visitMembers(visitor);
- visit(visitor, "distance", static_cast<uint64_t>(_distance));
-}
-
-
-bool
-ONearQueryNode::evaluate() const
-{
- bool ok(NearQueryNode::evaluate());
- return ok;
-}
-
Query::Query() = default;
Query::Query(const QueryNodeResultFactory & factory, vespalib::stringref queryRep)
diff --git a/searchlib/src/vespa/searchlib/query/streaming/query.h b/searchlib/src/vespa/searchlib/query/streaming/query.h
index 84c693b86d0..0f15c536d44 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/query.h
+++ b/searchlib/src/vespa/searchlib/query/streaming/query.h
@@ -108,69 +108,6 @@ public:
};
/**
- N-ary phrase operator. All terms must be satisfied and have the correct order
- with distance to next term equal to 1.
-*/
-class PhraseQueryNode : public AndQueryNode
-{
-public:
- PhraseQueryNode() noexcept : AndQueryNode("PHRASE"), _fieldInfo(32) { }
- bool evaluate() const override;
- const HitList & evaluateHits(HitList & hl) const override;
- void getPhrases(QueryNodeRefList & tl) override;
- void getPhrases(ConstQueryNodeRefList & tl) const override;
- const QueryTerm::FieldInfo & getFieldInfo(size_t fid) const { return _fieldInfo[fid]; }
- size_t getFieldInfoSize() const { return _fieldInfo.size(); }
- bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
- void addChild(QueryNode::UP child) override;
-private:
- mutable std::vector<QueryTerm::FieldInfo> _fieldInfo;
- void updateFieldInfo(size_t fid, size_t offset, size_t fieldLength) const;
-#if WE_EVER_NEED_TO_CACHE_THIS_WE_MIGHT_WANT_SOME_CODE_HERE
- HitList _cachedHitList;
- bool _evaluated;
-#endif
-};
-
-class SameElementQueryNode : public AndQueryNode
-{
-public:
- SameElementQueryNode() noexcept : AndQueryNode("SAME_ELEMENT") { }
- bool evaluate() const override;
- const HitList & evaluateHits(HitList & hl) const override;
- bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
- void addChild(QueryNode::UP child) override;
-};
-
-/**
- N-ary Near operator. All terms must be within the given distance.
-*/
-class NearQueryNode : public AndQueryNode
-{
-public:
- NearQueryNode() noexcept : AndQueryNode("NEAR"), _distance(0) { }
- explicit NearQueryNode(const char * opName) noexcept : AndQueryNode(opName), _distance(0) { }
- bool evaluate() const override;
- void distance(size_t dist) { _distance = dist; }
- size_t distance() const { return _distance; }
- void visitMembers(vespalib::ObjectVisitor &visitor) const override;
- bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
-private:
- size_t _distance;
-};
-
-/**
- N-ary Ordered near operator. The terms must be in order and the distance between
- the first and last must not exceed the given distance.
-*/
-class ONearQueryNode : public NearQueryNode
-{
-public:
- ONearQueryNode() noexcept : NearQueryNode("ONEAR") { }
- bool evaluate() const override;
-};
-
-/**
Query packages the query tree. The usage pattern is like this.
Construct the tree with the correct tree description.
Get the leaf nodes and populate them with the term occurences.
diff --git a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
index e71529a8aca..6ef63dcdd7a 100644
--- a/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
+++ b/searchlib/src/vespa/searchlib/query/streaming/querynode.cpp
@@ -1,9 +1,12 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "fuzzy_term.h"
+#include "near_query_node.h"
#include "nearest_neighbor_query_node.h"
+#include "phrase_query_node.h"
#include "query.h"
#include "regexp_term.h"
+#include "same_element_query_node.h"
#include <vespa/searchlib/parsequery/stackdumpiterator.h>
#include <vespa/searchlib/query/streaming/dot_product_term.h>
#include <vespa/searchlib/query/streaming/in_term.h>
diff --git a/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.cpp b/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.cpp
new file mode 100644
index 00000000000..49d5fb0f9fb
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.cpp
@@ -0,0 +1,65 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "same_element_query_node.h"
+#include <cassert>
+
+namespace search::streaming {
+
+bool
+SameElementQueryNode::evaluate() const {
+ HitList hl;
+ return ! evaluateHits(hl).empty();
+}
+
+void
+SameElementQueryNode::addChild(QueryNode::UP child) {
+ assert(dynamic_cast<const QueryTerm *>(child.get()) != nullptr);
+ AndQueryNode::addChild(std::move(child));
+}
+
+const HitList &
+SameElementQueryNode::evaluateHits(HitList & hl) const
+{
+ hl.clear();
+ if ( !AndQueryNode::evaluate()) return hl;
+
+ HitList tmpHL;
+ const auto & children = getChildren();
+ unsigned int numFields = children.size();
+ unsigned int currMatchCount = 0;
+ std::vector<unsigned int> indexVector(numFields, 0);
+ auto curr = static_cast<const QueryTerm *> (children[currMatchCount].get());
+ bool exhausted( curr->evaluateHits(tmpHL).empty());
+ for (; !exhausted; ) {
+ auto next = static_cast<const QueryTerm *>(children[currMatchCount+1].get());
+ unsigned int & currIndex = indexVector[currMatchCount];
+ unsigned int & nextIndex = indexVector[currMatchCount+1];
+
+ const auto & currHit = curr->evaluateHits(tmpHL)[currIndex];
+ uint32_t currElemId = currHit.element_id();
+
+ const HitList & nextHL = next->evaluateHits(tmpHL);
+
+ size_t nextIndexMax = nextHL.size();
+ while ((nextIndex < nextIndexMax) && (nextHL[nextIndex].element_id() < currElemId)) {
+ nextIndex++;
+ }
+ if ((nextIndex < nextIndexMax) && (nextHL[nextIndex].element_id() == currElemId)) {
+ currMatchCount++;
+ if ((currMatchCount+1) == numFields) {
+ Hit h = nextHL[indexVector[currMatchCount]];
+ hl.emplace_back(h.field_id(), h.element_id(), h.element_weight(), 0);
+ currMatchCount = 0;
+ indexVector[0]++;
+ }
+ } else {
+ currMatchCount = 0;
+ indexVector[currMatchCount]++;
+ }
+ curr = static_cast<const QueryTerm *>(children[currMatchCount].get());
+ exhausted = (nextIndex >= nextIndexMax) || (indexVector[currMatchCount] >= curr->evaluateHits(tmpHL).size());
+ }
+ return hl;
+}
+
+}
diff --git a/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.h b/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.h
new file mode 100644
index 00000000000..50a479eda94
--- /dev/null
+++ b/searchlib/src/vespa/searchlib/query/streaming/same_element_query_node.h
@@ -0,0 +1,22 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "query.h"
+
+namespace search::streaming {
+
+/**
+ N-ary Same element operator. All terms must be within the same element.
+*/
+class SameElementQueryNode : public AndQueryNode
+{
+public:
+ SameElementQueryNode() noexcept : AndQueryNode("SAME_ELEMENT") { }
+ bool evaluate() const override;
+ const HitList & evaluateHits(HitList & hl) const override;
+ bool isFlattenable(ParseItem::ItemType type) const override { return type == ParseItem::ITEM_NOT; }
+ void addChild(QueryNode::UP child) override;
+};
+
+}