From 8a2279f1c8a50903960a8e3854c270377eed3f54 Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Sun, 4 Sep 2022 21:41:42 +0000 Subject: Find and locate range query item in the 'and' path of a query. If found and correlates with match phase limiter, copy the limits into the limiter. --- .../matching/match_phase_limiter/CMakeLists.txt | 1 + .../match_phase_limiter_test.cpp | 108 +++++++++++++++++++-- 2 files changed, 103 insertions(+), 6 deletions(-) (limited to 'searchcore/src/tests/proton') diff --git a/searchcore/src/tests/proton/matching/match_phase_limiter/CMakeLists.txt b/searchcore/src/tests/proton/matching/match_phase_limiter/CMakeLists.txt index e2ebb119045..276a0dcbf6b 100644 --- a/searchcore/src/tests/proton/matching/match_phase_limiter/CMakeLists.txt +++ b/searchcore/src/tests/proton/matching/match_phase_limiter/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(searchcore_match_phase_limiter_test_app TEST match_phase_limiter_test.cpp DEPENDS searchcore_matching + searchlib_test ) vespa_add_test(NAME searchcore_match_phase_limiter_test_app COMMAND searchcore_match_phase_limiter_test_app) diff --git a/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp b/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp index 5b3b9f962f7..59eff86fab4 100644 --- a/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp +++ b/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp @@ -1,15 +1,22 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include +#include #include #include #include #include #include +#include #include #include +#include +#include +#include +#include #include #include +#include using namespace proton::matching; using namespace search::engine; @@ -24,6 +31,9 @@ using search::queryeval::AndSearchStrict; using search::queryeval::termAsString; using search::queryeval::FakeRequestContext; using search::fef::TermFieldMatchDataArray; +using search::attribute::BasicType; +using search::attribute::Config; +using vespalib::make_string_short::fmt; //----------------------------------------------------------------------------- @@ -90,6 +100,15 @@ struct MockSearchable : Searchable { } }; +class MockRangeLocator : public RangeQueryLocator { +public: + RangeLimitMetaInfo locate() const override { + return {}; + } + +public: +}; + //----------------------------------------------------------------------------- TEST("require that match phase limit calculator gives expert values") { @@ -160,12 +179,14 @@ TEST("require that max group size is calculated correctly") { TEST("require that the attribute limiter works correctly") { FakeRequestContext requestContext; + MockRangeLocator rangeLocator; for (int i = 0; i <= 7; ++i) { bool descending = (i & 1) != 0; bool strict = (i & 2) != 0; bool diverse = (i & 4) != 0; MockSearchable searchable; - AttributeLimiter limiter(searchable, requestContext, "limiter_attribute", descending, "category", 10.0, AttributeLimiter::LOOSE); + AttributeLimiter limiter(rangeLocator, searchable, requestContext, "limiter_attribute", descending, + "category", 10.0, AttributeLimiter::LOOSE); EXPECT_EQUAL(0u, searchable.create_cnt); EXPECT_FALSE(limiter.was_used()); SearchIterator::UP s1 = limiter.create_search(42, diverse ? 3 : 42, strict); @@ -215,7 +236,8 @@ TEST("require that no limiter has no behavior") { TEST("require that the match phase limiter may chose not to limit the query") { FakeRequestContext requestContext; MockSearchable searchable; - MatchPhaseLimiter yes_limiter(10000, searchable, requestContext, + MockRangeLocator rangeLocator; + MatchPhaseLimiter yes_limiter(10000, rangeLocator, searchable, requestContext, DegradationParams("limiter_attribute", 1000, true, 1.0, 0.2, 1.0), DiversityParams("", 1, 10.0, AttributeLimiter::LOOSE)); MaybeMatchPhaseLimiter &limiter = yes_limiter; @@ -234,9 +256,10 @@ struct MaxFilterCoverageLimiterFixture { FakeRequestContext requestContext; MockSearchable searchable; + MockRangeLocator rangeLocator; MatchPhaseLimiter::UP getMaxFilterCoverageLimiter() { - auto yes_limiter = std::make_unique(10000, searchable, requestContext, + auto yes_limiter = std::make_unique(10000, rangeLocator, searchable, requestContext, DegradationParams("limiter_attribute", 10000, true, 0.05, 1.0, 1.0), DiversityParams("", 1, 10.0, AttributeLimiter::LOOSE)); MaybeMatchPhaseLimiter &limiter = *yes_limiter; @@ -286,7 +309,8 @@ void verify(vespalib::stringref expected, const vespalib::Slime & slime) { TEST("require that the match phase limiter is able to pre-limit the query") { FakeRequestContext requestContext; MockSearchable searchable; - MatchPhaseLimiter yes_limiter(10000, searchable, requestContext, + MockRangeLocator rangeLocator; + MatchPhaseLimiter yes_limiter(10000, rangeLocator, searchable, requestContext, DegradationParams("limiter_attribute", 500, true, 1.0, 0.2, 1.0), DiversityParams("", 1, 10.0, AttributeLimiter::LOOSE)); MaybeMatchPhaseLimiter &limiter = yes_limiter; @@ -341,7 +365,8 @@ TEST("require that the match phase limiter is able to pre-limit the query") { TEST("require that the match phase limiter is able to post-limit the query") { MockSearchable searchable; FakeRequestContext requestContext; - MatchPhaseLimiter yes_limiter(10000, searchable, requestContext, + MockRangeLocator rangeLocator; + MatchPhaseLimiter yes_limiter(10000, rangeLocator, searchable, requestContext, DegradationParams("limiter_attribute", 1500, true, 1.0, 0.2, 1.0), DiversityParams("", 1, 10.0, AttributeLimiter::LOOSE)); MaybeMatchPhaseLimiter &limiter = yes_limiter; @@ -373,7 +398,8 @@ void verifyDiversity(AttributeLimiter::DiversityCutoffStrategy strategy) { MockSearchable searchable; FakeRequestContext requestContext; - MatchPhaseLimiter yes_limiter(10000, searchable, requestContext, + MockRangeLocator rangeLocator; + MatchPhaseLimiter yes_limiter(10000, rangeLocator, searchable, requestContext, DegradationParams("limiter_attribute", 500, true, 1.0, 0.2, 1.0), DiversityParams("category", 10, 13.1, strategy)); MaybeMatchPhaseLimiter &limiter = yes_limiter; @@ -401,4 +427,74 @@ TEST("require that the match phase limiter can use strict diversity") { verifyDiversity(AttributeLimiter::STRICT); } +struct RangeLimitFixture { + RangeLimitFixture() + : attrSearchable(), + attrManager(), + attributeContext(attrManager.createContext()), + requestContext(attributeContext.get()), + a1FieldSpec("a1", 1, 1), + f1FieldSpec("f1", 2, 2) + { + attrManager.addAttribute(search::AttributeFactory::createAttribute(a1FieldSpec.getName(), Config(BasicType("int64")))); + attrManager.addAttribute(search::AttributeFactory::createAttribute(f1FieldSpec.getName(), Config(BasicType("double")))); + } + search::AttributeBlueprintFactory attrSearchable; + search::attribute::test::MockAttributeManager attrManager; + search::attribute::IAttributeContext::UP attributeContext; + FakeRequestContext requestContext; + FieldSpec a1FieldSpec; + FieldSpec f1FieldSpec; +}; + +void +verifyLocateRange(const vespalib::string & from, const vespalib::string & to, + const FieldSpec & fieldSpec, RangeLimitFixture & f) +{ + search::query::SimpleNumberTerm term(fmt("[%s;%s]", from.c_str(), to.c_str()), fieldSpec.getName(), 0, search::query::Weight(1)); + Blueprint::UP bp = f.attrSearchable.createBlueprint(f.requestContext, fieldSpec, term); + EXPECT_FALSE(LocateRangeItemFromQuery(*bp, 0).locate().valid()); + EXPECT_TRUE(LocateRangeItemFromQuery(*bp, fieldSpec.getFieldId()).locate().valid()); + LocateRangeItemFromQuery locator(*bp, fieldSpec.getFieldId()); + RangeLimitMetaInfo rangeInfo = locator.locate(); + EXPECT_EQUAL(from, rangeInfo.low()); + EXPECT_EQUAL(to, rangeInfo.high()); +} + +TEST_F("require that RangeLocator locates range from attribute blueprint", RangeLimitFixture) { + verifyLocateRange("7", "100", f.a1FieldSpec, f); + verifyLocateRange("7.7", "100.3", f.f1FieldSpec, f); +} + + +void +verifyRangeIsReflectedInLimiter(const vespalib::string & from, const vespalib::string & to, + const FieldSpec & fieldSpec, RangeLimitFixture & f) +{ + search::query::SimpleNumberTerm term(fmt("[%s;%s]", from.c_str(), to.c_str()), fieldSpec.getName(), 0, search::query::Weight(1)); + Blueprint::UP bp = f.attrSearchable.createBlueprint(f.requestContext, fieldSpec, term); + LocateRangeItemFromQuery locator(*bp, fieldSpec.getFieldId()); + RangeLimitMetaInfo rangeInfo = locator.locate(); + EXPECT_EQUAL(from, rangeInfo.low()); + EXPECT_EQUAL(to, rangeInfo.high()); + + MockSearchable mockSearchable; + MatchPhaseLimiter yes_limiter(10000, locator, mockSearchable, f.requestContext, + DegradationParams(fieldSpec.getName(), 500, true, 1.0, 0.2, 1.0), + DiversityParams()); + MaybeMatchPhaseLimiter &limiter = yes_limiter; + SearchIterator::UP search = limiter.maybe_limit(prepare(new MockSearch("search")), 0.1, 100000, nullptr); + limiter.updateDocIdSpaceEstimate(1000, 9000); + EXPECT_EQUAL(1680u, limiter.getDocIdSpaceEstimate()); + auto *strict_and = dynamic_cast(search.get()); + ASSERT_TRUE(strict_and != nullptr); + const auto *ms1 = dynamic_cast(&strict_and->getFirst()); + ASSERT_TRUE(ms1 != nullptr); + EXPECT_EQUAL(fmt("[%s;%s;-5000]", from.c_str(), to.c_str()), ms1->term); +} +TEST_F("require that range term is used to adjust limiter", RangeLimitFixture) { + verifyRangeIsReflectedInLimiter("7", "100", f.a1FieldSpec, f); + verifyRangeIsReflectedInLimiter("7.7", "100.3", f.f1FieldSpec, f); +} + TEST_MAIN() { TEST_RUN_ALL(); } -- cgit v1.2.3