diff options
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(); + } }; //----------------------------------------------------------------------------- |