diff options
author | Håvard Pettersen <havardpe@yahooinc.com> | 2022-10-24 15:06:43 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@yahooinc.com> | 2022-10-24 15:06:43 +0000 |
commit | 8ea06975f4874f15fb33997a1aa7dde24a2d89ba (patch) | |
tree | 50c336a047265a6945acc38b656316ae55632ed7 /searchlib | |
parent | 1afef7d4676c17d70087aeb36df228faf7073afd (diff) |
filter search test initial version
Diffstat (limited to 'searchlib')
-rw-r--r-- | searchlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | searchlib/src/tests/queryeval/filter_search/CMakeLists.txt | 10 | ||||
-rw-r--r-- | searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp | 157 |
3 files changed, 168 insertions, 0 deletions
diff --git a/searchlib/CMakeLists.txt b/searchlib/CMakeLists.txt index 30d00410758..20df8cd3683 100644 --- a/searchlib/CMakeLists.txt +++ b/searchlib/CMakeLists.txt @@ -190,6 +190,7 @@ vespa_define_module( src/tests/queryeval/dot_product src/tests/queryeval/equiv src/tests/queryeval/fake_searchable + src/tests/queryeval/filter_search src/tests/queryeval/getnodeweight src/tests/queryeval/global_filter src/tests/queryeval/matching_elements_search diff --git a/searchlib/src/tests/queryeval/filter_search/CMakeLists.txt b/searchlib/src/tests/queryeval/filter_search/CMakeLists.txt new file mode 100644 index 00000000000..ad2da1f6eed --- /dev/null +++ b/searchlib/src/tests/queryeval/filter_search/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(searchlib_filter_search_test_app TEST + SOURCES + filter_search_test.cpp + DEPENDS + searchlib + searchlib_test + GTest::GTest +) +vespa_add_test(NAME searchlib_filter_search_test_app COMMAND searchlib_filter_search_test_app) diff --git a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp new file mode 100644 index 00000000000..fe9f6424b9c --- /dev/null +++ b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp @@ -0,0 +1,157 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/searchlib/queryeval/simpleresult.h> +#include <vespa/searchlib/queryeval/blueprint.h> +#include <vespa/searchlib/queryeval/leaf_blueprints.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; +using vespalib::Trinary; + +using Constraint = Blueprint::FilterConstraint; +constexpr auto lower_bound = Constraint::LOWER_BOUND; +constexpr auto upper_bound = Constraint::UPPER_BOUND; + +const uint32_t docid_limit = 100; + +template <typename T> +concept FilterFactory = requires(const T &a, bool strict, Constraint upper_or_lower) { + { 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; + } + } +}; + +// 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>(); +} + +// make a simple result containing the given documents +SimpleResult make_result(const std::vector<uint32_t> &docs) { + SimpleResult result; + for (uint32_t doc: docs) { + result.addHit(doc); + } + return result; +} + +// make a simple result containing all the documents +SimpleResult make_full_result() { + SimpleResult result; + for (uint32_t docid = 1; docid < docid_limit; ++docid) { + result.addHit(docid); + } + return result; +} + +// make a simple result containing none of the documents +SimpleResult make_empty_result() { + return SimpleResult(); +} + +// 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)); +} + +// what kind of results are we expecting from a filter search? +struct Expect { + Trinary matches_any; + SimpleResult hits; + Expect(const std::vector<uint32_t> &hits_in) + : matches_any(Trinary::Undefined), hits(make_result(hits_in)) {} + Expect(Trinary matches_any_in) : matches_any(matches_any_in), hits() { + REQUIRE(matches_any != Trinary::Undefined); + if (matches_any == Trinary::True) { + hits = make_full_result(); + } else { + hits = make_empty_result(); + } + } +}; + +template <FilterFactory Blueprint> +void verify(const Blueprint &blueprint, const Expect &upper, const Expect &lower) { + for (auto constraint: {lower_bound, upper_bound}) { + const Expect &expect = (constraint == upper_bound) ? upper : lower; + for (bool strict: {false, true}) { + auto filter = blueprint.createFilterSearch(strict, constraint); + EXPECT_EQ(filter->matches_any(), expect.matches_any); + SimpleResult actual; + if (strict) { + actual.searchStrict(*filter, docid_limit); + } else { + actual.search(*filter, docid_limit); + } + EXPECT_EQ(actual, expect.hits); + } + } +} + +template <FilterFactory Blueprint> +void verify(const Blueprint &blueprint, const Expect &upper_and_lower) { + verify(blueprint, upper_and_lower, upper_and_lower); +} + +TEST(FilterSearchTest, empty_leaf) { + verify(*empty(), Expect(Trinary::False)); +} + +TEST(FilterSearchTest, full_leaf) { + verify(*full(), Expect(Trinary::True)); +} + +TEST(FilterSearchTest, custom_leaf) { + verify(*leaf({5,10,20}), Expect({5,10,20})); +} + +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})); +} + +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})); +} + +GTEST_MAIN_RUN_ALL_TESTS() |