// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once #include "attribute_object_visitor.h" #include "i_direct_posting_store.h" #include #include #include #include #include #include #include #include namespace search::queryeval { class SearchIterator; } namespace search::attribute { /** * Blueprint used for multi-term query operators as InTerm, WeightedSetTerm or DotProduct * over an attribute which supports the IDocidPostingStore or IDocidWithWeightPostingStore interface. * * This uses access to low-level posting lists, which speeds up query execution. */ template class DirectMultiTermBlueprint : public queryeval::ComplexLeafBlueprint { private: std::vector _weights; std::vector _terms; const IAttributeVector &_iattr; const PostingStoreType &_attr; vespalib::datastore::EntryRef _dictionary_snapshot; using IteratorType = typename PostingStoreType::IteratorType; using IteratorWeights = std::variant>, std::vector>; bool use_hash_filter(bool strict) const; IteratorWeights create_iterators(std::vector& btree_iterators, std::vector>& bitvectors, bool use_bitvector_when_available, fef::TermFieldMatchData& tfmd, bool strict) const; std::unique_ptr combine_iterators(std::unique_ptr multi_term_iterator, std::vector>&& bitvectors, bool strict) const; template std::unique_ptr create_search_helper(const fef::TermFieldMatchDataArray& tfmda, bool strict) const; public: DirectMultiTermBlueprint(const queryeval::FieldSpec &field, const IAttributeVector &iattr, const PostingStoreType &attr, size_t size_hint); ~DirectMultiTermBlueprint() override; void addTerm(const IDirectPostingStore::LookupKey & key, int32_t weight, HitEstimate & estimate) { IDirectPostingStore::LookupResult result = _attr.lookup(key, _dictionary_snapshot); HitEstimate childEst(result.posting_size, (result.posting_size == 0)); if (!childEst.empty) { if (estimate.empty) { estimate = childEst; } else { estimate.estHits += childEst.estHits; } _weights.push_back(weight); _terms.push_back(result); } } void complete(HitEstimate estimate) { setEstimate(estimate); } void sort(queryeval::InFlow in_flow) override { resolve_strict(in_flow); } queryeval::FlowStats calculate_flow_stats(uint32_t docid_limit) const override; std::unique_ptr createLeafSearch(const fef::TermFieldMatchDataArray &tfmda) const override; std::unique_ptr createFilterSearch(FilterConstraint constraint) const override; std::unique_ptr create_matching_elements_search(const MatchingElementsFields &fields) const override { if (fields.has_field(_iattr.getName())) { return queryeval::MatchingElementsSearch::create(_iattr, _dictionary_snapshot, vespalib::ConstArrayRef(_terms)); } else { return {}; } } void visitMembers(vespalib::ObjectVisitor& visitor) const override { LeafBlueprint::visitMembers(visitor); visit_attribute(visitor, _iattr); } }; }