// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include "multi_term_or_filter_search.h" #include "posting_iterator_pack.h" #include #include #include #include using search::queryeval::SearchIteratorPack; namespace search::attribute { template class MultiTermOrFilterSearchImpl : public MultiTermOrFilterSearch { // Vector that caches the docids of the current positions of the iterators in the pack. // This reduces cache misses in doSeek() and seek_all(). std::vector _docids; IteratorPack _children; fef::TermFieldMatchData* _tfmd; void seek_all(uint32_t docId); public: explicit MultiTermOrFilterSearchImpl(IteratorPack&& children, fef::TermFieldMatchData* tfmd); ~MultiTermOrFilterSearchImpl() override; void doSeek(uint32_t docId) override; void doUnpack(uint32_t docid) override { if (_tfmd != nullptr) { _tfmd->resetOnlyDocId(docid); } } void initRange(uint32_t begin, uint32_t end) override { SearchIterator::initRange(begin, end); _children.initRange(begin, end); for (uint16_t i = 0; i < _children.size(); ++i) { _docids[i] = _children.get_docid(i); } } void or_hits_into(BitVector &result, uint32_t begin_id) override { return _children.or_hits_into(result, begin_id); } void and_hits_into(BitVector &result, uint32_t begin_id) override { return result.andWith(*get_hits(begin_id)); } std::unique_ptr get_hits(uint32_t begin_id) override { seek_all(getDocId()); return _children.get_hits(begin_id, getEndId()); } Trinary is_strict() const override { return Trinary::True; } }; template MultiTermOrFilterSearchImpl::MultiTermOrFilterSearchImpl(IteratorPack&& children, fef::TermFieldMatchData* tfmd) : MultiTermOrFilterSearch(), _docids(children.size(), 0), _children(std::move(children)), _tfmd(tfmd) { } template MultiTermOrFilterSearchImpl::~MultiTermOrFilterSearchImpl() = default; template void MultiTermOrFilterSearchImpl::seek_all(uint32_t docId) { for (uint16_t i = 0; i < _children.size(); ++i) { uint32_t next = _docids[i]; if (next < docId) { next = _children.seek(i, docId); _docids[i] = next; } } } template void MultiTermOrFilterSearchImpl::doSeek(uint32_t docId) { uint32_t min_doc_id = endDocId; for (uint16_t i = 0; i < _children.size(); ++i) { uint32_t next = _docids[i]; if (next < docId) { next = _children.seek(i, docId); _docids[i] = next; } if (next == docId) { setDocId(next); return; } min_doc_id = std::min(min_doc_id, next); } setDocId(min_doc_id); } namespace { template std::unique_ptr create_helper(std::vector&& children, fef::TermFieldMatchData* tfmd) { if (children.empty()) { return std::make_unique(); } else { std::sort(children.begin(), children.end(), [](const auto & a, const auto & b) { return a.size() > b.size(); }); using OrFilter = MultiTermOrFilterSearchImpl; return std::make_unique(IteratorPackType(std::move(children)), tfmd); } } } std::unique_ptr MultiTermOrFilterSearch::create(std::vector&& children) { return create_helper(std::move(children), nullptr); } std::unique_ptr MultiTermOrFilterSearch::create(std::vector&& children, fef::TermFieldMatchData& tfmd) { return create_helper(std::move(children), &tfmd); } std::unique_ptr MultiTermOrFilterSearch::create(std::vector&& children) { return create_helper(std::move(children), nullptr); } std::unique_ptr MultiTermOrFilterSearch::create(std::vector&& children, fef::TermFieldMatchData& tfmd) { return create_helper(std::move(children), &tfmd); } std::unique_ptr MultiTermOrFilterSearch::create(const std::vector& children, std::unique_ptr md) { if (children.empty()) { return std::make_unique(); } else { using OrFilter = MultiTermOrFilterSearchImpl; return std::make_unique(SearchIteratorPack(children, std::move(md)), nullptr); } } }