aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib/src/vespa/searchlib/queryeval/equiv_blueprint.cpp
blob: 61ae7b51de1ad5c65fcdfeda90ee44a23e686882 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "equiv_blueprint.h"
#include "equivsearch.h"
#include "field_spec.hpp"
#include "flow_tuning.h"
#include <vespa/vespalib/objects/visit.hpp>
#include <vespa/vespalib/stllike/hash_map.hpp>

namespace search::queryeval {

namespace {

class UnpackNeed
{
    bool _needs_normal_features;
    bool _needs_interleaved_features;
public:
    UnpackNeed()
        : _needs_normal_features(false),
          _needs_interleaved_features(false)
    {
    }

    void observe(const fef::TermFieldMatchData &output)
    {
        if (output.needs_normal_features()) {
            _needs_normal_features = true;
        }
        if (output.needs_interleaved_features()) {
            _needs_interleaved_features = true;
        }
    }

    void notify(fef::TermFieldMatchData &input) const
    {
        input.setNeedNormalFeatures(_needs_normal_features);
        input.setNeedInterleavedFeatures(_needs_interleaved_features);
    }
};

};

EquivBlueprint::EquivBlueprint(FieldSpecBaseList fields,
                               fef::MatchDataLayout subtree_mdl)
    : ComplexLeafBlueprint(std::move(fields)),
      _estimate(),
      _layout(std::move(subtree_mdl)),
      _terms(),
      _exactness()
{
}

EquivBlueprint::~EquivBlueprint() = default;

void
EquivBlueprint::sort(InFlow in_flow)
{
    strict(in_flow.strict());
    auto flow = OrFlow(in_flow);
    for (auto &term: _terms) {
        term->sort(InFlow(flow.strict(), flow.flow()));
        flow.add(term->estimate());
    }
}

FlowStats
EquivBlueprint::calculate_flow_stats(uint32_t docid_limit) const
{
    for (auto &term: _terms) {
        term->update_flow_stats(docid_limit);
    }
    double est = OrFlow::estimate_of(_terms);
    return {est, OrFlow::cost_of(_terms, false),
            OrFlow::cost_of(_terms, true) + flow::array_cost(est, _terms.size())};
}

SearchIterator::UP
EquivBlueprint::createLeafSearch(const fef::TermFieldMatchDataArray &outputs) const
{
    fef::MatchData::UP md = _layout.createMatchData();
    MultiSearch::Children children;
    children.reserve(_terms.size());
    fef::TermMatchDataMerger::Inputs childMatch;
    vespalib::hash_map<uint16_t, UnpackNeed> unpack_needs(outputs.size());
    for (size_t i = 0; i < outputs.size(); ++i) {
        unpack_needs[outputs[i]->getFieldId()].observe(*outputs[i]);
    }
    for (size_t i = 0; i < _terms.size(); ++i) {
        const State &childState = _terms[i]->getState();
        for (size_t j = 0; j < childState.numFields(); ++j) {
            auto *child_term_field_match_data = childState.field(j).resolve(*md);
            unpack_needs[child_term_field_match_data->getFieldId()].notify(*child_term_field_match_data);
            childMatch.emplace_back(child_term_field_match_data, _exactness[i]);
        }
        children.push_back(_terms[i]->createSearch(*md));
    }
    return EquivSearch::create(std::move(children), std::move(md), childMatch, outputs, strict());
}

SearchIterator::UP
EquivBlueprint::createFilterSearch(FilterConstraint constraint) const
{
    return create_or_filter(_terms, strict(), constraint);
}

void
EquivBlueprint::visitMembers(vespalib::ObjectVisitor &visitor) const
{
    LeafBlueprint::visitMembers(visitor);
    visit(visitor, "terms", _terms);
}

void
EquivBlueprint::fetchPostings(const ExecuteInfo &execInfo)
{
    for (size_t i = 0; i < _terms.size(); ++i) {
        _terms[i]->fetchPostings(execInfo);
    }
}

EquivBlueprint&
EquivBlueprint::addTerm(Blueprint::UP term, double exactness)
{
    const State &childState = term->getState();

    HitEstimate childEst = childState.estimate();
    if (_terms.empty() || _estimate < childEst  ) {
        _estimate = childEst;
    }
    setEstimate(_estimate);
    _terms.push_back(std::move(term));
    _exactness.push_back(exactness);
    return *this;
}

}