diff options
Diffstat (limited to 'searchlib/src/tests')
3 files changed, 214 insertions, 159 deletions
diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp index 8e8327584a7..3c0b49da762 100644 --- a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp +++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp @@ -30,7 +30,7 @@ public: return mixChildrenFields(); } - virtual void sort(std::vector<Blueprint*> &children) const override { + virtual void sort(Children &children) const override { std::sort(children.begin(), children.end(), TieredGreaterEstimate()); } diff --git a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp index 2379213b87b..ef0fd56840a 100644 --- a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp +++ b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp @@ -56,6 +56,18 @@ bool got_global_filter(Blueprint &b) { return (static_cast<MyLeaf &>(b)).got_global_filter(); } +void check_sort_order(IntermediateBlueprint &self, std::vector<Blueprint*> unordered, std::vector<size_t> order) { + ASSERT_EQUAL(unordered.size(), order.size()); + std::vector<Blueprint::UP> children; + for (auto *child: unordered) { + children.push_back(std::unique_ptr<Blueprint>(child)); + } + self.sort(children); + for (size_t i = 0; i < children.size(); ++i) { + EXPECT_EQUAL(children[i].get(), unordered[order[i]]); + } +} + TEST("test AndNot Blueprint") { AndNotBlueprint b; { // combine @@ -86,20 +98,12 @@ TEST("test AndNot Blueprint") { EXPECT_EQUAL(true, got_global_filter(a.getChild(1))); } { - 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]); + check_sort_order(b, + {MyLeafSpec(10).create(), + MyLeafSpec(20).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {0, 2, 3, 1}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -161,20 +165,12 @@ TEST("test And Blueprint") { EXPECT_EQUAL(true, got_global_filter(a.getChild(1))); } { - 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]); + check_sort_order(b, + {MyLeafSpec(20).create(), + MyLeafSpec(40).create(), + MyLeafSpec(10).create(), + MyLeafSpec(30).create()}, + {2, 0, 3, 1}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -241,20 +237,12 @@ TEST("test Or Blueprint") { EXPECT_EQUAL(true, got_global_filter(o.getChild(o.childCnt() - 1))); } { - 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]); + check_sort_order(b, + {MyLeafSpec(10).create(), + MyLeafSpec(20).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {2, 3, 1, 0}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -290,20 +278,12 @@ TEST("test Near Blueprint") { 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]); + check_sort_order(b, + {MyLeafSpec(40).create(), + MyLeafSpec(10).create(), + MyLeafSpec(30).create(), + MyLeafSpec(20).create()}, + {1, 3, 2, 0}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -339,20 +319,12 @@ TEST("test ONear Blueprint") { 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]); + check_sort_order(b, + {MyLeafSpec(20).create(), + MyLeafSpec(10).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {0, 1, 2, 3}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -396,20 +368,12 @@ TEST("test Rank Blueprint") { EXPECT_EQUAL(true, got_global_filter(a.getChild(1))); } { - 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]); + check_sort_order(b, + {MyLeafSpec(20).create(), + MyLeafSpec(10).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {0, 1, 2, 3}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -469,20 +433,12 @@ TEST("test SourceBlender Blueprint") { 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]); + check_sort_order(b, + {MyLeafSpec(20).create(), + MyLeafSpec(10).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {0, 1, 2, 3}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); @@ -1080,20 +1036,12 @@ TEST("test WeakAnd Blueprint") { 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]); + check_sort_order(b, + {MyLeafSpec(20).create(), + MyLeafSpec(10).create(), + MyLeafSpec(40).create(), + MyLeafSpec(30).create()}, + {0, 1, 2, 3}); } { EXPECT_EQUAL(true, b.inheritStrict(0)); diff --git a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp index fe9f6424b9c..5741d210079 100644 --- a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp +++ b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp @@ -2,18 +2,17 @@ #include <vespa/searchlib/queryeval/simpleresult.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/isourceselector.h> #include <vespa/vespalib/util/trinary.h> #include <vespa/vespalib/util/require.h> #include <vespa/vespalib/gtest/gtest.h> #include <functional> -using search::queryeval::AlwaysTrueBlueprint; -using search::queryeval::Blueprint; -using search::queryeval::EmptyBlueprint; -using search::queryeval::SimpleBlueprint; -using search::queryeval::SimpleResult; -using search::queryeval::SearchIterator; +namespace search::fef { class MatchData; } + +using namespace search::queryeval; using vespalib::Trinary; using Constraint = Blueprint::FilterConstraint; @@ -27,37 +26,29 @@ concept FilterFactory = requires(const T &a, bool strict, Constraint upper_or_lo { a.createFilterSearch(strict, upper_or_lower) } -> std::same_as<std::unique_ptr<SearchIterator>>; }; -using factory_fun = std::function<std::unique_ptr<SearchIterator>(const std::vector<Blueprint*> &, bool, Constraint)>; - -// Combine children blueprints using a shared filter creation -// algorithm. Satisfies the FilterFactory concept. -struct Combine { - factory_fun fun; - std::vector<Blueprint*> list; - Combine(factory_fun fun_in) : fun(fun_in), list() {} - Combine &&add(std::unique_ptr<Blueprint> child) && { - list.push_back(child.release()); - return std::move(*this); - } - auto createFilterSearch(bool strict, Constraint upper_or_lower) const { - return fun(list, strict, upper_or_lower); - } - ~Combine() { - for (auto *ptr: list) { - delete ptr; - } - } +template <typename T> +concept FilterFactoryBuilder = requires(T a, std::unique_ptr<Blueprint> bp) { + { std::move(a).add(std::move(bp)) } -> std::same_as<T&&>; }; -// create a leaf blueprint that matches no documents -std::unique_ptr<Blueprint> empty() { - return std::make_unique<EmptyBlueprint>(); -} +// inherit Blueprint to capture the default filter factory +struct DefaultBlueprint : Blueprint { + void optimize(Blueprint* &) override { abort(); } + const State &getState() const override { abort(); } + void fetchPostings(const ExecuteInfo &) override { abort(); } + void freeze() override { abort(); } + SearchIteratorUP createSearch(search::fef::MatchData &, bool) const override { abort(); } +}; -// create a leaf blueprint that matches all documents -std::unique_ptr<Blueprint> full() { - return std::make_unique<AlwaysTrueBlueprint>(); -} +// need one of these to be able to create a SourceBlender +struct NullSelector : ISourceSelector { + NullSelector() : ISourceSelector(7) {} + void setSource(uint32_t, Source) override { abort(); } + uint32_t getDocIdLimit() const override { abort(); } + void compactLidSpace(uint32_t) override { abort(); } + std::unique_ptr<sourceselector::Iterator> createIterator() const override { abort(); } +}; +NullSelector null_selector; // make a simple result containing the given documents SimpleResult make_result(const std::vector<uint32_t> &docs) { @@ -82,11 +73,89 @@ SimpleResult make_empty_result() { return SimpleResult(); } +// create a leaf blueprint that matches no documents +std::unique_ptr<Blueprint> empty() { + return std::make_unique<EmptyBlueprint>(); +} + +// create a leaf blueprint that matches all documents +std::unique_ptr<Blueprint> full() { + return std::make_unique<AlwaysTrueBlueprint>(); +} + // create a leaf blueprint with the specified hits std::unique_ptr<Blueprint> leaf(const std::vector<uint32_t> &docs) { return std::make_unique<SimpleBlueprint>(make_result(docs)); } +// Describes blueprint children with a list of simple factories that +// can later be used to create them. +struct Children { + using Factory = std::function<Blueprint::UP()>; + std::vector<Factory> list; + Children() : list() {} + size_t size() const { return list.size(); } + Children &&leaf(const std::vector<uint32_t> &docs) && { + list.push_back([docs](){ return ::leaf(docs); }); + return std::move(*this); + } + Children &&full() && { + list.push_back([](){ return ::full(); }); + return std::move(*this); + } + Children &&empty() && { + list.push_back([](){ return ::empty(); }); + return std::move(*this); + } + template <FilterFactoryBuilder Builder> + Builder &&apply(Builder &&builder) const { + for (const Factory &make_child: list) { + std::move(builder).add(make_child()); + } + return std::move(builder); + } +}; + +// Combine children blueprints using a shared filter creation +// algorithm. Satisfies the FilterFactory concept. +struct Combine { + using factory_fun = std::function<std::unique_ptr<SearchIterator>(const Blueprint::Children &, bool, Constraint)>; + factory_fun fun; + Blueprint::Children list; + Combine(factory_fun fun_in) noexcept : fun(fun_in), list() {} + Combine &&add(std::unique_ptr<Blueprint> child) && { + list.push_back(std::move(child)); + return std::move(*this); + } + Combine &&add(const Children &children) && { + return children.apply(std::move(*this)); + } + auto createFilterSearch(bool strict, Constraint upper_or_lower) const { + return fun(list, strict, upper_or_lower); + } + ~Combine(); +}; +Combine::~Combine() = default; + +// Make a specific (intermediate) blueprint that you can add children +// to. Satisfies the FilterFactory concept. +template <FilterFactory T> +struct Make { + T blueprint; + template <typename ... Args> + Make(Args && ... args) : blueprint(std::forward<Args>(args)...) {} + Make &&add(std::unique_ptr<Blueprint> child) && { + blueprint.addChild(std::move(child)); + return std::move(*this); + } + Make &&add(const Children &children) && { + return children.apply(std::move(*this)); + } + auto createFilterSearch(bool strict, Constraint upper_or_lower) const { + return blueprint.createFilterSearch(strict, upper_or_lower); + } +}; + // what kind of results are we expecting from a filter search? struct Expect { Trinary matches_any; @@ -101,6 +170,8 @@ struct Expect { hits = make_empty_result(); } } + static Expect empty() { return Expect(Trinary::False); } + static Expect full() { return Expect(Trinary::True); } }; template <FilterFactory Blueprint> @@ -127,31 +198,67 @@ void verify(const Blueprint &blueprint, const Expect &upper_and_lower) { } TEST(FilterSearchTest, empty_leaf) { - verify(*empty(), Expect(Trinary::False)); + verify(*empty(), Expect::empty()); } TEST(FilterSearchTest, full_leaf) { - verify(*full(), Expect(Trinary::True)); + verify(*full(), Expect::full()); } TEST(FilterSearchTest, custom_leaf) { verify(*leaf({5,10,20}), Expect({5,10,20})); } +TEST(FilterSearchTest, default_blueprint) { + verify(DefaultBlueprint(), Expect::full(), Expect::empty()); +} + TEST(FilterSearchTest, simple_or) { - verify(Combine(Blueprint::create_or_filter) - .add(leaf({5, 10})) - .add(leaf({7})) - .add(leaf({3, 11})), - Expect({3, 5, 7, 10, 11})); + auto child_list = Children() + .leaf({5, 10}) + .leaf({7}) + .leaf({3, 11}); + auto expected = Expect({3, 5, 7, 10, 11}); + verify(Combine(Blueprint::create_or_filter).add(child_list), expected); + verify(Make<OrBlueprint>().add(child_list), expected); + verify(Combine(Blueprint::create_atmost_or_filter).add(child_list), expected, Expect::empty()); + verify(Make<WeakAndBlueprint>(child_list.size()).add(child_list), expected, Expect::empty()); + verify(Make<SourceBlenderBlueprint>(null_selector).add(child_list), expected, Expect::empty()); } TEST(FilterSearchTest, simple_and) { - verify(Combine(Blueprint::create_and_filter) - .add(leaf({1, 2, 3, 4, 5, 6})) - .add(leaf({2, 4, 6, 7})) - .add(leaf({1, 4, 6, 7, 10})), - Expect({4, 6})); + auto child_list = Children() + .leaf({1, 2, 3, 4, 5, 6}) + .leaf({2, 4, 6, 7}) + .leaf({1, 4, 6, 7, 10}); + auto expected = Expect({4, 6}); + verify(Combine(Blueprint::create_and_filter).add(child_list), expected); + verify(Make<AndBlueprint>().add(child_list), expected); + verify(Combine(Blueprint::create_atmost_and_filter).add(child_list), expected, Expect::empty()); + verify(Make<NearBlueprint>(3).add(child_list), expected, Expect::empty()); + verify(Make<ONearBlueprint>(3).add(child_list), expected, Expect::empty()); +} + +TEST(FilterSearchTest, simple_andnot) { + auto child_list = Children() + .leaf({1, 2, 3, 4, 5, 6}) + .leaf({2, 4, 6}) + .leaf({4, 6, 7}); + auto expected = Expect({1, 3, 5}); + verify(Combine(Blueprint::create_andnot_filter).add(child_list), expected); + verify(Make<AndNotBlueprint>().add(child_list), expected); +} + +TEST(FilterSearchTest, rank_filter) { + auto child_list1 = Children().leaf({1,2,3}).empty().full(); + auto child_list2 = Children().empty().leaf({1,2,3}).full(); + auto child_list3 = Children().full().leaf({1,2,3}).empty(); + verify(Combine(Blueprint::create_first_child_filter).add(child_list1), Expect({1,2,3})); + verify(Combine(Blueprint::create_first_child_filter).add(child_list2), Expect::empty()); + verify(Combine(Blueprint::create_first_child_filter).add(child_list3), Expect::full()); + verify(Make<RankBlueprint>().add(child_list1), Expect({1,2,3})); + verify(Make<RankBlueprint>().add(child_list2), Expect::empty()); + verify(Make<RankBlueprint>().add(child_list3), Expect::full()); } GTEST_MAIN_RUN_ALL_TESTS() |