diff options
author | Håvard Pettersen <havardpe@oath.com> | 2019-10-08 15:19:27 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2019-10-08 15:25:56 +0000 |
commit | 0f21137ac41a01ce9a77460b67d96fe7d76c2983 (patch) | |
tree | 00447d787793b5092ffe827ccf776bbf17e7bf1d /searchlib | |
parent | 60f301c40a1a57634247f3d240fdd1f2341a4420 (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
Diffstat (limited to 'searchlib')
8 files changed, 236 insertions, 184 deletions
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(); + } }; //----------------------------------------------------------------------------- |