// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include #include #include #include #include #include #include #include #include using namespace search; using namespace search::query; using namespace search::fef; using namespace search::queryeval; using search::test::SearchIteratorVerifier; using search::test::DocumentWeightAttributeHelper; namespace { void setupFakeSearchable(FakeSearchable &fake) { for (size_t docid = 1; docid < 10; ++docid) { std::string token1 = vespalib::make_string("%zu", docid); std::string token2 = vespalib::make_string("1%zu", docid); std::string token3 = vespalib::make_string("2%zu", docid); fake.addResult("field", token1, FakeResult().doc(docid)); fake.addResult("multi-field", token1, FakeResult().doc(docid)); fake.addResult("multi-field", token2, FakeResult().doc(docid)); fake.addResult("multi-field", token3, FakeResult().doc(docid)); } } struct WS { static const uint32_t fieldId = 42; MatchDataLayout layout; TermFieldHandle handle; std::vector > tokens; WS() : layout(), handle(layout.allocTermField(fieldId)), tokens() { MatchData::UP tmp = layout.createMatchData(); ASSERT_TRUE(tmp->resolveTermField(handle)->getFieldId() == fieldId); } WS &add(const std::string &token, uint32_t weight) { tokens.push_back(std::make_pair(token, weight)); return *this; } Node::UP createNode() const { SimpleWeightedSetTerm *node = new SimpleWeightedSetTerm("view", 0, Weight(0)); for (size_t i = 0; i < tokens.size(); ++i) { node->append(Node::UP(new SimpleStringTerm(tokens[i].first, "view", 0, Weight(tokens[i].second)))); } return Node::UP(node); } bool isGenericSearch(Searchable &searchable, const std::string &field, bool strict) const { FakeRequestContext requestContext; MatchData::UP md = layout.createMatchData(); Node::UP node = createNode(); FieldSpecList fields = FieldSpecList().add(FieldSpec(field, fieldId, handle)); queryeval::Blueprint::UP bp = searchable.createBlueprint(requestContext, fields, *node); bp->fetchPostings(strict); SearchIterator::UP sb = bp->createSearch(*md, strict); return (dynamic_cast(sb.get()) != 0); } FakeResult search(Searchable &searchable, const std::string &field, bool strict) const { FakeRequestContext requestContext; MatchData::UP md = layout.createMatchData(); Node::UP node = createNode(); FieldSpecList fields = FieldSpecList().add(FieldSpec(field, fieldId, handle)); queryeval::Blueprint::UP bp = searchable.createBlueprint(requestContext, fields, *node); bp->fetchPostings(strict); SearchIterator::UP sb = bp->createSearch(*md, strict); sb->initFullRange(); FakeResult result; for (uint32_t docId = 1; docId < 10; ++docId) { if (sb->seek(docId)) { sb->unpack(docId); result.doc(docId); TermFieldMatchData &data = *md->resolveTermField(handle); FieldPositionsIterator itr = data.getIterator(); for (; itr.valid(); itr.next()) { result.elem(itr.getElementId()); result.weight(itr.getElementWeight()); result.pos(itr.getPosition()); } } } return result; } }; struct MockSearch : public SearchIterator { int seekCnt; int _initial; MockSearch(uint32_t initial) : SearchIterator(), seekCnt(0), _initial(initial) { } void initRange(uint32_t begin, uint32_t end) override { SearchIterator::initRange(begin, end); setDocId(_initial); } virtual void doSeek(uint32_t) override { ++seekCnt; setAtEnd(); } virtual void doUnpack(uint32_t) override {} }; struct MockFixture { MockSearch *mock; TermFieldMatchData tfmd; std::unique_ptr search; MockFixture(uint32_t initial) : mock(0), tfmd(), search() { std::vector children; std::vector weights; mock = new MockSearch(initial); children.push_back(mock); weights.push_back(1); search.reset(WeightedSetTermSearch::create(children, tfmd, weights)); } }; } // namespace TEST("testSimple") { FakeSearchable index; setupFakeSearchable(index); FakeResult expect = FakeResult() .doc(3).elem(0).weight(30).pos(0) .doc(5).elem(0).weight(50).pos(0) .doc(7).elem(0).weight(70).pos(0); WS ws = WS().add("7", 70).add("5", 50).add("3", 30).add("100", 1000); EXPECT_TRUE(ws.isGenericSearch(index, "field", true)); EXPECT_TRUE(ws.isGenericSearch(index, "field", false)); EXPECT_TRUE(ws.isGenericSearch(index, "multi-field", true)); EXPECT_TRUE(ws.isGenericSearch(index, "multi-field", false)); EXPECT_EQUAL(expect, ws.search(index, "field", true)); EXPECT_EQUAL(expect, ws.search(index, "field", false)); EXPECT_EQUAL(expect, ws.search(index, "multi-field", true)); EXPECT_EQUAL(expect, ws.search(index, "multi-field", false)); } TEST("testMulti") { FakeSearchable index; setupFakeSearchable(index); FakeResult expect = FakeResult() .doc(3).elem(0).weight(230).pos(0).elem(0).weight(130).pos(0).elem(0).weight(30).pos(0) .doc(5).elem(0).weight(150).pos(0).elem(0).weight(50).pos(0) .doc(7).elem(0).weight(70).pos(0); WS ws = WS().add("7", 70).add("5", 50).add("3", 30) .add("15", 150).add("13", 130) .add("23", 230).add("100", 1000); EXPECT_TRUE(ws.isGenericSearch(index, "multi-field", true)); EXPECT_TRUE(ws.isGenericSearch(index, "multi-field", false)); EXPECT_EQUAL(expect, ws.search(index, "multi-field", true)); EXPECT_EQUAL(expect, ws.search(index, "multi-field", false)); } TEST_F("test Eager Empty Child", MockFixture(search::endDocId)) { MockSearch *mock = f1.mock; SearchIterator &search = *f1.search; search.initFullRange(); EXPECT_EQUAL(search.beginId(), search.getDocId()); EXPECT_TRUE(!search.seek(1)); EXPECT_TRUE(search.isAtEnd()); EXPECT_EQUAL(0, mock->seekCnt); } TEST_F("test Eager Matching Child", MockFixture(5)) { MockSearch *mock = f1.mock; SearchIterator &search = *f1.search; search.initFullRange(); EXPECT_EQUAL(search.beginId(), search.getDocId()); EXPECT_TRUE(!search.seek(3)); EXPECT_EQUAL(5u, search.getDocId()); EXPECT_EQUAL(0, mock->seekCnt); EXPECT_TRUE(search.seek(5)); EXPECT_EQUAL(5u, search.getDocId()); EXPECT_EQUAL(0, mock->seekCnt); EXPECT_TRUE(!search.seek(7)); EXPECT_TRUE(search.isAtEnd()); EXPECT_EQUAL(1, mock->seekCnt); } class IteratorChildrenVerifier : public search::test::IteratorChildrenVerifier { private: SearchIterator::UP create(const std::vector &children) const override { return SearchIterator::UP(WeightedSetTermSearch::create(children, _tfmd, _weights)); } }; class WeightIteratorChildrenVerifier : public search::test::DwaIteratorChildrenVerifier { private: SearchIterator::UP create(std::vector && children) const override { return SearchIterator::UP(WeightedSetTermSearch::create(_tfmd, _weights, std::move(children))); } }; TEST("verify search iterator conformance with search iterator children") { IteratorChildrenVerifier verifier; verifier.verify(); } TEST("verify search iterator conformance with document weight iterator children") { WeightIteratorChildrenVerifier verifier; verifier.verify(); } TEST_MAIN() { TEST_RUN_ALL(); }