aboutsummaryrefslogtreecommitdiffstats
path: root/searchlib/src/vespa/searchlib/queryeval/andsearchstrict.h
blob: 3205193bc03007d4fc3d0645335e414fd2e759b0 (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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include "andsearchnostrict.h"

namespace search::queryeval {

/**
 * A simple strict implementation of the And search operation.
 **/
template <typename Unpack>
class AndSearchStrict : public AndSearchNoStrict<Unpack>
{
private:
    template<bool doSeekOnly>
    VESPA_DLL_LOCAL void advance(uint32_t failedChildIndexd) __attribute__((noinline));
    using Trinary=vespalib::Trinary;
protected:
    void doSeek(uint32_t docid) override;
    Trinary is_strict() const override { return Trinary::True; }
    SearchIterator::UP andWith(SearchIterator::UP filter, uint32_t estimate) override;
public:
    AndSearchStrict(MultiSearch::Children children, const Unpack & unpacker)
        : AndSearchNoStrict<Unpack>(std::move(children), unpacker)
    {
    }

    void initRange(uint32_t beginid, uint32_t endid) override {
        AndSearchNoStrict<Unpack>::initRange(beginid, endid);
        advance<false>(0);
    }
};

template<typename Unpack>
template<bool doSeekOnly>
void
AndSearchStrict<Unpack>::advance(uint32_t failedChildIndex)
{
    const MultiSearch::Children & children(this->getChildren());
    SearchIterator & firstChild(*children[0]);
    bool foundHit(false);
    if (failedChildIndex != 0) {
        if (doSeekOnly) {
            if (__builtin_expect(children[failedChildIndex]->isAtEnd(), false)) {
                this->setAtEnd();
                return;
            }
            firstChild.doSeek(std::max(firstChild.getDocId() + 1, children[failedChildIndex]->getDocId()));
        } else {
            firstChild.seek(std::max(firstChild.getDocId() + 1, children[failedChildIndex]->getDocId()));
        }
    }
    uint32_t nextId(firstChild.getDocId());
    while (!foundHit && !this->isAtEnd(nextId)) {
        foundHit = true;
        for (uint32_t i(1); foundHit && (i < children.size()); ++i) {
            SearchIterator & child(*children[i]);
            if (!(foundHit = child.seek(nextId))) {
                if (__builtin_expect(!child.isAtEnd(), true)) {
                    firstChild.doSeek(std::max(nextId+1, child.getDocId()));
                    nextId = firstChild.getDocId();
                } else {
                    this->setAtEnd();
                    return;
                }
            }
        }
    }
    this->setDocId(nextId);
}

template<typename Unpack>
void
AndSearchStrict<Unpack>::doSeek(uint32_t docid)
{
    const MultiSearch::Children & children(this->getChildren());
    for (uint32_t i(0); i < children.size(); ++i) {
        children[i]->doSeek(docid);
        if (children[i]->getDocId() != docid) {
            advance<true>(i);
            return;
        }
    }
    this->setDocId(docid);
}

template<typename Unpack>
SearchIterator::UP
AndSearchStrict<Unpack>::andWith(SearchIterator::UP filter, uint32_t estimate_)
{
    filter = this->getChildren()[0]->andWith(std::move(filter), estimate_);
    if (filter) {
        if ((estimate_ < this->estimate()) && (filter->is_strict() == Trinary::True)) {
            this->insert(0, std::move(filter));
        } else {
            filter = this->offerFilterToChildren(std::move(filter), estimate_);
            if (filter) {
                this->insert(1, std::move(filter));
            }
        }
    }
    return filter; // Should always be empty, returning it incase logic changes.
}

}