summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2023-12-09 20:53:25 +0100
committerGitHub <noreply@github.com>2023-12-09 20:53:25 +0100
commitedcdc7775e0a268a6af446351c7e0f09bd8d13de (patch)
tree56a42a96d4b7e05a8f1285c902047fba54a1cbc1
parent8ee864d6a1ffed020d5d2e6158b49ba8037e6ebb (diff)
parent94a774815c48d711ba55ea384cace396bd8d651a (diff)
Merge pull request #29594 from vespa-engine/havardpe/relative-estimate
relative estimate
-rw-r--r--searchcore/src/tests/proton/documentmetastore/lid_allocator/lid_allocator_test.cpp23
-rw-r--r--searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp4
-rw-r--r--searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp9
-rw-r--r--searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp56
-rw-r--r--searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp1
-rw-r--r--searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp2
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.cpp39
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/blueprint.h29
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp64
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h9
10 files changed, 212 insertions, 24 deletions
diff --git a/searchcore/src/tests/proton/documentmetastore/lid_allocator/lid_allocator_test.cpp b/searchcore/src/tests/proton/documentmetastore/lid_allocator/lid_allocator_test.cpp
index b2fa8675835..e136e491f05 100644
--- a/searchcore/src/tests/proton/documentmetastore/lid_allocator/lid_allocator_test.cpp
+++ b/searchcore/src/tests/proton/documentmetastore/lid_allocator/lid_allocator_test.cpp
@@ -102,10 +102,15 @@ protected:
}
return result;
}
-
- SimpleResult get_active_lids_in_search_iterator(uint32_t docid_limit, bool filter) {
+
+ Blueprint::UP make_whitelist_blueprint(uint32_t docid_limit) {
auto blueprint = _allocator.createWhiteListBlueprint();
blueprint->setDocIdLimit(docid_limit);
+ return blueprint;
+ }
+
+ SimpleResult get_active_lids_in_search_iterator(uint32_t docid_limit, bool filter) {
+ auto blueprint = make_whitelist_blueprint(docid_limit);
std::unique_ptr<SearchIterator> iterator;
MatchData md(MatchData::params());
if (filter) {
@@ -119,8 +124,7 @@ protected:
}
Trinary filter_search_iterator_matches_any(uint32_t docid_limit) {
- auto blueprint = _allocator.createWhiteListBlueprint();
- blueprint->setDocIdLimit(docid_limit);
+ auto blueprint = make_whitelist_blueprint(docid_limit);
auto iterator = blueprint->createFilterSearch(true, Blueprint::FilterConstraint::UPPER_BOUND);
return iterator->matches_any();
}
@@ -170,6 +174,17 @@ TEST_F(LidAllocatorTest, filter_search_iterator_matches_all_when_all_lids_are_ac
EXPECT_EQ(SimpleResult({1, 2, 3, 4}), get_active_lids_in_search_iterator(6, false));
}
+TEST_F(LidAllocatorTest, whitelist_blueprint_can_maximize_relative_estimate)
+{
+ register_lids({ 1, 2, 3, 4 });
+ activate_lids({ 1, 2, 3, 4 }, true);
+ // the number of hits are overestimated based on the number of
+ // documents that could be active (100 in this test fixture)
+ EXPECT_EQ(make_whitelist_blueprint(1000)->estimate(), 0.1);
+ EXPECT_EQ(make_whitelist_blueprint(200)->estimate(), 0.5);
+ EXPECT_EQ(make_whitelist_blueprint(5)->estimate(), 1.0);
+}
+
class LidAllocatorPerformanceTest : public LidAllocatorTest,
public testing::WithParamInterface<bool>
{
diff --git a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
index 95e4eac437c..9e9199bc8ba 100644
--- a/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
+++ b/searchcore/src/vespa/searchcore/proton/documentmetastore/lid_allocator.cpp
@@ -222,6 +222,10 @@ public:
setEstimate(HitEstimate(_activeLids.size(), false));
}
+ double calculate_relative_estimate() const final {
+ return abs_to_rel_est(getState().estimate().estHits, get_docid_limit());
+ }
+
bool isWhiteList() const noexcept final { return true; }
SearchIterator::UP createFilterSearch(bool strict, FilterConstraint) const override {
diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
index 372b76fa9af..2d621fa1011 100644
--- a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
+++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp
@@ -22,6 +22,7 @@ class MyOr : public IntermediateBlueprint
{
private:
public:
+ double calculate_relative_estimate() const final { return 0.5; }
HitEstimate combine(const std::vector<HitEstimate> &data) const override {
return max(data);
}
@@ -639,6 +640,7 @@ getExpectedBlueprint()
" estimate: HitEstimate {\n"
" empty: false\n"
" estHits: 9\n"
+ " relative_estimate: 0.5\n"
" cost_tier: 1\n"
" tree_size: 2\n"
" allow_termwise_eval: false\n"
@@ -658,6 +660,7 @@ getExpectedBlueprint()
" estimate: HitEstimate {\n"
" empty: false\n"
" estHits: 9\n"
+ " relative_estimate: 0.5\n"
" cost_tier: 1\n"
" tree_size: 1\n"
" allow_termwise_eval: true\n"
@@ -687,6 +690,7 @@ getExpectedSlimeBlueprint() {
" '[type]': 'HitEstimate',"
" empty: false,"
" estHits: 9,"
+ " relative_estimate: 0.5,"
" cost_tier: 1,"
" tree_size: 2,"
" allow_termwise_eval: false"
@@ -711,6 +715,7 @@ getExpectedSlimeBlueprint() {
" '[type]': 'HitEstimate',"
" empty: false,"
" estHits: 9,"
+ " relative_estimate: 0.5,"
" cost_tier: 1,"
" tree_size: 1,"
" allow_termwise_eval: true"
@@ -767,9 +772,9 @@ TEST("requireThatDocIdLimitInjectionWorks")
}
TEST("Control object sizes") {
- EXPECT_EQUAL(32u, sizeof(Blueprint::State));
+ EXPECT_EQUAL(40u, sizeof(Blueprint::State));
EXPECT_EQUAL(32u, sizeof(Blueprint));
- EXPECT_EQUAL(64u, sizeof(LeafBlueprint));
+ EXPECT_EQUAL(72u, sizeof(LeafBlueprint));
}
TEST_MAIN() {
diff --git a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
index 38e7e27163d..5078672e84e 100644
--- a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
+++ b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp
@@ -157,9 +157,9 @@ TEST("test Or propagates updated histestimate") {
EXPECT_TRUE(child.executeInfo.isStrict());
}
EXPECT_EQUAL(1.0f, dynamic_cast<const RememberExecuteInfo &>(bp->getChild(0)).executeInfo.hitRate());
- EXPECT_EQUAL(1.0f, dynamic_cast<const RememberExecuteInfo &>(bp->getChild(1)).executeInfo.hitRate());
- EXPECT_EQUAL(3.0f/5.0f, dynamic_cast<const RememberExecuteInfo &>(bp->getChild(2)).executeInfo.hitRate());
- EXPECT_EQUAL(3.0f*42.0f/(5.0f*50.0f), dynamic_cast<const RememberExecuteInfo &>(bp->getChild(3)).executeInfo.hitRate());
+ EXPECT_APPROX(0.5f, dynamic_cast<const RememberExecuteInfo &>(bp->getChild(1)).executeInfo.hitRate(), 1e-6);
+ EXPECT_APPROX(0.5*3.0f/5.0f, dynamic_cast<const RememberExecuteInfo &>(bp->getChild(2)).executeInfo.hitRate(), 1e-6);
+ EXPECT_APPROX(0.5*3.0f*42.0f/(5.0f*50.0f), dynamic_cast<const RememberExecuteInfo &>(bp->getChild(3)).executeInfo.hitRate(), 1e-6);
}
TEST("test And Blueprint") {
@@ -644,7 +644,11 @@ struct make {
child->setSourceId(source_tag);
source_tag = invalid_source;
}
- making->addChild(std::move(child));
+ if (auto *weak_and = making->asWeakAnd()) {
+ weak_and->addTerm(std::move(child), 1);
+ } else {
+ making->addChild(std::move(child));
+ }
return std::move(*this);
}
make &&leaf(uint32_t estimate) && {
@@ -661,6 +665,9 @@ struct make {
static make RANK() { return make(std::make_unique<RankBlueprint>()); }
static make ANDNOT() { return make(std::make_unique<AndNotBlueprint>()); }
static make SB(ISourceSelector &selector) { return make(std::make_unique<SourceBlenderBlueprint>(selector)); }
+ static make NEAR(uint32_t window) { return make(std::make_unique<NearBlueprint>(window)); }
+ static make ONEAR(uint32_t window) { return make(std::make_unique<ONearBlueprint>(window)); }
+ static make WEAKAND(uint32_t n) { return make(std::make_unique<WeakAndBlueprint>(n)); }
};
TEST("AND AND collapsing") {
@@ -1181,4 +1188,45 @@ TEST("require that OR blueprint use saturated sum as estimate") {
TEST_DO(verify_or_est({{100, false},{300, false},{200, false}}, {300, false}));
}
+void verify_relative_estimate(make &&mk, double expect) {
+ EXPECT_EQUAL(mk.making->estimate(), 0.0);
+ Blueprint::UP bp = std::move(mk).leafs({200,300,950});
+ bp->setDocIdLimit(1000);
+ EXPECT_EQUAL(bp->estimate(), expect);
+}
+
+TEST("relative estimate for OR") {
+ verify_relative_estimate(make::OR(), 1.0-0.8*0.7*0.5);
+}
+
+TEST("relative estimate for AND") {
+ verify_relative_estimate(make::AND(), 0.2*0.3*0.5);
+}
+
+TEST("relative estimate for RANK") {
+ verify_relative_estimate(make::RANK(), 0.2);
+}
+
+TEST("relative estimate for ANDNOT") {
+ verify_relative_estimate(make::ANDNOT(), 0.2);
+}
+
+TEST("relative estimate for SB") {
+ InvalidSelector sel;
+ verify_relative_estimate(make::SB(sel), 1.0-0.8*0.7*0.5);
+}
+
+TEST("relative estimate for NEAR") {
+ verify_relative_estimate(make::NEAR(1), 0.2*0.3*0.5);
+}
+
+TEST("relative estimate for ONEAR") {
+ verify_relative_estimate(make::ONEAR(1), 0.2*0.3*0.5);
+}
+
+TEST("relative estimate for WEAKAND") {
+ verify_relative_estimate(make::WEAKAND(1000), 1.0-0.8*0.7*0.5);
+ verify_relative_estimate(make::WEAKAND(50), 0.05);
+}
+
TEST_MAIN() { TEST_DEBUG("lhs.out", "rhs.out"); TEST_RUN_ALL(); }
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 5933122d7a2..f910ff5be1b 100644
--- a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp
+++ b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp
@@ -47,6 +47,7 @@ concept ChildCollector = requires(T a, std::unique_ptr<Blueprint> bp) {
// inherit Blueprint to capture the default filter factory
struct DefaultBlueprint : Blueprint {
+ double calculate_relative_estimate() const override { abort(); }
void optimize(Blueprint* &, OptimizePass) override { abort(); }
const State &getState() const override { abort(); }
void fetchPostings(const ExecuteInfo &) override { abort(); }
diff --git a/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp b/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp
index 58f22f19da1..2a59a578ec9 100644
--- a/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp
+++ b/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp
@@ -594,6 +594,7 @@ TEST_F("require that asString() on blueprint works", BlueprintAsStringFixture)
" estimate: HitEstimate {\n"
" empty: false\n"
" estHits: 2\n"
+ " relative_estimate: 0.5\n"
" cost_tier: 1\n"
" tree_size: 2\n"
" allow_termwise_eval: false\n"
@@ -616,6 +617,7 @@ TEST_F("require that asString() on blueprint works", BlueprintAsStringFixture)
" estimate: HitEstimate {\n"
" empty: false\n"
" estHits: 2\n"
+ " relative_estimate: 0.5\n"
" cost_tier: 1\n"
" tree_size: 1\n"
" allow_termwise_eval: true\n"
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
index 1088decb8d6..96c7de928e5 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.cpp
@@ -89,6 +89,7 @@ Blueprint::sat_sum(const std::vector<HitEstimate> &data, uint32_t docid_limit)
Blueprint::State::State() noexcept
: _fields(),
+ _relative_estimate(0.0),
_estimateHits(0),
_tree_size(1),
_estimateEmpty(true),
@@ -105,6 +106,7 @@ Blueprint::State::State(FieldSpecBase field) noexcept
Blueprint::State::State(FieldSpecBaseList fields_in) noexcept
: _fields(std::move(fields_in)),
+ _relative_estimate(0.0),
_estimateHits(0),
_tree_size(1),
_estimateEmpty(true),
@@ -350,6 +352,7 @@ Blueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
visitor.openStruct("estimate", "HitEstimate");
visitor.visitBool("empty", state.estimate().empty);
visitor.visitInt("estHits", state.estimate().estHits);
+ visitor.visitFloat("relative_estimate", state.relative_estimate());
visitor.visitInt("cost_tier", state.cost_tier());
visitor.visitInt("tree_size", state.tree_size());
visitor.visitBool("allow_termwise_eval", state.allow_termwise_eval());
@@ -373,8 +376,10 @@ StateCache::updateState() const
void
StateCache::notifyChange() {
assert(!frozen());
- Blueprint::notifyChange();
- _stale = true;
+ if (!_stale) {
+ Blueprint::notifyChange();
+ _stale = true;
+ }
}
} // namespace blueprint
@@ -386,9 +391,11 @@ IntermediateBlueprint::~IntermediateBlueprint() = default;
void
IntermediateBlueprint::setDocIdLimit(uint32_t limit) noexcept
{
- Blueprint::setDocIdLimit(limit);
- for (Blueprint::UP &child : _children) {
- child->setDocIdLimit(limit);
+ if (limit != get_docid_limit()) {
+ Blueprint::setDocIdLimit(limit);
+ for (Blueprint::UP &child : _children) {
+ child->setDocIdLimit(limit);
+ }
}
}
@@ -511,6 +518,7 @@ IntermediateBlueprint::calculateState() const
{
State state(exposeFields());
state.estimate(calculateEstimate());
+ state.relative_estimate(calculate_relative_estimate());
state.cost_tier(calculate_cost_tier());
state.allow_termwise_eval(infer_allow_termwise_eval());
state.want_global_filter(infer_want_global_filter());
@@ -703,6 +711,27 @@ IntermediateBlueprint::calculateUnpackInfo(const fef::MatchData & md) const
//-----------------------------------------------------------------------------
void
+LeafBlueprint::setDocIdLimit(uint32_t limit) noexcept {
+ if (limit != get_docid_limit()) {
+ Blueprint::setDocIdLimit(limit);
+ _state.relative_estimate(calculate_relative_estimate());
+ notifyChange();
+ }
+}
+
+double
+LeafBlueprint::calculate_relative_estimate() const
+{
+ double rel_est = abs_to_rel_est(_state.estimate().estHits, get_docid_limit());
+ if (rel_est > 0.9) {
+ // Assume we do not really know how much we are matching when
+ // we claim to match 'everything'
+ return 0.5;
+ }
+ return rel_est;
+}
+
+void
LeafBlueprint::fetchPostings(const ExecuteInfo &)
{
}
diff --git a/searchlib/src/vespa/searchlib/queryeval/blueprint.h b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
index a61d435ac25..3261d380f36 100644
--- a/searchlib/src/vespa/searchlib/queryeval/blueprint.h
+++ b/searchlib/src/vespa/searchlib/queryeval/blueprint.h
@@ -29,6 +29,7 @@ class MatchingElementsSearch;
class LeafBlueprint;
class IntermediateBlueprint;
class SourceBlenderBlueprint;
+class WeakAndBlueprint;
class AndBlueprint;
class AndNotBlueprint;
class OrBlueprint;
@@ -74,6 +75,7 @@ public:
{
private:
FieldSpecBaseList _fields;
+ double _relative_estimate;
uint32_t _estimateHits;
uint32_t _tree_size : 20;
bool _estimateEmpty : 1;
@@ -109,6 +111,9 @@ public:
return nullptr;
}
+ void relative_estimate(double value) noexcept { _relative_estimate = value; }
+ double relative_estimate() const noexcept { return _relative_estimate; }
+
void estimate(HitEstimate est) noexcept {
_estimateHits = est.estHits;
_estimateEmpty = est.empty;
@@ -117,10 +122,9 @@ public:
HitEstimate estimate() const noexcept { return {_estimateHits, _estimateEmpty}; }
double hit_ratio(uint32_t docid_limit) const noexcept {
- uint32_t total_hits = _estimateHits;
- uint32_t total_docs = std::max(total_hits, docid_limit);
- return (total_docs == 0) ? 0.0 : double(total_hits) / double(total_docs);
+ return abs_to_rel_est(_estimateHits, docid_limit);
}
+
void tree_size(uint32_t value) noexcept {
assert(value < 0x100000);
_tree_size = value;
@@ -133,6 +137,12 @@ public:
void cost_tier(uint8_t value) noexcept { _cost_tier = value; }
uint8_t cost_tier() const noexcept { return _cost_tier; }
};
+
+ // converts from an absolute to a relative estimate
+ static double abs_to_rel_est(uint32_t est, uint32_t docid_limit) noexcept {
+ uint32_t total_docs = std::max(est, docid_limit);
+ return (total_docs == 0) ? 0.0 : double(est) / double(total_docs);
+ }
// utility that just takes maximum estimate
static HitEstimate max(const std::vector<HitEstimate> &data);
@@ -238,9 +248,9 @@ public:
virtual const State &getState() const = 0;
const Blueprint &root() const;
- double hit_ratio() const noexcept { return getState().hit_ratio(_docid_limit); }
- // TODO Call getState().estimate() when it return a normalized estimate
- double estimate() const noexcept { return getState().hit_ratio(_docid_limit); }
+ double hit_ratio() const { return getState().hit_ratio(_docid_limit); }
+ double estimate() const { return getState().relative_estimate(); }
+ virtual double calculate_relative_estimate() const = 0;
virtual void fetchPostings(const ExecuteInfo &execInfo) = 0;
virtual void freeze() = 0;
@@ -272,6 +282,7 @@ public:
bool isAndNot() const noexcept { return const_cast<Blueprint *>(this)->asAndNot() != nullptr; }
virtual OrBlueprint * asOr() noexcept { return nullptr; }
virtual SourceBlenderBlueprint * asSourceBlender() noexcept { return nullptr; }
+ virtual WeakAndBlueprint * asWeakAnd() noexcept { return nullptr; }
virtual bool isRank() const noexcept { return false; }
virtual const attribute::ISearchContext *get_attribute_search_context() const noexcept { return nullptr; }
@@ -357,7 +368,7 @@ public:
Blueprint::UP removeChild(size_t n);
Blueprint::UP removeLastChild() { return removeChild(childCnt() - 1); }
SearchIteratorUP createSearch(fef::MatchData &md, bool strict) const override;
-
+
virtual HitEstimate combine(const std::vector<HitEstimate> &data) const = 0;
virtual FieldSpecBaseList exposeFields() const = 0;
virtual void sort(Children &children) const = 0;
@@ -383,6 +394,7 @@ protected:
void optimize(Blueprint* &self, OptimizePass pass) final;
void setEstimate(HitEstimate est) {
_state.estimate(est);
+ _state.relative_estimate(calculate_relative_estimate());
notifyChange();
}
void set_cost_tier(uint32_t value);
@@ -413,7 +425,8 @@ protected:
public:
~LeafBlueprint() override = default;
const State &getState() const final { return _state; }
- void setDocIdLimit(uint32_t limit) noexcept final { Blueprint::setDocIdLimit(limit); }
+ void setDocIdLimit(uint32_t limit) noexcept final;
+ double calculate_relative_estimate() const override;
void fetchPostings(const ExecuteInfo &execInfo) override;
void freeze() final;
SearchIteratorUP createSearch(fef::MatchData &md, bool strict) const override;
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
index c4044ba3d00..7d992b510b5 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.cpp
@@ -81,10 +81,35 @@ need_normal_features_for_children(const IntermediateBlueprint &blueprint, fef::M
}
}
+double rel_est_first_child(const Blueprint::Children &children) {
+ return children.empty() ? 0.0 : children[0]->getState().relative_estimate();
+}
+
+double rel_est_and(const Blueprint::Children &children) {
+ double flow = 1.0;
+ for (const Blueprint::UP &child: children) {
+ flow *= child->getState().relative_estimate();
+ }
+ return children.empty() ? 0.0 : flow;
+}
+
+double rel_est_or(const Blueprint::Children &children) {
+ double flow = 1.0;
+ for (const Blueprint::UP &child: children) {
+ flow *= (1.0 - child->getState().relative_estimate());
+ }
+ return (1.0 - flow);
+}
+
} // namespace search::queryeval::<unnamed>
//-----------------------------------------------------------------------------
+double
+AndNotBlueprint::calculate_relative_estimate() const {
+ return rel_est_first_child(get_children());
+}
+
Blueprint::HitEstimate
AndNotBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -188,6 +213,11 @@ AndNotBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) co
//-----------------------------------------------------------------------------
+double
+AndBlueprint::calculate_relative_estimate() const {
+ return rel_est_and(get_children());
+}
+
Blueprint::HitEstimate
AndBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -282,7 +312,7 @@ OrBlueprint::computeNextHitRate(const Blueprint & child, double hit_rate, bool u
constexpr double MIN_INVERSE_HIT_RATIO = 0.10;
double estimate = use_estimate ? child.estimate() : child.hit_ratio();
double inverse_child_estimate = 1.0 - estimate;
- return (inverse_child_estimate > MIN_INVERSE_HIT_RATIO)
+ return (use_estimate || (inverse_child_estimate > MIN_INVERSE_HIT_RATIO))
? hit_rate * inverse_child_estimate
: hit_rate;
}
@@ -291,6 +321,11 @@ OrBlueprint::computeNextHitRate(const Blueprint & child, double hit_rate, bool u
OrBlueprint::~OrBlueprint() = default;
+double
+OrBlueprint::calculate_relative_estimate() const {
+ return rel_est_or(get_children());
+}
+
Blueprint::HitEstimate
OrBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -382,6 +417,13 @@ OrBlueprint::calculate_cost_tier() const
//-----------------------------------------------------------------------------
WeakAndBlueprint::~WeakAndBlueprint() = default;
+double
+WeakAndBlueprint::calculate_relative_estimate() const {
+ double child_est = rel_est_or(get_children());
+ double my_est = abs_to_rel_est(_n, get_docid_limit());
+ return std::min(my_est, child_est);
+}
+
Blueprint::HitEstimate
WeakAndBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -441,6 +483,11 @@ WeakAndBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) c
//-----------------------------------------------------------------------------
+double
+NearBlueprint::calculate_relative_estimate() const {
+ return rel_est_and(get_children());
+}
+
Blueprint::HitEstimate
NearBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -494,6 +541,11 @@ NearBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) cons
//-----------------------------------------------------------------------------
+double
+ONearBlueprint::calculate_relative_estimate() const {
+ return rel_est_and(get_children());
+}
+
Blueprint::HitEstimate
ONearBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -550,6 +602,11 @@ ONearBlueprint::createFilterSearch(bool strict, FilterConstraint constraint) con
//-----------------------------------------------------------------------------
+double
+RankBlueprint::calculate_relative_estimate() const {
+ return rel_est_first_child(get_children());
+}
+
Blueprint::HitEstimate
RankBlueprint::combine(const std::vector<HitEstimate> &data) const
{
@@ -642,6 +699,11 @@ SourceBlenderBlueprint::SourceBlenderBlueprint(const ISourceSelector &selector)
SourceBlenderBlueprint::~SourceBlenderBlueprint() = default;
+double
+SourceBlenderBlueprint::calculate_relative_estimate() const {
+ return rel_est_or(get_children());
+}
+
Blueprint::HitEstimate
SourceBlenderBlueprint::combine(const std::vector<HitEstimate> &data) const
{
diff --git a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
index 1d88b3b21eb..b9306ea307d 100644
--- a/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
+++ b/searchlib/src/vespa/searchlib/queryeval/intermediate_blueprints.h
@@ -15,6 +15,7 @@ class AndNotBlueprint : public IntermediateBlueprint
{
public:
bool supports_termwise_children() const override { return true; }
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void optimize_self(OptimizePass pass) override;
@@ -41,6 +42,7 @@ class AndBlueprint : public IntermediateBlueprint
{
public:
bool supports_termwise_children() const override { return true; }
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void optimize_self(OptimizePass pass) override;
@@ -65,6 +67,7 @@ class OrBlueprint : public IntermediateBlueprint
public:
~OrBlueprint() override;
bool supports_termwise_children() const override { return true; }
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void optimize_self(OptimizePass pass) override;
@@ -91,11 +94,13 @@ private:
std::vector<uint32_t> _weights;
public:
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void sort(Children &children) const override;
bool inheritStrict(size_t i) const override;
bool always_needs_unpack() const override;
+ WeakAndBlueprint * asWeakAnd() noexcept final { return this; }
SearchIterator::UP
createIntermediateSearch(MultiSearch::Children subSearches,
bool strict, fef::MatchData &md) const override;
@@ -119,6 +124,7 @@ private:
uint32_t _window;
public:
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
bool should_optimize_children() const override { return false; }
@@ -141,6 +147,7 @@ private:
uint32_t _window;
public:
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
bool should_optimize_children() const override { return false; }
@@ -160,6 +167,7 @@ public:
class RankBlueprint final : public IntermediateBlueprint
{
public:
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void optimize_self(OptimizePass pass) override;
@@ -187,6 +195,7 @@ private:
public:
explicit SourceBlenderBlueprint(const ISourceSelector &selector) noexcept;
~SourceBlenderBlueprint() override;
+ double calculate_relative_estimate() const final;
HitEstimate combine(const std::vector<HitEstimate> &data) const override;
FieldSpecBaseList exposeFields() const override;
void sort(Children &children) const override;