// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once #include "attributeiterators.h" #include #include #include #include #include namespace search { namespace { template bool matches(const SC & sc, uint32_t doc) { return sc.find(doc, 0) >= 0; } } template void AttributeIteratorBase::and_hits_into(const SC & sc, BitVector & result, uint32_t begin_id) const { result.foreach_truebit([&](uint32_t key) { if ( ! matches(sc, key)) { result.clearBit(key); }}, begin_id); result.invalidateCachedCount(); } template void AttributeIteratorBase::or_hits_into(const SC & sc, BitVector & result, uint32_t begin_id) const { result.foreach_falsebit([&](uint32_t key) { if ( matches(sc, key)) { result.setBit(key); }}, begin_id); result.invalidateCachedCount(); } template std::unique_ptr AttributeIteratorBase::get_hits(const SC & sc, uint32_t begin_id) const { BitVector::UP result = BitVector::create(begin_id, getEndId()); for (uint32_t docId(std::max(begin_id, getDocId())); docId < getEndId(); docId++) { if (matches(sc, docId)) { result->setBit(docId); } } result->invalidateCachedCount(); return result; } template template AttributePostingListIteratorT:: AttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx, bool hasWeight, fef::TermFieldMatchData *matchData, Args &&... args) : AttributePostingListIterator(baseSearchCtx, hasWeight, matchData), _iterator(std::forward(args)...), _postingInfo(1, 1), _postingInfoValid(false) { setupPostingInfo(); } template void AttributePostingListIteratorT::initRange(uint32_t begin, uint32_t end) { AttributePostingListIterator::initRange(begin, end); _iterator.lower_bound(begin); if (!_iterator.valid() || isAtEnd(_iterator.getKey())) { setAtEnd(); } else { setDocId(_iterator.getKey()); } } template template FilterAttributePostingListIteratorT:: FilterAttributePostingListIteratorT(const attribute::ISearchContext &baseSearchCtx, fef::TermFieldMatchData *matchData, Args &&... args) : FilterAttributePostingListIterator(baseSearchCtx, matchData), _iterator(std::forward(args)...), _postingInfo(1, 1), _postingInfoValid(false) { setupPostingInfo(); _matchPosition->setElementWeight(1); } template void FilterAttributePostingListIteratorT::initRange(uint32_t begin, uint32_t end) { FilterAttributePostingListIterator::initRange(begin, end); _iterator.lower_bound(begin); if (!_iterator.valid() || isAtEnd(_iterator.getKey())) { setAtEnd(); } else { setDocId(_iterator.getKey()); } } template void AttributePostingListIteratorT::doSeek(uint32_t docId) { _iterator.linearSeek(docId); if (_iterator.valid()) { setDocId(_iterator.getKey()); } else { setAtEnd(); } } namespace { template struct is_tree_iterator; template struct is_tree_iterator> { static constexpr bool value = false; }; template struct is_tree_iterator> { static constexpr bool value = false; }; template struct is_tree_iterator> { static constexpr bool value = true; }; template inline constexpr bool is_tree_iterator_v = is_tree_iterator::value; template void get_hits_helper(BitVector& result, PL& iterator, uint32_t end_id) { auto end_itr = iterator; if (end_itr.valid() && end_itr.getKey() < end_id) { end_itr.seek(end_id); } iterator.foreach_key_range(end_itr, [&](uint32_t key) { result.setBit(key); }); iterator = end_itr; } template void or_hits_helper(BitVector& result, PL& iterator, uint32_t end_id) { auto end_itr = iterator; if (end_itr.valid() && end_itr.getKey() < end_id) { end_itr.seek(end_id); } iterator.foreach_key_range(end_itr, [&](uint32_t key) { if (!result.testBit(key)) { result.setBit(key); } }); iterator = end_itr; } } template std::unique_ptr AttributePostingListIteratorT::get_hits(uint32_t begin_id) { BitVector::UP result(BitVector::create(begin_id, getEndId())); if constexpr (is_tree_iterator_v) { get_hits_helper(*result, _iterator, getEndId()); } else { for (; _iterator.valid() && _iterator.getKey() < getEndId(); ++_iterator) { result->setBit(_iterator.getKey()); } } result->invalidateCachedCount(); return result; } template void AttributePostingListIteratorT::or_hits_into(BitVector & result, uint32_t begin_id) { (void) begin_id; if constexpr (is_tree_iterator_v) { or_hits_helper(result, _iterator, getEndId()); } else { for (; _iterator.valid() && _iterator.getKey() < getEndId(); ++_iterator) { if ( ! result.testBit(_iterator.getKey()) ) { result.setBit(_iterator.getKey()); } } } result.invalidateCachedCount(); } template void AttributePostingListIteratorT::and_hits_into(BitVector &result, uint32_t begin_id) { result.andWith(*get_hits(begin_id)); } template std::unique_ptr FilterAttributePostingListIteratorT::get_hits(uint32_t begin_id) { BitVector::UP result(BitVector::create(begin_id, getEndId())); if constexpr (is_tree_iterator_v) { get_hits_helper(*result, _iterator, getEndId()); } else { for (; _iterator.valid() && _iterator.getKey() < getEndId(); ++_iterator) { result->setBit(_iterator.getKey()); } } result->invalidateCachedCount(); return result; } template void FilterAttributePostingListIteratorT::or_hits_into(BitVector & result, uint32_t begin_id) { (void) begin_id; if constexpr (is_tree_iterator_v) { or_hits_helper(result, _iterator, getEndId()); } else { for (; _iterator.valid() && _iterator.getKey() < getEndId(); ++_iterator) { if ( ! result.testBit(_iterator.getKey()) ) { result.setBit(_iterator.getKey()); } } } result.invalidateCachedCount(); } template void FilterAttributePostingListIteratorT::and_hits_into(BitVector &result, uint32_t begin_id) { result.andWith(*get_hits(begin_id)); } template void FilterAttributePostingListIteratorT::doSeek(uint32_t docId) { _iterator.linearSeek(docId); if (_iterator.valid()) { setDocId(_iterator.getKey()); } else { setAtEnd(); } } template void AttributePostingListIteratorT::doUnpack(uint32_t docId) { _matchData->resetOnlyDocId(docId); if (_hasWeight) { _matchPosition->setElementWeight(getWeight()); } else { uint32_t numOccs(0); for(; _iterator.valid() && (_iterator.getKey() == docId); numOccs += getWeight(), ++_iterator); _matchPosition->setElementWeight(numOccs); } } template void FilterAttributePostingListIteratorT::doUnpack(uint32_t docId) { _matchData->resetOnlyDocId(docId); } template void AttributeIteratorT::visitMembers(vespalib::ObjectVisitor &visitor) const { AttributeIterator::visitMembers(visitor); visit(visitor, "searchcontext.attribute", _concreteSearchCtx.attributeName()); visit(visitor, "searchcontext.queryterm", _concreteSearchCtx.queryTerm()); } template void FilterAttributeIteratorT::visitMembers(vespalib::ObjectVisitor &visitor) const { FilterAttributeIterator::visitMembers(visitor); visit(visitor, "searchcontext.attribute", _concreteSearchCtx.attributeName()); visit(visitor, "searchcontext.queryterm", _concreteSearchCtx.queryTerm()); } template AttributeIteratorT::AttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData) : AttributeIterator(concreteSearchCtx, matchData), _concreteSearchCtx(concreteSearchCtx) { } template FilterAttributeIteratorT::FilterAttributeIteratorT(const SC &concreteSearchCtx, fef::TermFieldMatchData *matchData) : FilterAttributeIterator(concreteSearchCtx, matchData), _concreteSearchCtx(concreteSearchCtx) { } template void FlagAttributeIteratorStrict::doSeek(uint32_t docId) { const SC & sc(_concreteSearchCtx); for (int i = sc._low; (i <= sc._high); ++i) { const BitVector * bv = sc.get_bit_vector(i); if ((bv != nullptr) && !isAtEnd(docId) && bv->testBit(docId)) { setDocId(docId); return; } } uint32_t minNextBit(search::endDocId); for (int i = sc._low; (i <= sc._high); ++i) { const BitVector * bv = sc.get_bit_vector(i); if (bv != nullptr && !isAtEnd(docId)) { uint32_t nextBit = bv->getNextTrueBit(docId); minNextBit = std::min(nextBit, minNextBit); } } if (!isAtEnd(minNextBit)) { setDocId(minNextBit); } else { setAtEnd(); } } template void FlagAttributeIteratorT::doSeek(uint32_t docId) { const SC & sc(_concreteSearchCtx); for (int i = sc._low; (i <= sc._high); ++i) { const BitVector * bv = sc.get_bit_vector(i); if ((bv != nullptr) && !isAtEnd(docId) && bv->testBit(docId)) { setDocId(docId); return; } } } template void FlagAttributeIteratorT::or_hits_into(BitVector &result, uint32_t begin_id) { (void) begin_id; const SC & sc(_concreteSearchCtx); for (int i = sc._low; (i <= sc._high); ++i) { const BitVector * bv = sc.get_bit_vector(i); if (bv != nullptr) { result.orWith(*bv); } } } template void FlagAttributeIteratorT::and_hits_into(BitVector &result, uint32_t begin_id) { const SC & sc(_concreteSearchCtx); if (sc._low == sc._high) { const BitVector * bv = sc.get_bit_vector(sc._low); if (bv != nullptr) { result.andWith(*bv); } else { // I would expect us never to end up in this case as we are probably // replaced by an EmptySearch, but I keep the code here to be functionally complete. result.clear(); } } else { SearchIterator::and_hits_into(result, begin_id); } } template std::unique_ptr FlagAttributeIteratorT::get_hits(uint32_t begin_id) { const SC & sc(_concreteSearchCtx); int i = sc._low; BitVector::UP result; for (;!result && i < sc._high; ++i) { const BitVector * bv = sc.get_bit_vector(i); if (bv != nullptr) { result = BitVector::create(*bv, begin_id, getEndId()); } } for (; i <= sc._high; ++i) { const BitVector * bv = sc.get_bit_vector(i); if (bv != nullptr) { result->orWith(*bv); } } if (!result) { result = BitVector::create(begin_id, getEndId()); } else if (begin_id < getDocId()) { result->clearInterval(begin_id, std::min(getDocId(), getEndId())); } return result; } template void AttributeIteratorT::doSeek(uint32_t docId) { if (isAtEnd(docId)) { setAtEnd(); } else if (matches(docId, _weight)) { setDocId(docId); } } template void FilterAttributeIteratorT::doSeek(uint32_t docId) { if (isAtEnd(docId)) { setAtEnd(); } else if (matches(docId)) { setDocId(docId); } } template void AttributeIteratorStrict::doSeek(uint32_t docId) { for (uint32_t nextId = docId; !isAtEnd(nextId); ++nextId) { if (this->matches(nextId, _weight)) { setDocId(nextId); return; } } setAtEnd(); } template void FilterAttributeIteratorStrict::doSeek(uint32_t docId) { for (uint32_t nextId = docId; !isAtEnd(nextId); ++nextId) { if (this->matches(nextId)) { setDocId(nextId); return; } } setAtEnd(); } template void AttributeIteratorT::or_hits_into(BitVector & result, uint32_t begin_id) { AttributeIteratorBase::or_hits_into(_concreteSearchCtx, result, begin_id); } template void FilterAttributeIteratorT::or_hits_into(BitVector & result, uint32_t begin_id) { AttributeIteratorBase::or_hits_into(_concreteSearchCtx, result, begin_id); } template BitVector::UP AttributeIteratorT::get_hits(uint32_t begin_id) { return AttributeIteratorBase::get_hits(_concreteSearchCtx, begin_id); } template BitVector::UP FilterAttributeIteratorT::get_hits(uint32_t begin_id) { return AttributeIteratorBase::get_hits(_concreteSearchCtx, begin_id); } template void AttributeIteratorT::and_hits_into(BitVector & result, uint32_t begin_id) { AttributeIteratorBase::and_hits_into(_concreteSearchCtx, result, begin_id); } template void FilterAttributeIteratorT::and_hits_into(BitVector & result, uint32_t begin_id) { AttributeIteratorBase::and_hits_into(_concreteSearchCtx, result, begin_id); } } // namespace search