summaryrefslogtreecommitdiffstats
path: root/searchlib/src/tests/queryeval/blueprint
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /searchlib/src/tests/queryeval/blueprint
Publish
Diffstat (limited to 'searchlib/src/tests/queryeval/blueprint')
-rw-r--r--searchlib/src/tests/queryeval/blueprint/.cvsignore3
-rw-r--r--searchlib/src/tests/queryeval/blueprint/.gitignore8
-rw-r--r--searchlib/src/tests/queryeval/blueprint/CMakeLists.txt23
-rw-r--r--searchlib/src/tests/queryeval/blueprint/DESC1
-rw-r--r--searchlib/src/tests/queryeval/blueprint/FILES1
-rw-r--r--searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp766
-rw-r--r--searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp1332
-rw-r--r--searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp125
-rw-r--r--searchlib/src/tests/queryeval/blueprint/mysearch.h155
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