diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /searchlib/src/tests/queryeval/blueprint |
Publish
Diffstat (limited to 'searchlib/src/tests/queryeval/blueprint')
9 files changed, 2414 insertions, 0 deletions
diff --git a/searchlib/src/tests/queryeval/blueprint/.cvsignore b/searchlib/src/tests/queryeval/blueprint/.cvsignore new file mode 100644 index 00000000000..a8da5289575 --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/.cvsignore @@ -0,0 +1,3 @@ +.depend +Makefile +blueprint_test diff --git a/searchlib/src/tests/queryeval/blueprint/.gitignore b/searchlib/src/tests/queryeval/blueprint/.gitignore new file mode 100644 index 00000000000..da4bf633103 --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/.gitignore @@ -0,0 +1,8 @@ +*_test +.depend +Makefile +lhs.out +rhs.out +searchlib_blueprint_test_app +searchlib_intermediate_blueprints_test_app +searchlib_leaf_blueprints_test_app diff --git a/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt b/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt new file mode 100644 index 00000000000..88ba3deeb29 --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(searchlib_blueprint_test_app + SOURCES + blueprint_test.cpp + DEPENDS + searchlib +) +vespa_add_test(NAME searchlib_blueprint_test_app COMMAND searchlib_blueprint_test_app || diff -u lhs.out rhs.out) +vespa_add_executable(searchlib_leaf_blueprints_test_app + SOURCES + leaf_blueprints_test.cpp + DEPENDS + searchlib +) +vespa_add_test(NAME searchlib_leaf_blueprints_test_app COMMAND searchlib_leaf_blueprints_test_app || diff -u lhs.out rhs.out) +vespa_add_executable(searchlib_intermediate_blueprints_test_app + SOURCES + intermediate_blueprints_test.cpp + DEPENDS + searchlib + searchlib_test +) +vespa_add_test(NAME searchlib_intermediate_blueprints_test_app COMMAND searchlib_intermediate_blueprints_test_app || diff -u lhs.out rhs.out) diff --git a/searchlib/src/tests/queryeval/blueprint/DESC b/searchlib/src/tests/queryeval/blueprint/DESC new file mode 100644 index 00000000000..a2634c017bd --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/DESC @@ -0,0 +1 @@ +blueprint test. Take a look at blueprint_test.cpp for details. diff --git a/searchlib/src/tests/queryeval/blueprint/FILES b/searchlib/src/tests/queryeval/blueprint/FILES new file mode 100644 index 00000000000..89c566c5aea --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/FILES @@ -0,0 +1 @@ +blueprint_test.cpp diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp new file mode 100644 index 00000000000..79fec3770b3 --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp @@ -0,0 +1,766 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("blueprint_test"); +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/searchlib/queryeval/blueprint.h> +#include <vespa/searchlib/queryeval/intermediate_blueprints.h> +#include <vespa/vespalib/objects/objectdumper.h> +#include <vespa/vespalib/objects/visit.h> + +#include "mysearch.h" + +using namespace search::queryeval; +using namespace search::fef; + +namespace { + +//----------------------------------------------------------------------------- + +class MyOr : public IntermediateBlueprint +{ +private: +public: + virtual HitEstimate combine(const std::vector<HitEstimate> &data) const { + return max(data); + } + + virtual FieldSpecBaseList exposeFields() const { + return mixChildrenFields(); + } + + virtual void sort(std::vector<Blueprint*> &children) const { + std::sort(children.begin(), children.end(), GreaterEstimate()); + } + + virtual bool inheritStrict(size_t i) const { + (void)i; + return true; + } + + virtual SearchIterator::UP + createIntermediateSearch(const MultiSearch::Children &subSearches, + bool strict, MatchData &md) const + { + return SearchIterator::UP(new MySearch("or", subSearches, &md, strict)); + } + + static MyOr& create() { return *(new MyOr()); } + MyOr& add(Blueprint *n) { addChild(UP(n)); return *this; } + MyOr& add(Blueprint &n) { addChild(UP(&n)); return *this; } +}; + + +class OtherOr : public OrBlueprint +{ +private: +public: + virtual SearchIterator::UP + createIntermediateSearch(const MultiSearch::Children &subSearches, + bool strict, MatchData &md) const + { + return SearchIterator::UP(new MySearch("or", subSearches, &md, strict)); + } + + static OtherOr& create() { return *(new OtherOr()); } + OtherOr& add(Blueprint *n) { addChild(UP(n)); return *this; } + OtherOr& add(Blueprint &n) { addChild(UP(&n)); return *this; } +}; + +//----------------------------------------------------------------------------- + +class MyAnd : public AndBlueprint +{ +private: +public: + virtual HitEstimate combine(const std::vector<HitEstimate> &data) const { + return min(data); + } + + virtual FieldSpecBaseList exposeFields() const { + return FieldSpecBaseList(); + } + + virtual bool inheritStrict(size_t i) const { + return (i == 0); + } + + virtual SearchIterator::UP + createIntermediateSearch(const MultiSearch::Children &subSearches, + bool strict, MatchData &md) const + { + return SearchIterator::UP(new MySearch("and", subSearches, &md, strict)); + } + + static MyAnd& create() { return *(new MyAnd()); } + MyAnd& add(Blueprint *n) { addChild(UP(n)); return *this; } + MyAnd& add(Blueprint &n) { addChild(UP(&n)); return *this; } +}; + + +class OtherAnd : public AndBlueprint +{ +private: +public: + virtual SearchIterator::UP + createIntermediateSearch(const MultiSearch::Children &subSearches, + bool strict, MatchData &md) const + { + return SearchIterator::UP(new MySearch("and", subSearches, &md, strict)); + } + + static OtherAnd& create() { return *(new OtherAnd()); } + OtherAnd& add(Blueprint *n) { addChild(UP(n)); return *this; } + OtherAnd& add(Blueprint &n) { addChild(UP(&n)); return *this; } +}; + +class OtherAndNot : public AndNotBlueprint +{ +public: + virtual SearchIterator::UP + createIntermediateSearch(const MultiSearch::Children &subSearches, + bool strict, MatchData &md) const + { + return SearchIterator::UP(new MySearch("andnot", subSearches, &md, strict)); + } + + static OtherAndNot& create() { return *(new OtherAndNot()); } + OtherAndNot& add(Blueprint *n) { addChild(UP(n)); return *this; } + OtherAndNot& add(Blueprint &n) { addChild(UP(&n)); return *this; } + +}; + +//----------------------------------------------------------------------------- + +struct MyTerm : SimpleLeafBlueprint { + MyTerm(const FieldSpecBaseList &fields, uint32_t hitEstimate) : SimpleLeafBlueprint(fields) { + setEstimate(HitEstimate(hitEstimate, false)); + } + virtual SearchIterator::UP createLeafSearch(const search::fef::TermFieldMatchDataArray &, bool) const { + return SearchIterator::UP(); + } +}; + +//----------------------------------------------------------------------------- + +} // namespace <unnamed> + +class Test : public vespalib::TestApp +{ +private: + MatchData::UP _md; + + static Blueprint::UP ap(Blueprint *b) { return Blueprint::UP(b); } + static Blueprint::UP ap(Blueprint &b) { return Blueprint::UP(&b); } + + SearchIterator::UP create(const Blueprint &blueprint); + bool check_equal(const SearchIterator &a, const SearchIterator &b); + bool check_equal(const Blueprint &a, const Blueprint &b); + bool check_not_equal(const SearchIterator &a, const SearchIterator &b); + bool check_not_equal(const Blueprint &a, const Blueprint &b); + +public: + Test() + : vespalib::TestApp(), + _md(MatchData::makeTestInstance(0, 100, 10)) + { + } + Blueprint::UP buildBlueprint1(); + Blueprint::UP buildBlueprint2(); + void testBlueprintBuilding(); + void testHitEstimateCalculation(); + void testHitEstimatePropagation(); + void testMatchDataPropagation(); + void testChildSorting(); + void testChildAndNotCollapsing(); + void testChildAndCollapsing(); + void testChildOrCollapsing(); + void testSearchCreation(); + void testBlueprintMakeNew(); + void requireThatAsStringWorks(); + void requireThatVisitMembersWorks(); + void requireThatDocIdLimitInjectionWorks(); + int Main(); +}; + +SearchIterator::UP +Test::create(const Blueprint &blueprint) +{ + const_cast<Blueprint &>(blueprint).fetchPostings(true); + SearchIterator::UP search = blueprint.createSearch(*_md, true); + MySearch::verifyAndInfer(search.get(), *_md); + return search; +} + +bool +Test::check_equal(const SearchIterator &a, const SearchIterator &b) +{ + return EXPECT_EQUAL(a.asString(), b.asString()); +} + +bool +Test::check_equal(const Blueprint &a, const Blueprint &b) +{ + SearchIterator::UP searchA = create(a); + SearchIterator::UP searchB = create(b); + TEST_STATE("check_equal"); + bool ok = check_equal(*searchA, *searchB); + return ok; +} + +bool +Test::check_not_equal(const SearchIterator &a, const SearchIterator &b) +{ + return EXPECT_NOT_EQUAL(a.asString(), b.asString()); +} + +bool +Test::check_not_equal(const Blueprint &a, const Blueprint &b) +{ + SearchIterator::UP searchA = create(a); + SearchIterator::UP searchB = create(b); + TEST_STATE("check_not_equal"); + bool ok = check_not_equal(*searchA, *searchB); + return ok; +} + +Blueprint::UP +Test::buildBlueprint1() +{ + return ap(MyAnd::create() + .add(MyOr::create() + .add(MyLeafSpec(10).addField(1, 11).create()) + .add(MyLeafSpec(20).addField(1, 21).create()) + .add(MyLeafSpec(30).addField(1, 31).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(100).addField(2, 22).create()) + .add(MyLeafSpec(200).addField(2, 42).create()) + ) + ); +} + +Blueprint::UP +Test::buildBlueprint2() +{ + return ap(MyAnd::create() + .add(MyOr::create() + .add(MyLeafSpec(10).addField(1, 11).create()) + .add(MyLeafSpec(20).addField(1, 21).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(100).addField(2, 22).create()) + .add(MyLeafSpec(200).addField(2, 32).create()) + .add(MyLeafSpec(300).addField(2, 42).create()) + ) + ); +} + +void +Test::testBlueprintBuilding() +{ + Blueprint::UP root1 = buildBlueprint1(); + Blueprint::UP root2 = buildBlueprint2(); + SearchIterator::UP search1 = create(*root1); + SearchIterator::UP search2 = create(*root2); + // fprintf(stderr, "%s\n", search1->asString().c_str()); + // fprintf(stderr, "%s\n", search2->asString().c_str()); +} + +void +Test::testHitEstimateCalculation() +{ + { + Blueprint::UP leaf = ap(MyLeafSpec(37).create()); + EXPECT_EQUAL(37u, leaf->getState().estimate().estHits); + EXPECT_EQUAL(0u, leaf->getState().numFields()); + } + { + Blueprint::UP a1 = ap(MyAnd::create() + .add(MyLeafSpec(7).addField(1, 11).create()) + .add(MyLeafSpec(4).addField(1, 21).create()) + .add(MyLeafSpec(6).addField(1, 31).create())); + EXPECT_EQUAL(4u, a1->getState().estimate().estHits); + } + { + Blueprint::UP a2 = ap(MyAnd::create() + .add(MyLeafSpec(4).addField(1, 1).create()) + .add(MyLeafSpec(7).addField(2, 2).create()) + .add(MyLeafSpec(6).addField(3, 3).create())); + EXPECT_EQUAL(4u, a2->getState().estimate().estHits); + } + { + Blueprint::UP o1 = ap(MyOr::create() + .add(MyLeafSpec(7).addField(1, 11).create()) + .add(MyLeafSpec(4).addField(1, 21).create()) + .add(MyLeafSpec(6).addField(1, 31).create())); + EXPECT_EQUAL(7u, o1->getState().estimate().estHits); + } + { + Blueprint::UP o2 = ap(MyOr::create() + .add(MyLeafSpec(4).addField(1, 1).create()) + .add(MyLeafSpec(7).addField(2, 2).create()) + .add(MyLeafSpec(6).addField(3, 3).create())); + EXPECT_EQUAL(7u, o2->getState().estimate().estHits); + } + { + Blueprint::UP a = ap(MyAnd::create() + .add(MyLeafSpec(0).create()) + .add(MyLeafSpec(0, true).create())); + EXPECT_EQUAL(0u, a->getState().estimate().estHits); + EXPECT_EQUAL(true, a->getState().estimate().empty); + } + { + Blueprint::UP o = ap(MyOr::create() + .add(MyLeafSpec(0).create()) + .add(MyLeafSpec(0, true).create())); + EXPECT_EQUAL(0u, o->getState().estimate().estHits); + EXPECT_EQUAL(false, o->getState().estimate().empty); + } + { + Blueprint::UP tree1 = buildBlueprint1(); + EXPECT_EQUAL(30u, tree1->getState().estimate().estHits); + + Blueprint::UP tree2 = buildBlueprint2(); + EXPECT_EQUAL(20u, tree2->getState().estimate().estHits); + } +} + +void +Test::testHitEstimatePropagation() +{ + MyLeaf *leaf1 = new MyLeaf(FieldSpecBaseList()); + leaf1->estimate(10); + + MyLeaf *leaf2 = new MyLeaf(FieldSpecBaseList()); + leaf2->estimate(20); + + MyLeaf *leaf3 = new MyLeaf(FieldSpecBaseList()); + leaf3->estimate(30); + + MyOr *parent = new MyOr(); + MyOr *grandparent = new MyOr(); + + Blueprint::UP root(grandparent); + + parent->addChild(ap(leaf1)); + parent->addChild(ap(leaf3)); + grandparent->addChild(ap(leaf2)); + grandparent->addChild(ap(parent)); + EXPECT_EQUAL(30u, root->getState().estimate().estHits); + + // edit + leaf3->estimate(50); + EXPECT_EQUAL(50u, root->getState().estimate().estHits); + + // remove + ASSERT_TRUE(parent->childCnt() == 2); + Blueprint::UP tmp = parent->removeChild(1); + ASSERT_TRUE(tmp.get() == leaf3); + EXPECT_EQUAL(1u, parent->childCnt()); + EXPECT_EQUAL(20u, root->getState().estimate().estHits); + + // add + leaf3->estimate(25); + EXPECT_EQUAL(20u, root->getState().estimate().estHits); + parent->addChild(std::move(tmp)); + EXPECT_TRUE(tmp.get() == 0); + EXPECT_EQUAL(25u, root->getState().estimate().estHits); +} + +void +Test::testMatchDataPropagation() +{ + { + Blueprint::UP leaf = ap(MyLeafSpec(0, true).create()); + EXPECT_EQUAL(0u, leaf->getState().numFields()); + } + { + Blueprint::UP leaf = ap(MyLeafSpec(42) + .addField(1, 41) + .addField(2, 72).create()); + EXPECT_EQUAL(42u, leaf->getState().estimate().estHits); + ASSERT_TRUE(leaf->getState().numFields() == 2); + EXPECT_EQUAL(1u, leaf->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, leaf->getState().field(1).getFieldId()); + EXPECT_EQUAL(41u, leaf->getState().field(0).getHandle()); + EXPECT_EQUAL(72u, leaf->getState().field(1).getHandle()); + } + { + Blueprint::UP a = ap(MyAnd::create() + .add(MyLeafSpec(7).addField(1, 11).create()) + .add(MyLeafSpec(4).addField(1, 21).create()) + .add(MyLeafSpec(6).addField(1, 31).create())); + EXPECT_EQUAL(0u, a->getState().numFields()); + } + { + MyOr &o = MyOr::create() + .add(MyLeafSpec(1).addField(1, 1).create()) + .add(MyLeafSpec(2).addField(2, 2).create()); + + Blueprint::UP a = ap(o); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(2u, a->getState().estimate().estHits); + + o.add(MyLeafSpec(5).addField(2, 2).create()); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(5u, a->getState().estimate().estHits); + + o.add(MyLeafSpec(5).addField(2, 32).create()); + EXPECT_EQUAL(0u, a->getState().numFields()); + o.removeChild(3); + EXPECT_EQUAL(2u, a->getState().numFields()); + o.add(MyLeafSpec(0, true).create()); + EXPECT_EQUAL(0u, a->getState().numFields()); + } +} + +void +Test::testChildAndNotCollapsing() +{ + Blueprint::UP unsorted = ap(OtherAndNot::create() + .add(OtherAndNot::create() + .add(OtherAndNot::create() + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + ) + .add(OtherAnd::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + ) + ) + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + ); + + Blueprint::UP sorted = ap(OtherAndNot::create() + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + .add(OtherAnd::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + ) + ); + TEST_DO(check_not_equal(*sorted, *unsorted)); + unsorted = Blueprint::optimize(std::move(unsorted)); + TEST_DO(check_equal(*sorted, *unsorted)); +} + +void +Test::testChildAndCollapsing() +{ + Blueprint::UP unsorted = ap(OtherAnd::create() + .add(OtherAnd::create() + .add(OtherAnd::create() + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + ) + .add(OtherAnd::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + ) + ) + .add(OtherAnd::create() + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + ) + ); + + Blueprint::UP sorted = ap(OtherAnd::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + ); + + TEST_DO(check_not_equal(*sorted, *unsorted)); + unsorted = Blueprint::optimize(std::move(unsorted)); + TEST_DO(check_equal(*sorted, *unsorted)); +} + +void +Test::testChildOrCollapsing() +{ + Blueprint::UP unsorted = ap(OtherOr::create() + .add(OtherOr::create() + .add(OtherOr::create() + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + ) + .add(OtherOr::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + ) + ) + .add(OtherOr::create() + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + ) + ); + + Blueprint::UP sorted = ap(OtherOr::create() + .add(MyLeafSpec(300).addField(1, 31).create()) + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(1).addField(2, 42).create()) + ); + TEST_DO(check_not_equal(*sorted, *unsorted)); + unsorted = Blueprint::optimize(std::move(unsorted)); + TEST_DO(check_equal(*sorted, *unsorted)); +} + +void +Test::testChildSorting() +{ + Blueprint::UP unsorted = ap(MyAnd::create() + .add(MyOr::create() + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + .add(MyLeafSpec(300).addField(1, 31).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(1).addField(2, 42).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(3).addField(2, 62).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + ) + ); + + Blueprint::UP sorted = ap(MyAnd::create() + .add(MyOr::create() + .add(MyLeafSpec(3).addField(2, 62).create()) + .add(MyLeafSpec(2).addField(2, 52).create()) + .add(MyLeafSpec(1).addField(2, 42).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(30).addField(3, 73).create()) + .add(MyLeafSpec(20).addField(3, 83).create()) + .add(MyLeafSpec(10).addField(3, 93).create()) + ) + .add(MyOr::create() + .add(MyLeafSpec(300).addField(1, 31).create()) + .add(MyLeafSpec(200).addField(1, 11).create()) + .add(MyLeafSpec(100).addField(1, 21).create()) + ) + ); + + TEST_DO(check_not_equal(*sorted, *unsorted)); + unsorted = Blueprint::optimize(std::move(unsorted)); + TEST_DO(check_equal(*sorted, *unsorted)); +} + + +void +Test::testSearchCreation() +{ + { + Blueprint::UP l = ap(MyLeafSpec(3) + .addField(1, 1) + .addField(2, 2) + .addField(3, 3).create()); + SearchIterator::UP leafsearch = create(*l); + + MySearch *lw = new MySearch("leaf", true, true); + lw->addHandle(1).addHandle(2).addHandle(3); + SearchIterator::UP wantleaf(lw); + + TEST_DO(check_equal(*wantleaf, *leafsearch)); + } + { + Blueprint::UP a = ap(MyAnd::create() + .add(MyLeafSpec(1).addField(1, 1).create()) + .add(MyLeafSpec(2).addField(2, 2).create())); + SearchIterator::UP andsearch = create(*a); + + MySearch *l1 = new MySearch("leaf", true, true); + MySearch *l2 = new MySearch("leaf", true, false); + l1->addHandle(1); + l2->addHandle(2); + MySearch *aw = new MySearch("and", false, true); + aw->add(l1); + aw->add(l2); + SearchIterator::UP wanted(aw); + TEST_DO(check_equal(*wanted, *andsearch)); + } + { + Blueprint::UP o = ap(MyOr::create() + .add(MyLeafSpec(1).addField(1, 11).create()) + .add(MyLeafSpec(2).addField(2, 22).create())); + SearchIterator::UP orsearch = create(*o); + + MySearch *l1 = new MySearch("leaf", true, true); + MySearch *l2 = new MySearch("leaf", true, true); + l1->addHandle(11); + l2->addHandle(22); + MySearch *ow = new MySearch("or", false, true); + ow->add(l1); + ow->add(l2); + SearchIterator::UP wanted(ow); + TEST_DO(check_equal(*wanted, *orsearch)); + } +} + +template<typename T> +Blueprint::UP makeNew(T *orig) +{ + return Blueprint::UP(new T(*orig)); +} + +void +Test::testBlueprintMakeNew() +{ + Blueprint::UP orig = ap(MyOr::create() + .add(MyLeafSpec(1).addField(1, 11).create()) + .add(MyLeafSpec(2).addField(2, 22).create())); + orig->setSourceId(42); + MyOr *myOr = dynamic_cast<MyOr*>(orig.get()); + ASSERT_TRUE(myOr != 0); + Blueprint::UP copy1 = makeNew(myOr); + Blueprint::UP copy2 = makeNew(myOr); + TEST_DO(check_equal(*copy1, *copy2)); + TEST_DO(check_not_equal(*orig, *copy1)); + TEST_DO(check_not_equal(*orig, *copy2)); + EXPECT_TRUE(dynamic_cast<MyOr*>(copy1.get()) != 0); + EXPECT_TRUE(dynamic_cast<MyOr*>(copy2.get()) != 0); + EXPECT_EQUAL(42u, orig->getSourceId()); + EXPECT_EQUAL(42u, copy1->getSourceId()); + EXPECT_EQUAL(2u, orig->getState().numFields()); + EXPECT_EQUAL(0u, copy1->getState().numFields()); +} + +vespalib::string +getExpectedBlueprint() +{ + return "(anonymous namespace)::MyOr {\n" + " isTermLike: true\n" + " fields: FieldList {\n" + " [0]: Field {\n" + " fieldId: 5\n" + " handle: 7\n" + " isFilter: false\n" + " }\n" + " }\n" + " estimate: HitEstimate {\n" + " empty: false\n" + " estHits: 9\n" + " tree_size: 2\n" + " allow_termwise_eval: 0\n" + " }\n" + " sourceId: 4294967295\n" + " docid_limit: 0\n" + " children: std::vector {\n" + " [0]: (anonymous namespace)::MyTerm {\n" + " isTermLike: true\n" + " fields: FieldList {\n" + " [0]: Field {\n" + " fieldId: 5\n" + " handle: 7\n" + " isFilter: false\n" + " }\n" + " }\n" + " estimate: HitEstimate {\n" + " empty: false\n" + " estHits: 9\n" + " tree_size: 1\n" + " allow_termwise_eval: 1\n" + " }\n" + " sourceId: 4294967295\n" + " docid_limit: 0\n" + " }\n" + " }\n" + "}\n"; +} + +struct BlueprintFixture +{ + MyOr _blueprint; + BlueprintFixture() : _blueprint() { + _blueprint.add(new MyTerm(FieldSpecBaseList().add(FieldSpecBase(5, 7)), 9)); + } +}; + +void +Test::requireThatAsStringWorks() +{ + BlueprintFixture f; + EXPECT_EQUAL(getExpectedBlueprint(), f._blueprint.asString()); +} + +void +Test::requireThatVisitMembersWorks() +{ + BlueprintFixture f; + vespalib::ObjectDumper dumper; + visit(dumper, "", &f._blueprint); + EXPECT_EQUAL(getExpectedBlueprint(), dumper.toString()); +} + +void +Test::requireThatDocIdLimitInjectionWorks() +{ + BlueprintFixture f; + ASSERT_GREATER(f._blueprint.childCnt(), 0u); + const MyTerm &term = dynamic_cast<MyTerm&>(f._blueprint.getChild(0)); + EXPECT_EQUAL(0u, term.get_docid_limit()); + f._blueprint.setDocIdLimit(1000); + EXPECT_EQUAL(1000u, term.get_docid_limit()); +} + +int +Test::Main() +{ + TEST_DEBUG("lhs.out", "rhs.out"); + TEST_INIT("blueprint_test"); + testBlueprintBuilding(); + testHitEstimateCalculation(); + testHitEstimatePropagation(); + testMatchDataPropagation(); + testChildSorting(); + testChildAndNotCollapsing(); + testChildAndCollapsing(); + testChildOrCollapsing(); + testSearchCreation(); + testBlueprintMakeNew(); + requireThatAsStringWorks(); + requireThatVisitMembersWorks(); + requireThatDocIdLimitInjectionWorks(); + TEST_DONE(); +} + +TEST_APPHOOK(Test); diff --git a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp new file mode 100644 index 00000000000..161537104e0 --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp @@ -0,0 +1,1332 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("blueprint_test"); + +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/searchlib/queryeval/blueprint.h> +#include <vespa/searchlib/queryeval/intermediate_blueprints.h> +#include <vespa/searchlib/queryeval/leaf_blueprints.h> +#include <vespa/searchlib/queryeval/equiv_blueprint.h> +#include <vespa/searchlib/queryeval/searchable.h> + +#include "mysearch.h" + +#include <vespa/searchlib/queryeval/multisearch.h> +#include <vespa/searchlib/queryeval/andnotsearch.h> +#include <vespa/searchlib/queryeval/andsearch.h> +#include <vespa/searchlib/queryeval/orsearch.h> +#include <vespa/searchlib/queryeval/nearsearch.h> +#include <vespa/searchlib/queryeval/ranksearch.h> +#include <vespa/searchlib/queryeval/wand/weak_and_search.h> +#include <vespa/searchlib/queryeval/fake_requestcontext.h> +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/searchlib/test/diskindex/testdiskindex.h> +#include <vespa/searchlib/query/tree/simplequery.h> +#include <vespa/searchlib/common/bitvectoriterator.h> +#include <vespa/searchlib/diskindex/zcpostingiterators.h> + +#include <algorithm> + +using namespace search::queryeval; +using namespace search::fef; +using namespace search::query; + +struct WeightOrder { + bool operator()(const wand::Term &t1, const wand::Term &t2) const { + return (t1.weight < t2.weight); + } +}; + +Blueprint::UP ap(Blueprint *b) { return Blueprint::UP(b); } +Blueprint::UP ap(Blueprint &b) { return Blueprint::UP(&b); } + +TEST("test AndNot Blueprint") { + AndNotBlueprint b; + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + } + { + AndNotBlueprint a; + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(10).create()); + Blueprint::UP c2 = ap(MyLeafSpec(20).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c1.get(), children[0]); + EXPECT_EQUAL(c3.get(), children[1]); + EXPECT_EQUAL(c4.get(), children[2]); + EXPECT_EQUAL(c2.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(false, b.inheritStrict(1)); + EXPECT_EQUAL(false, b.inheritStrict(2)); + EXPECT_EQUAL(false, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test And Blueprint") { + AndBlueprint b; + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(5u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + } + { + AndBlueprint a; + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(20).create()); + Blueprint::UP c2 = ap(MyLeafSpec(40).create()); + Blueprint::UP c3 = ap(MyLeafSpec(10).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c3.get(), children[0]); + EXPECT_EQUAL(c1.get(), children[1]); + EXPECT_EQUAL(c4.get(), children[2]); + EXPECT_EQUAL(c2.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(false, b.inheritStrict(1)); + EXPECT_EQUAL(false, b.inheritStrict(2)); + EXPECT_EQUAL(false, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test Or Blueprint") { + OrBlueprint b; + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + } + { + OrBlueprint &o = *(new OrBlueprint()); + o.addChild(ap(MyLeafSpec(1).addField(1, 1).create())); + o.addChild(ap(MyLeafSpec(2).addField(2, 2).create())); + + Blueprint::UP a(&o); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(2u, a->getState().estimate().estHits); + + o.addChild(ap(MyLeafSpec(5).addField(2, 2).create())); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(5u, a->getState().estimate().estHits); + + o.addChild(ap(MyLeafSpec(5).addField(2, 3).create())); + EXPECT_EQUAL(0u, a->getState().numFields()); + o.removeChild(3); + EXPECT_EQUAL(2u, a->getState().numFields()); + o.addChild(ap(MyLeafSpec(0, true).create())); + EXPECT_EQUAL(0u, a->getState().numFields()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(10).create()); + Blueprint::UP c2 = ap(MyLeafSpec(20).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c3.get(), children[0]); + EXPECT_EQUAL(c4.get(), children[1]); + EXPECT_EQUAL(c2.get(), children[2]); + EXPECT_EQUAL(c1.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(true, b.inheritStrict(1)); + EXPECT_EQUAL(true, b.inheritStrict(2)); + EXPECT_EQUAL(true, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test Near Blueprint") { + NearBlueprint b(7); + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(5u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + } + { + NearBlueprint a(7); + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(40).create()); + Blueprint::UP c2 = ap(MyLeafSpec(10).create()); + Blueprint::UP c3 = ap(MyLeafSpec(30).create()); + Blueprint::UP c4 = ap(MyLeafSpec(20).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c2.get(), children[0]); + EXPECT_EQUAL(c4.get(), children[1]); + EXPECT_EQUAL(c3.get(), children[2]); + EXPECT_EQUAL(c1.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(false, b.inheritStrict(1)); + EXPECT_EQUAL(false, b.inheritStrict(2)); + EXPECT_EQUAL(false, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test ONear Blueprint") { + ONearBlueprint b(8); + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(5u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + } + { + ONearBlueprint a(8); + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(20).create()); + Blueprint::UP c2 = ap(MyLeafSpec(10).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c1.get(), children[0]); + EXPECT_EQUAL(c2.get(), children[1]); + EXPECT_EQUAL(c3.get(), children[2]); + EXPECT_EQUAL(c4.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(false, b.inheritStrict(1)); + EXPECT_EQUAL(false, b.inheritStrict(2)); + EXPECT_EQUAL(false, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test Rank Blueprint") { + RankBlueprint b; + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + } + { + RankBlueprint a; + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(20).create()); + Blueprint::UP c2 = ap(MyLeafSpec(10).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c1.get(), children[0]); + EXPECT_EQUAL(c2.get(), children[1]); + EXPECT_EQUAL(c3.get(), children[2]); + EXPECT_EQUAL(c4.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(false, b.inheritStrict(1)); + EXPECT_EQUAL(false, b.inheritStrict(2)); + EXPECT_EQUAL(false, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test SourceBlender Blueprint") { + ISourceSelector *selector = 0; // not needed here + SourceBlenderBlueprint b(*selector); + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + } + { + SourceBlenderBlueprint &o = *(new SourceBlenderBlueprint(*selector)); + o.addChild(ap(MyLeafSpec(1).addField(1, 1).create())); + o.addChild(ap(MyLeafSpec(2).addField(2, 2).create())); + + Blueprint::UP a(&o); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(2u, a->getState().estimate().estHits); + + o.addChild(ap(MyLeafSpec(5).addField(2, 2).create())); + ASSERT_TRUE(a->getState().numFields() == 2); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(5u, a->getState().estimate().estHits); + + o.addChild(ap(MyLeafSpec(5).addField(2, 3).create())); + EXPECT_EQUAL(0u, a->getState().numFields()); + o.removeChild(3); + EXPECT_EQUAL(2u, a->getState().numFields()); + o.addChild(ap(MyLeafSpec(0, true).create())); + EXPECT_EQUAL(0u, a->getState().numFields()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(20).create()); + Blueprint::UP c2 = ap(MyLeafSpec(10).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c1.get(), children[0]); + EXPECT_EQUAL(c2.get(), children[1]); + EXPECT_EQUAL(c3.get(), children[2]); + EXPECT_EQUAL(c4.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(true, b.inheritStrict(1)); + EXPECT_EQUAL(true, b.inheritStrict(2)); + EXPECT_EQUAL(true, b.inheritStrict(-1)); + } + // createSearch tested by iterator unit test +} + +TEST("test SourceBlender below AND optimization") { + ISourceSelector *selector_1 = 0; // the one + ISourceSelector *selector_2 = reinterpret_cast<ISourceSelector*>(100); // not the one + //------------------------------------------------------------------------- + AndBlueprint *top = new AndBlueprint(); + Blueprint::UP top_bp(top); + top->addChild(ap(MyLeafSpec(2).create())); + top->addChild(ap(MyLeafSpec(1).create())); + top->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + AndBlueprint *expect = new AndBlueprint(); + Blueprint::UP expect_bp(expect); + expect->addChild(ap(MyLeafSpec(1).create())); + expect->addChild(ap(MyLeafSpec(2).create())); + expect->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + expect->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender(new SourceBlenderBlueprint(*selector_1)); + { + AndBlueprint *sub_and = new AndBlueprint(); + sub_and->setSourceId(3); + sub_and->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + sub_and->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + blender->addChild(ap(sub_and)); + } + { + AndBlueprint *sub_and = new AndBlueprint(); + sub_and->setSourceId(2); + sub_and->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + blender->addChild(ap(sub_and)); + } + { + AndBlueprint *sub_and = new AndBlueprint(); + sub_and->setSourceId(1); + sub_and->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + blender->addChild(ap(sub_and)); + } + expect->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_bp->asString(), top_bp->asString()); + top_bp = Blueprint::optimize(std::move(top_bp)); + EXPECT_EQUAL(expect_bp->asString(), top_bp->asString()); + expect_bp = Blueprint::optimize(std::move(expect_bp)); + EXPECT_EQUAL(expect_bp->asString(), top_bp->asString()); +} + +TEST("test SourceBlender below OR optimization") { + ISourceSelector *selector_1 = 0; // the one + ISourceSelector *selector_2 = reinterpret_cast<ISourceSelector*>(100); // not the one + //------------------------------------------------------------------------- + OrBlueprint *top = new OrBlueprint(); + Blueprint::UP top_up(top); + top->addChild(ap(MyLeafSpec(2).create())); + top->addChild(ap(MyLeafSpec(1).create())); + top->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + OrBlueprint *expect = new OrBlueprint(); + Blueprint::UP expect_up(expect); + { + SourceBlenderBlueprint *blender(new SourceBlenderBlueprint(*selector_1)); + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(3); + sub_and->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + sub_and->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(2); + sub_and->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(1); + sub_and->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(sub_and)); + } + expect->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + expect->addChild(ap(blender)); + } + expect->addChild(ap(MyLeafSpec(3).create())); + expect->addChild(ap(MyLeafSpec(2).create())); + expect->addChild(ap(MyLeafSpec(1).create())); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test SourceBlender below AND_NOT optimization") { + ISourceSelector *selector_1 = 0; // the one + ISourceSelector *selector_2 = reinterpret_cast<ISourceSelector*>(100); // not the one + //------------------------------------------------------------------------- + AndNotBlueprint *top = new AndNotBlueprint(); + Blueprint::UP top_up(top); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(42).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + top->addChild(ap(MyLeafSpec(2).create())); + top->addChild(ap(MyLeafSpec(1).create())); + top->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + AndNotBlueprint *expect = new AndNotBlueprint(); + Blueprint::UP expect_up(expect); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(42).create()->setSourceId(1))); + expect->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender(new SourceBlenderBlueprint(*selector_1)); + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(3); + sub_and->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + sub_and->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(2); + sub_and->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(1); + sub_and->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(sub_and)); + } + expect->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + expect->addChild(ap(blender)); + } + expect->addChild(ap(MyLeafSpec(3).create())); + expect->addChild(ap(MyLeafSpec(2).create())); + expect->addChild(ap(MyLeafSpec(1).create())); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test SourceBlender below RANK optimization") { + ISourceSelector *selector_1 = 0; // the one + ISourceSelector *selector_2 = reinterpret_cast<ISourceSelector*>(100); // not the one + //------------------------------------------------------------------------- + RankBlueprint *top = new RankBlueprint(); + Blueprint::UP top_up(top); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(42).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + top->addChild(ap(MyLeafSpec(2).create())); + top->addChild(ap(MyLeafSpec(1).create())); + top->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + top->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + blender->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + top->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + RankBlueprint *expect = new RankBlueprint(); + Blueprint::UP expect_up(expect); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_1); + blender->addChild(ap(MyLeafSpec(42).create()->setSourceId(1))); + expect->addChild(ap(blender)); + } + expect->addChild(ap(MyLeafSpec(2).create())); + expect->addChild(ap(MyLeafSpec(1).create())); + expect->addChild(ap(MyLeafSpec(3).create())); + { + SourceBlenderBlueprint *blender = new SourceBlenderBlueprint(*selector_2); + blender->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + expect->addChild(ap(blender)); + } + { + SourceBlenderBlueprint *blender(new SourceBlenderBlueprint(*selector_1)); + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(3); + sub_and->addChild(ap(MyLeafSpec(300).create()->setSourceId(3))); + sub_and->addChild(ap(MyLeafSpec(30).create()->setSourceId(3))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(2); + sub_and->addChild(ap(MyLeafSpec(2000).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(200).create()->setSourceId(2))); + sub_and->addChild(ap(MyLeafSpec(20).create()->setSourceId(2))); + blender->addChild(ap(sub_and)); + } + { + OrBlueprint *sub_and = new OrBlueprint(); + sub_and->setSourceId(1); + sub_and->addChild(ap(MyLeafSpec(1000).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(100).create()->setSourceId(1))); + sub_and->addChild(ap(MyLeafSpec(10).create()->setSourceId(1))); + blender->addChild(ap(sub_and)); + } + expect->addChild(ap(blender)); + } + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test empty root node optimization and safeness") { + //------------------------------------------------------------------------- + // tests leaf node elimination + Blueprint::UP top1_up(ap(MyLeafSpec(0, true).create())); + //------------------------------------------------------------------------- + // tests intermediate node elimination + Blueprint::UP top2_up(ap((new AndBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(20).create())))); + //------------------------------------------------------------------------- + // tests safety of empty AND_NOT child removal + Blueprint::UP top3_up(ap((new AndNotBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(20).create())))); + //------------------------------------------------------------------------- + // tests safety of empty RANK child removal + Blueprint::UP top4_up(ap((new RankBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(20).create())))); + //------------------------------------------------------------------------- + // tests safety of empty OR child removal + Blueprint::UP top5_up(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(0, true).create())))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up(new EmptyBlueprint()); + //------------------------------------------------------------------------- + top1_up = Blueprint::optimize(std::move(top1_up)); + top2_up = Blueprint::optimize(std::move(top2_up)); + top3_up = Blueprint::optimize(std::move(top3_up)); + top4_up = Blueprint::optimize(std::move(top4_up)); + top5_up = Blueprint::optimize(std::move(top5_up)); + EXPECT_EQUAL(expect_up->asString(), top1_up->asString()); + EXPECT_EQUAL(expect_up->asString(), top2_up->asString()); + EXPECT_EQUAL(expect_up->asString(), top3_up->asString()); + EXPECT_EQUAL(expect_up->asString(), top4_up->asString()); + EXPECT_EQUAL(expect_up->asString(), top5_up->asString()); +} + +TEST("and with one empty child is optimized away") { + ISourceSelector *selector = 0; + Blueprint::UP top(ap((new SourceBlenderBlueprint(*selector))-> + addChild(ap(MyLeafSpec(10).create())). + addChild(ap((new AndBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(20).create())))))); + top = Blueprint::optimize(std::move(top)); + Blueprint::UP expect_up(ap((new SourceBlenderBlueprint(*selector))-> + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(new EmptyBlueprint())))); + EXPECT_EQUAL(expect_up->asString(), top->asString()); +} + +TEST("test single child optimization") { + ISourceSelector *selector = 0; + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new AndNotBlueprint())-> + addChild(ap((new AndBlueprint())-> + addChild(ap((new OrBlueprint())-> + addChild(ap((new SourceBlenderBlueprint(*selector))-> + addChild(ap((new RankBlueprint())-> + addChild(ap(MyLeafSpec(42).create())))))))))))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new SourceBlenderBlueprint(*selector))-> + addChild(ap(MyLeafSpec(42).create())))); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test empty OR child optimization") { + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(0).create())). + addChild(ap(MyLeafSpec(30).create())). + addChild(ap(MyLeafSpec(0, true).create())))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(30).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0).create())))); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test empty AND_NOT child optimization") { + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new AndNotBlueprint())-> + addChild(ap(MyLeafSpec(42).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(0).create())). + addChild(ap(MyLeafSpec(30).create())). + addChild(ap(MyLeafSpec(0, true).create())))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new AndNotBlueprint())-> + addChild(ap(MyLeafSpec(42).create())). + addChild(ap(MyLeafSpec(30).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0).create())))); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("test empty RANK child optimization") { + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new RankBlueprint())-> + addChild(ap(MyLeafSpec(42).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(0).create())). + addChild(ap(MyLeafSpec(30).create())). + addChild(ap(MyLeafSpec(0, true).create())))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new RankBlueprint())-> + addChild(ap(MyLeafSpec(42).create())). + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(10).create())). + addChild(ap(MyLeafSpec(0).create())). + addChild(ap(MyLeafSpec(30).create())))); + //------------------------------------------------------------------------- + EXPECT_NOT_EQUAL(expect_up->asString(), top_up->asString()); + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); + expect_up = Blueprint::optimize(std::move(expect_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("require that replaced blueprints retain source id") { + //------------------------------------------------------------------------- + // replace empty root with empty search + Blueprint::UP top1_up(ap(MyLeafSpec(0, true).create()->setSourceId(13))); + Blueprint::UP expect1_up(new EmptyBlueprint()); + expect1_up->setSourceId(13); + //------------------------------------------------------------------------- + // replace self with single child + Blueprint::UP top2_up(ap(static_cast<AndBlueprint&>((new AndBlueprint())->setSourceId(42)). + addChild(ap(MyLeafSpec(30).create()->setSourceId(55))))); + Blueprint::UP expect2_up(ap(MyLeafSpec(30).create()->setSourceId(42))); + //------------------------------------------------------------------------- + top1_up = Blueprint::optimize(std::move(top1_up)); + top2_up = Blueprint::optimize(std::move(top2_up)); + EXPECT_EQUAL(expect1_up->asString(), top1_up->asString()); + EXPECT_EQUAL(expect2_up->asString(), top2_up->asString()); + EXPECT_EQUAL(13u, top1_up->getSourceId()); + EXPECT_EQUAL(42u, top2_up->getSourceId()); +} + +TEST("test Equiv Blueprint") { + FieldSpecBaseList fields; + search::fef::MatchDataLayout subLayout; + fields.add(FieldSpecBase(1, 1)); + fields.add(FieldSpecBase(2, 2)); + fields.add(FieldSpecBase(3, 3)); + EquivBlueprint b(fields, subLayout); + { + EquivBlueprint &o = *(new EquivBlueprint(fields, subLayout)); + o.addTerm(ap(MyLeafSpec(5).addField(1, 4).create()), 1.0); + o.addTerm(ap(MyLeafSpec(10).addField(1, 5).create()), 1.0); + o.addTerm(ap(MyLeafSpec(20).addField(1, 6).create()), 1.0); + o.addTerm(ap(MyLeafSpec(50).addField(2, 7).create()), 1.0); + + Blueprint::UP a(&o); + ASSERT_TRUE(a->getState().numFields() == 3); + EXPECT_EQUAL(1u, a->getState().field(0).getFieldId()); + EXPECT_EQUAL(2u, a->getState().field(1).getFieldId()); + EXPECT_EQUAL(3u, a->getState().field(2).getFieldId()); + + EXPECT_EQUAL(1u, a->getState().field(0).getHandle()); + EXPECT_EQUAL(2u, a->getState().field(1).getHandle()); + EXPECT_EQUAL(3u, a->getState().field(2).getHandle()); + + EXPECT_EQUAL(50u, a->getState().estimate().estHits); + EXPECT_EQUAL(false, a->getState().estimate().empty); + } + // createSearch tested by iterator unit test +} + + +TEST("test WeakAnd Blueprint") { + WeakAndBlueprint b(1000); + { // combine + std::vector<Blueprint::HitEstimate> est; + EXPECT_EQUAL(true, b.combine(est).empty); + EXPECT_EQUAL(0u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(10, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(10u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(20, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(5, false)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + est.push_back(Blueprint::HitEstimate(0, true)); + EXPECT_EQUAL(false, b.combine(est).empty); + EXPECT_EQUAL(20u, b.combine(est).estHits); + } + { + WeakAndBlueprint a(1000); + a.addChild(ap(MyLeafSpec(10).addField(1, 1).create())); + EXPECT_EQUAL(0u, a.exposeFields().size()); + } + { + std::vector<Blueprint *> children; + Blueprint::UP c1 = ap(MyLeafSpec(10).create()); + Blueprint::UP c2 = ap(MyLeafSpec(20).create()); + Blueprint::UP c3 = ap(MyLeafSpec(40).create()); + Blueprint::UP c4 = ap(MyLeafSpec(30).create()); + children.push_back(c1.get()); + children.push_back(c2.get()); + children.push_back(c3.get()); + children.push_back(c4.get()); + b.sort(children); + EXPECT_EQUAL(c1.get(), children[0]); + EXPECT_EQUAL(c2.get(), children[1]); + EXPECT_EQUAL(c3.get(), children[2]); + EXPECT_EQUAL(c4.get(), children[3]); + } + { + EXPECT_EQUAL(true, b.inheritStrict(0)); + EXPECT_EQUAL(true, b.inheritStrict(1)); + EXPECT_EQUAL(true, b.inheritStrict(2)); + EXPECT_EQUAL(true, b.inheritStrict(-1)); + } + { + FieldSpec field("foo", 1, 1); + FakeResult x = FakeResult().doc(1).doc(2).doc(5); + FakeResult y = FakeResult().doc(2); + FakeResult z = FakeResult().doc(1).doc(4); + { + WeakAndBlueprint wa(456); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + wa.addTerm(Blueprint::UP(new FakeBlueprint(field, x)), 120); + wa.addTerm(Blueprint::UP(new FakeBlueprint(field, z)), 140); + wa.addTerm(Blueprint::UP(new FakeBlueprint(field, y)), 130); + { + wa.fetchPostings(true); + SearchIterator::UP search = wa.createSearch(*md, true); + EXPECT_TRUE(dynamic_cast<WeakAndSearch*>(search.get()) != 0); + WeakAndSearch &s = dynamic_cast<WeakAndSearch&>(*search); + EXPECT_EQUAL(456u, s.getN()); + ASSERT_EQUAL(3u, s.getTerms().size()); + EXPECT_GREATER(s.get_max_score(0), 0.0); + EXPECT_GREATER(s.get_max_score(1), 0.0); + EXPECT_GREATER(s.get_max_score(2), 0.0); + wand::Terms terms = s.getTerms(); + std::sort(terms.begin(), terms.end(), WeightOrder()); + EXPECT_EQUAL(120, terms[0].weight); + EXPECT_EQUAL(3u, terms[0].estHits); + EXPECT_EQUAL(0u, terms[0].maxScore); // NB: not set + EXPECT_EQUAL(130, terms[1].weight); + EXPECT_EQUAL(1u, terms[1].estHits); + EXPECT_EQUAL(0u, terms[1].maxScore); // NB: not set + EXPECT_EQUAL(140, terms[2].weight); + EXPECT_EQUAL(2u, terms[2].estHits); + EXPECT_EQUAL(0u, terms[2].maxScore); // NB: not set + } + { + wa.fetchPostings(false); + SearchIterator::UP search = wa.createSearch(*md, false); + EXPECT_TRUE(dynamic_cast<WeakAndSearch*>(search.get()) != 0); + EXPECT_TRUE(search->seek(1)); + EXPECT_TRUE(search->seek(2)); + EXPECT_FALSE(search->seek(3)); + EXPECT_TRUE(search->seek(4)); + EXPECT_TRUE(search->seek(5)); + EXPECT_FALSE(search->seek(6)); + } + } + } +} + +TEST("require_that_unpack_of_or_over_multisearch_is_optimized") { + Blueprint::UP child1( + ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(1,1).create())). + addChild(ap(MyLeafSpec(20).addField(2,2).create())). + addChild(ap(MyLeafSpec(10).addField(3,3).create())))); + Blueprint::UP child2( + ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(4,4).create())). + addChild(ap(MyLeafSpec(20).addField(5,5).create())). + addChild(ap(MyLeafSpec(10).addField(6,6).create())))); + Blueprint::UP top_up( + ap((new OrBlueprint())-> + addChild(std::move(child1)). + addChild(std::move(child2)))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(2)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(1)->tagAsNotNeeded(); + md->resolveTermField(3)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::SelectiveUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(4)->tagAsNotNeeded(); + md->resolveTermField(6)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::SelectiveUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(5)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::NoUnpack>", + top_up->createSearch(*md, false)->getClassName()); +} + +TEST("require_that_unpack_of_or_is_optimized") { + Blueprint::UP top_up( + ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(1,1).create())). + addChild(ap(MyLeafSpec(20).addField(2,2).create())). + addChild(ap(MyLeafSpec(10).addField(3,3).create())))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(2)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::(anonymous namespace)::SelectiveUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(1)->tagAsNotNeeded(); + md->resolveTermField(3)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<false, search::queryeval::NoUnpack>", + top_up->createSearch(*md, false)->getClassName()); +} + +TEST("require_that_unpack_of_and_is_optimized") { + Blueprint::UP top_up( + ap((new AndBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(1,1).create())). + addChild(ap(MyLeafSpec(20).addField(2,2).create())). + addChild(ap(MyLeafSpec(10).addField(3,3).create())))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(2)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::(anonymous namespace)::SelectiveUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(1)->tagAsNotNeeded(); + md->resolveTermField(3)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::NoUnpack>", + top_up->createSearch(*md, false)->getClassName()); +} + +TEST("require_that_unpack_optimization_is_honoured_by_parents") { + Blueprint::UP top_up( + ap((new AndBlueprint())-> + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(1,1).create())). + addChild(ap(MyLeafSpec(20).addField(2,2).create())). + addChild(ap(MyLeafSpec(10).addField(3,3).create())))))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(2)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::(anonymous namespace)::FullUnpack>", + top_up->createSearch(*md, false)->getClassName()); + md->resolveTermField(1)->tagAsNotNeeded(); + md->resolveTermField(3)->tagAsNotNeeded(); + EXPECT_EQUAL("search::queryeval::AndSearchNoStrict<search::queryeval::NoUnpack>", + top_up->createSearch(*md, false)->getClassName()); +} + +namespace { + +SimpleStringTerm +makeTerm(const std::string & term) +{ + return SimpleStringTerm(term, "field", 0, search::query::Weight(0)); +} + +} + +TEST("require that children does not optimize when parents refuse them to") { + FakeRequestContext requestContext; + search::diskindex::TestDiskIndex index; + vespalib::mkdir("index", false); + index.buildSchema(); + index.openIndex("index/1", false, true, false, false, false); + FieldSpecBaseList fields; + fields.add(FieldSpecBase(1, 11)); + fields.add(FieldSpecBase(2, 22)); + search::fef::MatchDataLayout subLayout; + search::fef::TermFieldHandle idxth21 = subLayout.allocTermField(2); + search::fef::TermFieldHandle idxth22 = subLayout.allocTermField(2); + search::fef::TermFieldHandle idxth1 = subLayout.allocTermField(1); + Blueprint::UP top_up( + ap((new EquivBlueprint(fields, subLayout))-> + addTerm(index.getIndex().createBlueprint(requestContext, + FieldSpec("f2", 2, idxth22, true), + makeTerm("w2")), + 1.0). + addTerm(index.getIndex().createBlueprint(requestContext, + FieldSpec("f1", 1, idxth1), + makeTerm("w1")), + 1.0). + addTerm(index.getIndex().createBlueprint(requestContext, + FieldSpec("f2", 2, idxth21), makeTerm("w2")), + 1.0))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + SearchIterator::UP search = top_up->createSearch(*md, true); + EXPECT_EQUAL("search::queryeval::EquivImpl<true>", search->getClassName()); + { + const MultiSearch & e = dynamic_cast<const MultiSearch &>(*search); + EXPECT_EQUAL("search::BitVectorIteratorStrict", e.getChildren()[0]->getClassName()); + EXPECT_EQUAL("search::diskindex::Zc4RareWordPosOccIterator<true>", e.getChildren()[1]->getClassName()); + EXPECT_EQUAL("search::diskindex::Zc4RareWordPosOccIterator<true>", e.getChildren()[2]->getClassName()); + } + + md->resolveTermField(12)->tagAsNotNeeded(); + search = top_up->createSearch(*md, true); + EXPECT_EQUAL("search::queryeval::EquivImpl<true>", search->getClassName()); + { + const MultiSearch & e = dynamic_cast<const MultiSearch &>(*search); + EXPECT_EQUAL("search::BitVectorIteratorStrict", e.getChildren()[0]->getClassName()); + EXPECT_EQUAL("search::diskindex::Zc4RareWordPosOccIterator<true>", e.getChildren()[1]->getClassName()); + EXPECT_EQUAL("search::diskindex::Zc4RareWordPosOccIterator<true>", e.getChildren()[2]->getClassName()); + } +} + +TEST("require_that_unpack_optimization_is_overruled_by_equiv") { + FieldSpecBaseList fields; + fields.add(FieldSpecBase(1, 1)); + fields.add(FieldSpecBase(2, 2)); + fields.add(FieldSpecBase(3, 3)); + search::fef::MatchDataLayout subLayout; + search::fef::TermFieldHandle idxth1 = subLayout.allocTermField(1); + search::fef::TermFieldHandle idxth2 = subLayout.allocTermField(2); + search::fef::TermFieldHandle idxth3 = subLayout.allocTermField(3); + Blueprint::UP top_up( + ap((new EquivBlueprint(fields, subLayout))-> + addTerm(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).addField(1,idxth1).create())). + addChild(ap(MyLeafSpec(20).addField(2,idxth2).create())). + addChild(ap(MyLeafSpec(10).addField(3,idxth3).create()))), + 1.0))); + MatchData::UP md = MatchData::makeTestInstance(0, 100, 10); + top_up->fetchPostings(false); + SearchIterator::UP search = top_up->createSearch(*md, true); + EXPECT_EQUAL("search::queryeval::EquivImpl<true>", search->getClassName()); + { + const MultiSearch & e = dynamic_cast<const MultiSearch &>(*search); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<true, search::queryeval::(anonymous namespace)::FullUnpack>", + e.getChildren()[0]->getClassName()); + } + + md->resolveTermField(2)->tagAsNotNeeded(); + search = top_up->createSearch(*md, true); + EXPECT_EQUAL("search::queryeval::EquivImpl<true>", search->getClassName()); + { + const MultiSearch & e = dynamic_cast<const MultiSearch &>(*search); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<true, search::queryeval::(anonymous namespace)::FullUnpack>", + e.getChildren()[0]->getClassName()); + } + + md->resolveTermField(1)->tagAsNotNeeded(); + md->resolveTermField(3)->tagAsNotNeeded(); + search = top_up->createSearch(*md, true); + EXPECT_EQUAL("search::queryeval::EquivImpl<true>", search->getClassName()); + { + const MultiSearch & e = dynamic_cast<const MultiSearch &>(*search); + EXPECT_EQUAL("search::queryeval::OrLikeSearch<true, search::queryeval::(anonymous namespace)::FullUnpack>", + e.getChildren()[0]->getClassName()); + } +} + +TEST("require that children of near are not optimized") { + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new NearBlueprint(10))-> + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())))). + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(30).create())))))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new NearBlueprint(10))-> + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())))). + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(30).create())))))); + //------------------------------------------------------------------------- + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST("require that children of onear are not optimized") { + //------------------------------------------------------------------------- + Blueprint::UP top_up( + ap((new ONearBlueprint(10))-> + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).create()->estimate(20))). + addChild(ap(MyLeafSpec(0, true).create()->estimate(0, true))))). + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create()->estimate(0, true))). + addChild(ap(MyLeafSpec(30).create()->estimate(30))))))); + //------------------------------------------------------------------------- + Blueprint::UP expect_up( + ap((new ONearBlueprint(10))-> + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(20).create())). + addChild(ap(MyLeafSpec(0, true).create())))). + addChild(ap((new OrBlueprint())-> + addChild(ap(MyLeafSpec(0, true).create())). + addChild(ap(MyLeafSpec(30).create())))))); + //------------------------------------------------------------------------- + top_up = Blueprint::optimize(std::move(top_up)); + EXPECT_EQUAL(expect_up->asString(), top_up->asString()); +} + +TEST_MAIN() { TEST_DEBUG("lhs.out", "rhs.out"); TEST_RUN_ALL(); } diff --git a/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp new file mode 100644 index 00000000000..a2353184c9f --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp @@ -0,0 +1,125 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("blueprint_test"); +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/searchlib/queryeval/blueprint.h> +#include <vespa/searchlib/queryeval/leaf_blueprints.h> +#include <vespa/vespalib/objects/visit.h> + +using namespace search::queryeval; +using namespace search::fef; + +class Test : public vespalib::TestApp +{ +public: + void testEmptyBlueprint(); + void testSimpleBlueprint(); + void testFakeBlueprint(); + int Main(); +}; + +void +Test::testEmptyBlueprint() +{ + MatchData::UP md(MatchData::makeTestInstance(0, 100, 10)); + EmptyBlueprint empty(FieldSpecBase(1, 11)); + EmptyBlueprint copy(empty); + ASSERT_TRUE(copy.getState().numFields() == 1u); + EXPECT_EQUAL(1u, copy.getState().field(0).getFieldId()); + EXPECT_EQUAL(11u, copy.getState().field(0).getHandle()); + + copy.fetchPostings(true); + SearchIterator::UP search = copy.createSearch(*md, true); + + SimpleResult res; + res.search(*search); + SimpleResult expect; // empty + EXPECT_EQUAL(res, expect); +} + +void +Test::testSimpleBlueprint() +{ + MatchData::UP md(MatchData::makeTestInstance(0, 100, 10)); + SimpleResult a; + a.addHit(3).addHit(5).addHit(7); + SimpleBlueprint simple(a); + simple.tag("tag"); + SimpleBlueprint copy(simple); + EXPECT_EQUAL("tag", copy.tag()); + copy.fetchPostings(true); + SearchIterator::UP search = copy.createSearch(*md, true); + + SimpleResult res; + res.search(*search); + SimpleResult expect; + expect.addHit(3).addHit(5).addHit(7); + EXPECT_EQUAL(res, expect); +} + +void +Test::testFakeBlueprint() +{ + MatchData::UP md(MatchData::makeTestInstance(0, 100, 10)); + FakeResult fake; + fake.doc(10).len(50).pos(2).pos(3) + .doc(25).len(10).pos(5); + + uint32_t fieldId = 0; + TermFieldHandle handle = 0; + FakeBlueprint orig(FieldSpec("<field>", fieldId, handle), fake); + FakeBlueprint copy(orig); + + copy.fetchPostings(true); + SearchIterator::UP search = copy.createSearch(*md, true); + search->initFullRange(); + EXPECT_TRUE(!search->seek(1u)); + EXPECT_EQUAL(10u, search->getDocId()); + { + search->unpack(10u); + TermFieldMatchData &data = *md->resolveTermField(handle); + EXPECT_EQUAL(fieldId, data.getFieldId()); + EXPECT_EQUAL(10u, data.getDocId()); + EXPECT_EQUAL(10u, data.getDocId()); + FieldPositionsIterator itr = data.getIterator(); + EXPECT_EQUAL(50u, itr.getFieldLength()); + EXPECT_EQUAL(2u, itr.size()); + ASSERT_TRUE(itr.valid()); + EXPECT_EQUAL(2u, itr.getPosition()); + itr.next(); + ASSERT_TRUE(itr.valid()); + EXPECT_EQUAL(3u, itr.getPosition()); + itr.next(); + EXPECT_TRUE(!itr.valid()); + } + EXPECT_TRUE(search->seek(25)); + EXPECT_EQUAL(25u, search->getDocId()); + { + search->unpack(25u); + TermFieldMatchData &data = *md->resolveTermField(handle); + EXPECT_EQUAL(fieldId, data.getFieldId()); + EXPECT_EQUAL(25u, data.getDocId()); + FieldPositionsIterator itr = data.getIterator(); + EXPECT_EQUAL(10u, itr.getFieldLength()); + EXPECT_EQUAL(1u, itr.size()); + ASSERT_TRUE(itr.valid()); + EXPECT_EQUAL(5u, itr.getPosition()); + itr.next(); + EXPECT_TRUE(!itr.valid()); + } + EXPECT_TRUE(!search->seek(50)); + EXPECT_TRUE(search->isAtEnd()); +} + +int +Test::Main() +{ + TEST_INIT("leaf_blueprints_test"); + testEmptyBlueprint(); + testSimpleBlueprint(); + testFakeBlueprint(); + TEST_DONE(); +} + +TEST_APPHOOK(Test); diff --git a/searchlib/src/tests/queryeval/blueprint/mysearch.h b/searchlib/src/tests/queryeval/blueprint/mysearch.h new file mode 100644 index 00000000000..7ab852b384f --- /dev/null +++ b/searchlib/src/tests/queryeval/blueprint/mysearch.h @@ -0,0 +1,155 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/searchlib/queryeval/blueprint.h> +#include <vespa/vespalib/objects/visit.h> + +namespace search { +namespace queryeval { + +//----------------------------------------------------------------------------- + +class MySearch : public SearchIterator +{ +public: + typedef MultiSearch::Children Children; + typedef std::vector<SearchIterator::UP> MyChildren; + typedef search::fef::TermFieldMatchDataArray TFMDA; + typedef search::fef::MatchData MatchData; + +private: + std::string _tag; + bool _isLeaf; + bool _isStrict; + MyChildren _children; + TFMDA _match; + MatchData *_md; + + std::vector<uint32_t> _handles; + +protected: + virtual void doSeek(uint32_t) {} + virtual void doUnpack(uint32_t) {} + +public: + MySearch(const std::string &tag, bool leaf, bool strict) + : _tag(tag), _isLeaf(leaf), _isStrict(strict), _children(), + _match(), _md(0) {} + + MySearch(const std::string &tag, const TFMDA &tfmda, bool strict) + : _tag(tag), _isLeaf(true), _isStrict(strict), _children(), + _match(tfmda), _md(0) {} + + MySearch(const std::string &tag, const Children &children, + MatchData *md, bool strict) + : _tag(tag), _isLeaf(false), _isStrict(strict), _children(), + _match(), _md(md) { + for (size_t i(0); i < children.size(); i++) { + _children.emplace_back(children[i]); + } + } + + MySearch &add(SearchIterator *search) { + _children.emplace_back(search); + return *this; + } + + MySearch &addHandle(uint32_t handle) { + _handles.push_back(handle); + return *this; + } + + bool verifyAndInferImpl(MatchData &md) { + bool ok = true; + if (!_isLeaf) { + ok &= (_md == &md); + } + for (size_t i = 0; i < _children.size(); ++i) { + MySearch *child = dynamic_cast<MySearch *>(_children[i].get()); + ok &= (child != 0); + if (child != 0) { + ok &= child->verifyAndInferImpl(md); + } + } + for (size_t i = 0; i < _match.size(); ++i) { + search::fef::TermFieldMatchData *tfmd = _match[i]; + _handles.push_back(search::fef::IllegalHandle); + for (search::fef::TermFieldHandle j = 0; j < md.getNumTermFields(); ++j) { + if (md.resolveTermField(j) == tfmd) { + _handles.back() = j; + break; + } + } + ok &= (_handles.back() != search::fef::IllegalHandle); + } + return ok; + } + + static bool verifyAndInfer(SearchIterator *search, MatchData &md) { + MySearch *self = dynamic_cast<MySearch *>(search); + if (self == 0) { + return false; + } else { + return self->verifyAndInferImpl(md); + } + } + + virtual void visitMembers(vespalib::ObjectVisitor &visitor) const { + visit(visitor, "_tag", _tag); + visit(visitor, "_isLeaf", _isLeaf); + visit(visitor, "_isStrict", _isStrict); + visit(visitor, "_children", _children); + visit(visitor, "_handles", _handles); + } + + virtual ~MySearch() {} +}; + +//----------------------------------------------------------------------------- + +class MyLeaf : public SimpleLeafBlueprint +{ + typedef search::fef::TermFieldMatchDataArray TFMDA; + +public: + virtual SearchIterator::UP + createLeafSearch(const TFMDA &tfmda, bool strict) const + { + return SearchIterator::UP(new MySearch("leaf", tfmda, strict)); + } + + MyLeaf(const FieldSpecBaseList &fields) + : SimpleLeafBlueprint(fields) + {} + + MyLeaf &estimate(uint32_t hits, bool empty = false) { + setEstimate(HitEstimate(hits, empty)); + return *this; + } +}; + +//----------------------------------------------------------------------------- + +class MyLeafSpec +{ +private: + FieldSpecBaseList _fields; + Blueprint::HitEstimate _estimate; + +public: + explicit MyLeafSpec(uint32_t estHits, bool empty = false) + : _fields(), _estimate(estHits, empty) {} + + MyLeafSpec &addField(uint32_t fieldId, uint32_t handle) { + _fields.add(FieldSpecBase(fieldId, handle)); + return *this; + } + MyLeaf *create() const { + MyLeaf *leaf = new MyLeaf(_fields); + leaf->estimate(_estimate.estHits, _estimate.empty); + return leaf; + } +}; + +//----------------------------------------------------------------------------- + +} // namespace queryeval +} // namespace search |