summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2019-10-08 15:19:27 +0000
committerHåvard Pettersen <havardpe@oath.com>2019-10-08 15:25:56 +0000
commit0f21137ac41a01ce9a77460b67d96fe7d76c2983 (patch)
tree00447d787793b5092ffe827ccf776bbf17e7bf1d
parent60f301c40a1a57634247f3d240fdd1f2341a4420 (diff)
improve fake searchable attributes
blueprint/search iterator now exposes a more functional attribute search context fake attribute searches will now unpack a single entry containing the sum of all matched weights improve matcher test for same element matching by also using the attribute element iterator added tests for identifying matching elements in docsum request
-rw-r--r--searchcore/src/tests/proton/matching/matching_test.cpp78
-rw-r--r--searchcore/src/vespa/searchcore/proton/matching/fakesearchcontext.cpp4
-rw-r--r--searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt1
-rw-r--r--searchlib/src/tests/queryeval/fake_searchable/fake_searchable_test.cpp273
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_search.cpp41
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_search.h10
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_searchable.cpp17
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/fake_searchable.h16
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp49
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h13
10 files changed, 297 insertions, 205 deletions
diff --git a/searchcore/src/tests/proton/matching/matching_test.cpp b/searchcore/src/tests/proton/matching/matching_test.cpp
index 950321533b0..05ca9e758ab 100644
--- a/searchcore/src/tests/proton/matching/matching_test.cpp
+++ b/searchcore/src/tests/proton/matching/matching_test.cpp
@@ -91,7 +91,7 @@ vespalib::string make_simple_stack_dump(const vespalib::string &field, const ves
vespalib::string make_same_element_stack_dump(const vespalib::string &a1_term, const vespalib::string &f1_term)
{
QueryBuilder<ProtonNodeTypes> builder;
- builder.addSameElement(2, "");
+ builder.addSameElement(2, "my");
builder.addStringTerm(a1_term, "a1", 1, search::query::Weight(1));
builder.addStringTerm(f1_term, "f1", 2, search::query::Weight(1));
return StackDumpCreator::create(*builder.build());
@@ -129,10 +129,12 @@ struct MyWorld {
schema.addIndexField(Schema::IndexField("f1", DataType::STRING));
schema.addIndexField(Schema::IndexField("f2", DataType::STRING));
schema.addIndexField(Schema::IndexField("tensor_field", DataType::TENSOR));
+ schema.addIndexField(Schema::IndexField("my.f1", DataType::STRING));
schema.addAttributeField(Schema::AttributeField("a1", DataType::INT32));
schema.addAttributeField(Schema::AttributeField("a2", DataType::INT32));
schema.addAttributeField(Schema::AttributeField("a3", DataType::INT32));
schema.addAttributeField(Schema::AttributeField("predicate_field", DataType::BOOLEANTREE));
+ schema.addAttributeField(Schema::AttributeField("my.a1", DataType::STRING));
// config
config.add(indexproperties::rank::FirstPhase::NAME, "attribute(a1)");
@@ -237,11 +239,11 @@ struct MyWorld {
searchContext.attr().addResult("a1", term, result);
}
- void add_same_element_results(const vespalib::string &a1_term, const vespalib::string &f1_0_term) {
- auto a1_result = make_elem_result({{10, {1}}, {20, {2}}, {21, {2}}});
- auto f1_0_result = make_elem_result({{10, {2}}, {20, {2}}, {21, {2}}});
- searchContext.attr().addResult("a1", a1_term, a1_result);
- searchContext.idx(0).getFake().addResult("f1", f1_0_term, f1_0_result);
+ void add_same_element_results(const vespalib::string &my_a1_term, const vespalib::string &my_f1_0_term) {
+ auto my_a1_result = make_elem_result({{10, {1}}, {20, {2, 3}}, {21, {2}}});
+ auto my_f1_0_result = make_elem_result({{10, {2}}, {20, {1, 2}}, {21, {2}}});
+ searchContext.attr().addResult("my.a1", my_a1_term, my_a1_result);
+ searchContext.idx(0).getFake().addResult("my.f1", my_f1_0_term, my_f1_0_result);
}
void basicResults() {
@@ -323,20 +325,20 @@ struct MyWorld {
return reply;
}
- DocsumRequest::SP createSimpleDocsumRequest(const vespalib::string & field, const vespalib::string & term)
- {
- DocsumRequest::SP request(new DocsumRequest);
- setStackDump(*request, make_simple_stack_dump(field, term));
+ DocsumRequest::UP create_docsum_request(const vespalib::string &stack_dump, const std::initializer_list<uint32_t> docs) {
+ auto req = std::make_unique<DocsumRequest>();
+ setStackDump(*req, stack_dump);
+ for (uint32_t docid: docs) {
+ req->hits.push_back(DocsumRequest::Hit());
+ req->hits.back().docid = docid;
+ }
+ return req;
+ }
+ DocsumRequest::SP createSimpleDocsumRequest(const vespalib::string & field, const vespalib::string & term) {
// match a subset of basic result + request for a non-hit (not
// sorted on docid)
- request->hits.push_back(DocsumRequest::Hit());
- request->hits.back().docid = 30;
- request->hits.push_back(DocsumRequest::Hit());
- request->hits.back().docid = 10;
- request->hits.push_back(DocsumRequest::Hit());
- request->hits.back().docid = 15;
- return request;
+ return create_docsum_request(make_simple_stack_dump(field, term), {30, 10, 15});
}
std::unique_ptr<FieldInfo> get_field_info(const vespalib::string &field_name) {
@@ -350,14 +352,21 @@ struct MyWorld {
FeatureSet::SP getSummaryFeatures(DocsumRequest::SP req) {
Matcher::SP matcher = createMatcher();
- return matcher->getSummaryFeatures(*req, searchContext, attributeContext, *sessionManager);
+ auto docsum_matcher = matcher->create_docsum_matcher(*req, searchContext, attributeContext, *sessionManager);
+ return docsum_matcher->get_summary_features();
}
FeatureSet::SP getRankFeatures(DocsumRequest::SP req) {
Matcher::SP matcher = createMatcher();
- return matcher->getRankFeatures(*req, searchContext, attributeContext, *sessionManager);
+ auto docsum_matcher = matcher->create_docsum_matcher(*req, searchContext, attributeContext, *sessionManager);
+ return docsum_matcher->get_rank_features();
}
+ MatchingElements::UP get_matching_elements(const DocsumRequest &req, const StructFieldMapper &mapper) {
+ Matcher::SP matcher = createMatcher();
+ auto docsum_matcher = matcher->create_docsum_matcher(req, searchContext, attributeContext, *sessionManager);
+ return docsum_matcher->get_matching_elements(mapper);
+ }
};
MyWorld::MyWorld()
@@ -898,7 +907,7 @@ TEST("require that fields are tagged with data type") {
EXPECT_EQUAL(predicate_field->get_data_type(), FieldInfo::DataType::BOOLEANTREE);
}
-TEST("require that same element search works (note that this does not test/use the attribute element iterator wrapper)") {
+TEST("require that same element search works") {
MyWorld world;
world.basicSetup();
world.add_same_element_results("foo", "bar");
@@ -908,4 +917,33 @@ TEST("require that same element search works (note that this does not test/use t
EXPECT_EQUAL(document::DocumentId("id:ns:searchdocument::20").getGlobalId(), reply->hits[0].gid);
}
+TEST("require that docsum matcher can extract matching elements from same element blueprint") {
+ MyWorld world;
+ world.basicSetup();
+ world.add_same_element_results("foo", "bar");
+ auto request = world.create_docsum_request(make_same_element_stack_dump("foo", "bar"), {20});
+ StructFieldMapper mapper;
+ mapper.add_mapping("my", "my.a1");
+ mapper.add_mapping("my", "my.f1");
+ auto result = world.get_matching_elements(*request, mapper);
+ const auto &list = result->get_matching_elements(20, "my");
+ ASSERT_EQUAL(list.size(), 1u);
+ EXPECT_EQUAL(list[0], 2u);
+}
+
+TEST("require that docsum matcher can extract matching elements from single attribute term") {
+ MyWorld world;
+ world.basicSetup();
+ world.add_same_element_results("foo", "bar");
+ auto request = world.create_docsum_request(make_simple_stack_dump("my.a1", "foo"), {20});
+ StructFieldMapper mapper;
+ mapper.add_mapping("my", "my.a1");
+ mapper.add_mapping("my", "my.f1");
+ auto result = world.get_matching_elements(*request, mapper);
+ const auto &list = result->get_matching_elements(20, "my");
+ ASSERT_EQUAL(list.size(), 2u);
+ EXPECT_EQUAL(list[0], 2u);
+ EXPECT_EQUAL(list[1], 3u);
+}
+
TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/searchcore/src/vespa/searchcore/proton/matching/fakesearchcontext.cpp b/searchcore/src/vespa/searchcore/proton/matching/fakesearchcontext.cpp
index 11cf2713528..36129640c0e 100644
--- a/searchcore/src/vespa/searchcore/proton/matching/fakesearchcontext.cpp
+++ b/searchcore/src/vespa/searchcore/proton/matching/fakesearchcontext.cpp
@@ -12,7 +12,9 @@ FakeSearchContext::FakeSearchContext(size_t initialNumDocs)
_indexes(new IndexCollection(_selector)),
_attrSearchable(),
_docIdLimit(initialNumDocs)
-{}
+{
+ _attrSearchable.is_attr(true);
+}
FakeSearchContext::~FakeSearchContext() {}
diff --git a/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt b/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
index e5f5e6aadf3..c98306aa179 100644
--- a/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
+++ b/searchlib/src/tests/queryeval/fake_searchable/CMakeLists.txt
@@ -4,5 +4,6 @@ vespa_add_executable(searchlib_fake_searchable_test_app TEST
fake_searchable_test.cpp
DEPENDS
searchlib
+ gtest
)
vespa_add_test(NAME searchlib_fake_searchable_test_app COMMAND searchlib_fake_searchable_test_app)
diff --git a/searchlib/src/tests/queryeval/fake_searchable/fake_searchable_test.cpp b/searchlib/src/tests/queryeval/fake_searchable/fake_searchable_test.cpp
index 661ca9c2ba3..6cef4479439 100644
--- a/searchlib/src/tests/queryeval/fake_searchable/fake_searchable_test.cpp
+++ b/searchlib/src/tests/queryeval/fake_searchable/fake_searchable_test.cpp
@@ -1,5 +1,6 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/vespalib/testkit/testapp.h>
+
+#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/searchlib/queryeval/fake_searchable.h>
#include <vespa/searchlib/queryeval/fake_requestcontext.h>
@@ -8,76 +9,58 @@
#include <vespa/searchlib/query/tree/simplequery.h>
#include <vespa/searchlib/fef/matchdata.h>
-#include <vespa/log/log.h>
-LOG_SETUP("fake_searchable_test");
-
using namespace search::queryeval;
using namespace search::query;
using namespace search::fef;
-class Test : public vespalib::TestApp {
-public:
- ~Test();
- int Main() override;
- void testTestFakeResult();
- void testTerm();
- void testPhrase();
- void testWeightedSet();
- void testMultiField();
- void testPhraseWithEmptyChild();
-private:
- FakeRequestContext _requestContext;
+struct FakeSearchableTest : ::testing::Test {
+ Weight w;
+ FakeRequestContext req_ctx;
+ FakeSearchable source;
+ FakeSearchableTest() : w(100), req_ctx(), source() {}
};
-Test::~Test() {}
-void
-Test::testTestFakeResult()
-{
- EXPECT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6).elem(6).doc(6),
- FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6).elem(6).doc(6));
+TEST(FakeResultTest, require_that_fake_result_works) {
+ EXPECT_EQ(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6).elem(6).doc(6),
+ FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6).elem(6).doc(6));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(1).elem(5).len(15).weight(5).pos(5));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(1).elem(5).len(15).weight(5).pos(5));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(1).len(15).weight(5).pos(5));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(1).len(15).weight(5).pos(5));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(19).weight(5).pos(5));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(19).weight(5).pos(5));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(15).weight(1).pos(5));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(15).weight(1).pos(5));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(15).weight(5).pos(1));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(15).weight(5).pos(1));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).doc(6));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).doc(6));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).elem(6));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).elem(6));
- EXPECT_NOT_EQUAL(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5),
- FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6));
+ EXPECT_FALSE(FakeResult().doc(5).elem(5).len(15).weight(5).pos(5) ==
+ FakeResult().doc(5).elem(5).len(15).weight(5).pos(5).pos(6));
}
-void
-Test::testTerm()
-{
- Weight w(100);
-
- FakeSearchable source;
+TEST_F(FakeSearchableTest, require_that_term_search_works) {
source.addResult("fieldfoo", "word1",
- FakeResult().doc(5).pos(3));
+ FakeResult().doc(5).elem(2).pos(3).elem(4).pos(5));
SimpleStringTerm termNode("word1", "viewfoo", 1, w);
FieldSpecList fields;
fields.add(FieldSpec("fieldfoo", 1, 1));
- Blueprint::UP bp = source.createBlueprint(_requestContext, fields, termNode);
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, termNode);
for (int i = 0; i <= 1; ++i) {
bool strict = (i == 0);
- TEST_STATE(strict ? "strict" : "non-strict");
+ SCOPED_TRACE(strict ? "strict" : "non-strict");
MatchData::UP md = MatchData::makeTestInstance(100, 10);
bp->fetchPostings(strict);
SearchIterator::UP search = bp->createSearch(*md, strict);
@@ -85,21 +68,26 @@ Test::testTerm()
EXPECT_TRUE(!search->seek(3));
if (strict) {
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
} else {
EXPECT_TRUE(search->seek(5u));
}
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
{ // test doc 5 results
search->unpack(5u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_EQUAL(5u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(5u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(2u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(3u, itr.getPosition());
+ EXPECT_EQ(2u, itr.getElementId());
+ EXPECT_EQ(3u, itr.getPosition());
+ itr.next();
+ ASSERT_TRUE(itr.valid());
+ EXPECT_EQ(4u, itr.getElementId());
+ EXPECT_EQ(5u, itr.getPosition());
itr.next();
EXPECT_TRUE(!itr.valid());
}
@@ -111,12 +99,7 @@ Test::testTerm()
}
}
-void
-Test::testPhrase()
-{
- Weight w(100);
-
- FakeSearchable source;
+TEST_F(FakeSearchableTest, require_that_phrase_search_works) {
source.addResult("fieldfoo", "word1",
FakeResult().doc(3).pos(7).doc(5).pos(3));
source.addResult("fieldfoo", "word2",
@@ -128,10 +111,10 @@ Test::testPhrase()
FieldSpecList fields;
fields.add(FieldSpec("fieldfoo", 1, 1));
- Blueprint::UP bp = source.createBlueprint(_requestContext, fields, phraseNode);
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, phraseNode);
for (int i = 0; i <= 1; ++i) {
bool strict = (i == 0);
- TEST_STATE(strict ? "strict" : "non-strict");
+ SCOPED_TRACE(strict ? "strict" : "non-strict");
MatchData::UP md = MatchData::makeTestInstance(100, 10);
bp->fetchPostings(strict);
SearchIterator::UP search = bp->createSearch(*md, strict);
@@ -139,21 +122,21 @@ Test::testPhrase()
EXPECT_TRUE(!search->seek(3));
if (strict) {
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
} else {
EXPECT_TRUE(search->seek(5u));
}
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
{ // test doc 5 results
search->unpack(5u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_EQUAL(5u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(5u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(1u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(3u, itr.getPosition());
+ EXPECT_EQ(3u, itr.getPosition());
itr.next();
EXPECT_TRUE(!itr.valid());
}
@@ -165,12 +148,7 @@ Test::testPhrase()
}
}
-void
-Test::testWeightedSet()
-{
- Weight w(100);
-
- FakeSearchable source;
+TEST_F(FakeSearchableTest, require_that_weigheted_set_search_works) {
source.addResult("fieldfoo", "friend1",
FakeResult().doc(3).doc(5).doc(7).doc(9));
source.addResult("fieldfoo", "friend2",
@@ -184,10 +162,10 @@ Test::testWeightedSet()
FieldSpecList fields;
fields.add(FieldSpec("fieldfoo", 1, 1));
- Blueprint::UP bp = source.createBlueprint(_requestContext, fields, weightedSet);
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, weightedSet);
for (int i = 0; i <= 1; ++i) {
bool strict = (i == 0);
- TEST_STATE(strict ? "strict" : "non-strict");
+ SCOPED_TRACE(strict ? "strict" : "non-strict");
MatchData::UP md = MatchData::makeTestInstance(100, 10);
bp->fetchPostings(strict);
SearchIterator::UP search = bp->createSearch(*md, strict);
@@ -195,24 +173,24 @@ Test::testWeightedSet()
EXPECT_TRUE(!search->seek(2));
if (strict) {
- EXPECT_EQUAL(3u, search->getDocId());
+ EXPECT_EQ(3u, search->getDocId());
} else {
EXPECT_TRUE(search->seek(3u));
}
- EXPECT_EQUAL(3u, search->getDocId());
+ EXPECT_EQ(3u, search->getDocId());
{ // test doc 3 results
search->unpack(3u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_EQUAL(3u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(3u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(2u, itr.size());
+ EXPECT_EQ(2u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(2, itr.getElementWeight());
+ EXPECT_EQ(2, itr.getElementWeight());
itr.next();
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(1, itr.getElementWeight());
+ EXPECT_EQ(1, itr.getElementWeight());
itr.next();
EXPECT_TRUE(!itr.valid());
}
@@ -227,12 +205,12 @@ Test::testWeightedSet()
search->unpack(9u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_EQUAL(9u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(9u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(1u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(1, itr.getElementWeight());
+ EXPECT_EQ(1, itr.getElementWeight());
itr.next();
EXPECT_TRUE(!itr.valid());
}
@@ -244,12 +222,7 @@ Test::testWeightedSet()
}
}
-void
-Test::testMultiField()
-{
- Weight w(100);
-
- FakeSearchable source;
+TEST_F(FakeSearchableTest, require_that_multi_field_search_works) {
source.addResult("fieldfoo", "word1",
FakeResult().doc(5).pos(3));
source.addResult("fieldbar", "word1",
@@ -260,10 +233,10 @@ Test::testMultiField()
FieldSpecList fields;
fields.add(FieldSpec("fieldfoo", 1, 1));
fields.add(FieldSpec("fieldbar", 2, 2));
- Blueprint::UP bp = source.createBlueprint(_requestContext, fields, termNode);
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, termNode);
for (int i = 0; i <= 1; ++i) {
bool strict = (i == 0);
- TEST_STATE(strict ? "strict" : "non-strict");
+ SCOPED_TRACE(strict ? "strict" : "non-strict");
MatchData::UP md = MatchData::makeTestInstance(100, 10);
bp->fetchPostings(strict);
SearchIterator::UP search = bp->createSearch(*md, strict);
@@ -271,58 +244,58 @@ Test::testMultiField()
EXPECT_TRUE(!search->seek(3));
if (strict) {
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
} else {
EXPECT_TRUE(search->seek(5u));
}
- EXPECT_EQUAL(5u, search->getDocId());
+ EXPECT_EQ(5u, search->getDocId());
{ // test doc 5 results
search->unpack(5u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_EQUAL(5u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(5u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(1u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(3u, itr.getPosition());
+ EXPECT_EQ(3u, itr.getPosition());
itr.next();
EXPECT_TRUE(!itr.valid());
}
{
TermFieldMatchData &data = *md->resolveTermField(2);
- EXPECT_EQUAL(2u, data.getFieldId());
- EXPECT_EQUAL(5u, data.getDocId());
+ EXPECT_EQ(2u, data.getFieldId());
+ EXPECT_EQ(5u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(1u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(7u, itr.getPosition());
+ EXPECT_EQ(7u, itr.getPosition());
itr.next();
EXPECT_TRUE(!itr.valid());
}
}
EXPECT_TRUE(!search->seek(7));
if (strict) {
- EXPECT_EQUAL(10u, search->getDocId());
+ EXPECT_EQ(10u, search->getDocId());
} else {
EXPECT_TRUE(search->seek(10u));
}
- EXPECT_EQUAL(10u, search->getDocId());
+ EXPECT_EQ(10u, search->getDocId());
{ // test doc 10 results
search->unpack(10u);
{
TermFieldMatchData &data = *md->resolveTermField(1);
- EXPECT_EQUAL(1u, data.getFieldId());
- EXPECT_NOT_EQUAL(10u, data.getDocId());
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_NE(10u, data.getDocId());
}
{
TermFieldMatchData &data = *md->resolveTermField(2);
- EXPECT_EQUAL(2u, data.getFieldId());
- EXPECT_EQUAL(10u, data.getDocId());
+ EXPECT_EQ(2u, data.getFieldId());
+ EXPECT_EQ(10u, data.getDocId());
FieldPositionsIterator itr = data.getIterator();
- EXPECT_EQUAL(1u, itr.size());
+ EXPECT_EQ(1u, itr.size());
ASSERT_TRUE(itr.valid());
- EXPECT_EQUAL(2u, itr.getPosition());
+ EXPECT_EQ(2u, itr.getPosition());
itr.next();
EXPECT_TRUE(!itr.valid());
}
@@ -334,12 +307,7 @@ Test::testMultiField()
}
}
-void
-Test::testPhraseWithEmptyChild()
-{
- Weight w(100);
-
- FakeSearchable source;
+TEST_F(FakeSearchableTest, require_that_phrase_with_empty_child_works) {
source.addResult("fieldfoo", "word1",
FakeResult().doc(3).pos(7).doc(5).pos(3));
@@ -349,10 +317,10 @@ Test::testPhraseWithEmptyChild()
FieldSpecList fields;
fields.add(FieldSpec("fieldfoo", 1, 1));
- Blueprint::UP bp = source.createBlueprint(_requestContext, fields, phraseNode);
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, phraseNode);
for (int i = 0; i <= 1; ++i) {
bool strict = (i == 0);
- TEST_STATE(strict ? "strict" : "non-strict");
+ SCOPED_TRACE(strict ? "strict" : "non-strict");
MatchData::UP md = MatchData::makeTestInstance(100, 10);
bp->fetchPostings(strict);
SearchIterator::UP search = bp->createSearch(*md, strict);
@@ -365,17 +333,58 @@ Test::testPhraseWithEmptyChild()
}
}
-int
-Test::Main()
-{
- TEST_INIT("fake_searchable_test");
- testTestFakeResult();
- testTerm();
- testPhrase();
- testWeightedSet();
- testMultiField();
- testPhraseWithEmptyChild();
- TEST_DONE();
+TEST_F(FakeSearchableTest, require_that_match_data_is_compressed_for_attributes) {
+ source.is_attr(true);
+ source.addResult("attrfoo", "word1",
+ FakeResult().doc(5).elem(2).weight(6).pos(3).elem(4).weight(8).pos(5));
+ SimpleStringTerm termNode("word1", "viewfoo", 1, w);
+ FieldSpecList fields;
+ fields.add(FieldSpec("attrfoo", 1, 1));
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, termNode);
+ MatchData::UP md = MatchData::makeTestInstance(100, 10);
+ bp->fetchPostings(false);
+ SearchIterator::UP search = bp->createSearch(*md, false);
+ search->initFullRange();
+ EXPECT_TRUE(search->seek(5));
+ search->unpack(5u);
+ {
+ TermFieldMatchData &data = *md->resolveTermField(1);
+ EXPECT_EQ(1u, data.getFieldId());
+ EXPECT_EQ(5u, data.getDocId());
+ FieldPositionsIterator itr = data.getIterator();
+ EXPECT_EQ(1u, itr.size());
+ ASSERT_TRUE(itr.valid());
+ EXPECT_EQ(14u, itr.getElementWeight()); // 6 + 8
+ itr.next();
+ EXPECT_TRUE(!itr.valid());
+ }
+}
+
+TEST_F(FakeSearchableTest, require_that_relevant_data_can_be_obtained_from_fake_attribute_search_context) {
+ source.is_attr(true);
+ source.addResult("attrfoo", "word1",
+ FakeResult().doc(5).elem(2).weight(6).pos(3).elem(4).weight(8).pos(5));
+ SimpleStringTerm termNode("word1", "viewfoo", 1, w);
+ FieldSpecList fields;
+ fields.add(FieldSpec("attrfoo", 1, 1));
+ Blueprint::UP bp = source.createBlueprint(req_ctx, fields, termNode);
+ MatchData::UP md = MatchData::makeTestInstance(100, 10);
+ bp->fetchPostings(false);
+ SearchIterator::UP search = bp->createSearch(*md, false);
+ EXPECT_EQ(bp->get_attribute_search_context(), search->getAttributeSearchContext());
+ const auto *attr_ctx = bp->get_attribute_search_context();
+ ASSERT_TRUE(attr_ctx);
+ EXPECT_EQ(attr_ctx->attributeName(), "attrfoo");
+ int32_t elem_weight = 0;
+ EXPECT_EQ(attr_ctx->find(4, 0, elem_weight), -1);
+ int32_t elem_id = attr_ctx->find(5, 0, elem_weight);
+ EXPECT_EQ(elem_id, 2);
+ EXPECT_EQ(elem_weight, 6);
+ elem_id = attr_ctx->find(5, 3, elem_weight);
+ EXPECT_EQ(elem_id, 4);
+ EXPECT_EQ(elem_weight, 8);
+ EXPECT_EQ(attr_ctx->find(5, 5, elem_weight), -1);
+ EXPECT_EQ(attr_ctx->find(6, 0, elem_weight), -1);
}
-TEST_APPHOOK(Test);
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp b/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
index 099e28f552a..4ee214b0f16 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_search.cpp
@@ -9,32 +9,6 @@
namespace search {
namespace queryeval {
-namespace {
-
-struct FakeContext : search::attribute::ISearchContext {
- int32_t onFind(DocId, int32_t, int32_t &) const override { return -1; }
- int32_t onFind(DocId, int32_t) const override { return -1; }
- unsigned int approximateHits() const override { return 0; }
- std::unique_ptr<SearchIterator> createIterator(fef::TermFieldMatchData *, bool) override { abort(); }
- void fetchPostings(bool) override { }
- bool valid() const override { return true; }
- search::Int64Range getAsIntegerTerm() const override { abort(); }
- const search::QueryTermBase * queryTerm() const override { abort(); }
- const vespalib::string &attributeName() const override { abort(); }
-};
-
-} // namespace search::queryeval::<unnamed>
-
-void
-FakeSearch::is_attr(bool value)
-{
- if (value) {
- _ctx = std::make_unique<FakeContext>();
- } else {
- _ctx.reset();
- }
-}
-
void
FakeSearch::doSeek(uint32_t docid)
{
@@ -59,13 +33,20 @@ FakeSearch::doUnpack(uint32_t docid)
const Doc &doc = _result.inspect()[_offset];
assert(doc.docId == docid);
_tfmda[0]->reset(docid);
+ int32_t sum_weight = 0;
for (uint32_t i = 0; i < doc.elements.size(); ++i) {
- const Elem &elem =doc.elements[i];
- for (uint32_t j = 0; j < elem.positions.size(); ++j) {
- _tfmda[0]->appendPosition(PosCtx(elem.id, elem.positions[j],
- elem.weight, elem.length));
+ const Elem &elem = doc.elements[i];
+ sum_weight += elem.weight;
+ if (!is_attr()) {
+ for (uint32_t j = 0; j < elem.positions.size(); ++j) {
+ _tfmda[0]->appendPosition(PosCtx(elem.id, elem.positions[j],
+ elem.weight, elem.length));
+ }
}
}
+ if (is_attr()) {
+ _tfmda[0]->appendPosition(PosCtx(0, 0, sum_weight, 1));
+ }
}
void
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_search.h b/searchlib/src/vespa/searchlib/queryeval/fake_search.h
index aa6df480a21..e629e849408 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_search.h
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_search.h
@@ -19,7 +19,7 @@ private:
FakeResult _result;
uint32_t _offset;
fef::TermFieldMatchDataArray _tfmda;
- std::unique_ptr<attribute::ISearchContext> _ctx;
+ const attribute::ISearchContext *_ctx;
bool valid() const { return _offset < _result.inspect().size(); }
uint32_t currId() const { return _result.inspect()[_offset].docId; }
@@ -32,16 +32,18 @@ public:
const FakeResult &res,
const fef::TermFieldMatchDataArray &tfmda)
: _tag(tag), _field(field), _term(term),
- _result(res), _offset(0), _tfmda(tfmda)
+ _result(res), _offset(0), _tfmda(tfmda),
+ _ctx(nullptr)
{
assert(_tfmda.size() == 1);
}
- void is_attr(bool value);
+ void attr_ctx(const attribute::ISearchContext *ctx) { _ctx = ctx; }
+ bool is_attr() const { return (_ctx != nullptr); }
void doSeek(uint32_t docid) override;
void doUnpack(uint32_t docid) override;
const PostingInfo *getPostingInfo() const override { return _result.postingInfo(); }
void visitMembers(vespalib::ObjectVisitor &visitor) const override;
- const attribute::ISearchContext *getAttributeSearchContext() const override { return _ctx.get(); }
+ const attribute::ISearchContext *getAttributeSearchContext() const override { return _ctx; }
};
} // namespace queryeval
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_searchable.cpp b/searchlib/src/vespa/searchlib/queryeval/fake_searchable.cpp
index 997f9afc54f..4c678a9902f 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_searchable.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_searchable.cpp
@@ -21,7 +21,8 @@ namespace search::queryeval {
FakeSearchable::FakeSearchable()
: _tag("<undef>"),
- _map()
+ _map(),
+ _is_attr(false)
{
}
@@ -44,10 +45,11 @@ class LookupVisitor : public CreateBlueprintVisitorHelper
{
const Map &_map;
const vespalib::string _tag;
+ bool _is_attr;
public:
LookupVisitor(Searchable &searchable, const IRequestContext & requestContext,
- const Map &map, const vespalib::string &tag, const FieldSpec &field);
+ const Map &map, const vespalib::string &tag, bool is_attr, const FieldSpec &field);
~LookupVisitor();
template <class TermNode>
@@ -66,10 +68,11 @@ public:
template <class Map>
LookupVisitor<Map>::LookupVisitor(Searchable &searchable, const IRequestContext & requestContext,
- const Map &map, const vespalib::string &tag, const FieldSpec &field)
+ const Map &map, const vespalib::string &tag, bool is_attr, const FieldSpec &field)
: CreateBlueprintVisitorHelper(searchable, field, requestContext),
_map(map),
- _tag(tag)
+ _tag(tag),
+ _is_attr(is_attr)
{}
template <class Map>
@@ -86,8 +89,8 @@ LookupVisitor<Map>::visitTerm(TermNode &n) {
if (pos != _map.end()) {
result = pos->second;
}
- auto fake = std::make_unique<FakeBlueprint>(getField(), result);
- fake->tag(_tag).term(term_string);
+ auto fake = std::make_unique<FakeBlueprint>(getField(), result);
+ fake->tag(_tag).is_attr(_is_attr).term(term_string);
setResult(std::move(fake));
}
@@ -98,7 +101,7 @@ FakeSearchable::createBlueprint(const IRequestContext & requestContext,
const FieldSpec &field,
const search::query::Node &term)
{
- LookupVisitor<Map> visitor(*this, requestContext, _map, _tag, field);
+ LookupVisitor<Map> visitor(*this, requestContext, _map, _tag, _is_attr, field);
const_cast<Node &>(term).accept(visitor);
return visitor.getResult();
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/fake_searchable.h b/searchlib/src/vespa/searchlib/queryeval/fake_searchable.h
index 8f95c116bf5..a47ac18d88b 100644
--- a/searchlib/src/vespa/searchlib/queryeval/fake_searchable.h
+++ b/searchlib/src/vespa/searchlib/queryeval/fake_searchable.h
@@ -17,11 +17,11 @@ class FakeSearchable : public Searchable
{
private:
typedef std::pair<vespalib::string, vespalib::string> Key;
- typedef FakeResult Value;
- typedef std::map<Key, Value> Map;
+ typedef std::map<Key, FakeResult> Map;
vespalib::string _tag;
- Map _map;
+ Map _map;
+ bool _is_attr;
public:
/**
@@ -42,6 +42,16 @@ public:
}
/**
+ * Is this searchable searching attributes? Setting this to true
+ * will result in blueprints and search iterators exposing a
+ * mocked attribute search context interface.
+ **/
+ FakeSearchable &is_attr(bool value) {
+ _is_attr = value;
+ return *this;
+ }
+
+ /**
* Add a fake result to be returned for lookup on the given field
* and term combination.
*
diff --git a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
index a140fb146d5..042de113a35 100644
--- a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.cpp
@@ -61,11 +61,46 @@ SimpleBlueprint::tag(const vespalib::string &t)
//-----------------------------------------------------------------------------
+namespace {
+
+struct FakeContext : attribute::ISearchContext {
+ const vespalib::string &name;
+ const FakeResult &result;
+ FakeContext(const vespalib::string &name_in, const FakeResult &result_in)
+ : name(name_in), result(result_in) {}
+ int32_t onFind(DocId docid, int32_t elemid, int32_t &weight) const override {
+ for (const auto &doc: result.inspect()) {
+ if (doc.docId == docid) {
+ for (const auto &elem: doc.elements) {
+ if (elem.id >= uint32_t(elemid)) {
+ weight = elem.weight;
+ return elem.id;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+ int32_t onFind(DocId docid, int32_t elem) const override {
+ int32_t ignore_weight;
+ return onFind(docid, elem, ignore_weight);
+ }
+ unsigned int approximateHits() const override { return 0; }
+ std::unique_ptr<SearchIterator> createIterator(fef::TermFieldMatchData *, bool) override { abort(); }
+ void fetchPostings(bool) override { }
+ bool valid() const override { return true; }
+ search::Int64Range getAsIntegerTerm() const override { abort(); }
+ const search::QueryTermBase * queryTerm() const override { abort(); }
+ const vespalib::string &attributeName() const override { return name; }
+};
+
+}
+
SearchIterator::UP
FakeBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &tfmda, bool) const
{
auto result = std::make_unique<FakeSearch>(_tag, _field.getName(), _term, _result, tfmda);
- result->is_attr(_is_attr);
+ result->attr_ctx(_ctx.get());
return result;
}
@@ -75,11 +110,21 @@ FakeBlueprint::FakeBlueprint(const FieldSpec &field, const FakeResult &result)
_term("<term>"),
_field(field),
_result(result),
- _is_attr(false)
+ _ctx()
{
setEstimate(HitEstimate(result.inspect().size(), result.inspect().empty()));
}
FakeBlueprint::~FakeBlueprint() = default;
+FakeBlueprint &
+FakeBlueprint::is_attr(bool value) {
+ if (value) {
+ _ctx = std::make_unique<FakeContext>(_field.getName(), _result);
+ } else {
+ _ctx.reset();
+ }
+ return *this;
+}
+
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
index 85d30aaf003..2dc2d938bb6 100644
--- a/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
+++ b/searchlib/src/vespa/searchlib/queryeval/leaf_blueprints.h
@@ -51,7 +51,7 @@ private:
vespalib::string _term;
FieldSpec _field;
FakeResult _result;
- bool _is_attr;
+ std::unique_ptr<attribute::ISearchContext> _ctx;
protected:
SearchIterator::UP
@@ -67,16 +67,17 @@ public:
}
const vespalib::string &tag() const { return _tag; }
- FakeBlueprint &is_attr(bool value) {
- _is_attr = value;
- return *this;
- }
- bool is_attr() const { return _is_attr; }
+ FakeBlueprint &is_attr(bool value);
+ bool is_attr() const { return bool(_ctx); }
FakeBlueprint &term(const vespalib::string &t) {
_term = t;
return *this;
}
+
+ const attribute::ISearchContext *get_attribute_search_context() const override {
+ return _ctx.get();
+ }
};
//-----------------------------------------------------------------------------