-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[improvement](inverted index) Push the candidate row bitmap down into phrase queries #67180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
af66197
57c3522
cbc7778
f82c48c
4057b24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,15 @@ void PhraseQuery::add(const InvertedIndexQueryInfo& query_info) { | |
| init_ordered_sloppy_phrase_matcher(query_info, is_similarity); | ||
| } | ||
|
|
||
| // Two-phase evaluation with a pushed-down candidate set: the candidate | ||
| // bitmap joins the leapfrog intersection (restricting doc-list walking and | ||
| // position verification to candidates) but never a matcher's postings, so | ||
| // phrase semantics stay with the real term iterators. | ||
| if (_context->candidate_rows != nullptr) { | ||
| _iterators.emplace_back(std::make_shared<RoaringDocIdIterator>(_context->candidate_rows)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Do not expose this candidate-restricted bitmap as a full-domain expression result.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in RED on the previous head reproduced both observable failures for candidate row 0 ( Normalized triage (arithmetic mean): severity 10/10 (silent wrong result), scenario confidence 10/10 (deterministic RED), production likelihood 3.5/10 (requires the VirtualSlotRef-wrapped nullable compound shape) => 7.83/10, above the 6/10 fix threshold. |
||
| _context->candidate_rows_consumed = true; | ||
| } | ||
|
|
||
| std::sort(_iterators.begin(), _iterators.end(), [](const DISI& a, const DISI& b) { | ||
| int64_t freq1 = visit_node(a, DocFreq {}); | ||
| int64_t freq2 = visit_node(b, DocFreq {}); | ||
|
|
@@ -64,6 +73,12 @@ void PhraseQuery::add(const InvertedIndexQueryInfo& query_info) { | |
| for (int32_t i = 2; i < _iterators.size(); i++) { | ||
| _others.emplace_back(&_iterators[i]); | ||
| } | ||
| for (auto& iter : _iterators) { | ||
| if (const auto* term_iter = std::get_if<TermPositionsIterPtr>(&iter)) { | ||
| _norm_source = term_iter->get(); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| init_similarities(query_info.field_name, is_similarity); | ||
| } | ||
|
|
@@ -160,6 +175,11 @@ void PhraseQuery::search(roaring::Roaring& roaring) { | |
| } | ||
|
|
||
| void PhraseQuery::search_by_skiplist(roaring::Roaring& roaring) { | ||
| if (_phrase_similarity) { | ||
| // _norm_source is fixed once in add(); validate it before the loop so | ||
| // the per-document path below stays free of release-mode checks. | ||
| DORIS_CHECK(_norm_source != nullptr); | ||
| } | ||
| int32_t doc = 0; | ||
| while ((doc = do_next(visit_node(*_lead1, NextDoc {}))) != INT32_MAX) { | ||
| if (_phrase_similarity) { | ||
|
|
@@ -168,7 +188,7 @@ void PhraseQuery::search_by_skiplist(roaring::Roaring& roaring) { | |
| continue; | ||
| } | ||
| roaring.add(doc); | ||
| int32_t norm = visit_node(*_lead1, Norm {}); | ||
| int32_t norm = _norm_source->norm(); | ||
| float score = _phrase_similarity->score(phrase_freq, static_cast<int64_t>(norm)); | ||
|
|
||
| _context->collection_similarity->collect(doc, score); | ||
|
|
@@ -285,4 +305,4 @@ void PhraseQuery::parser_info(OlapReaderStatistics* stats, std::string& query, | |
| } | ||
| } | ||
|
|
||
| } // namespace doris::segment_v2 | ||
| } // namespace doris::segment_v2 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // Licensed to the Apache Software Foundation (ASF) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The ASF licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <climits> | ||
| #include <cstdint> | ||
| #include <memory> | ||
|
|
||
| #include "roaring/roaring.hh" | ||
|
|
||
| namespace doris::segment_v2 { | ||
|
|
||
| // Read-only DISI adapter over a candidate row bitmap | ||
| // (IndexQueryContext::candidate_rows). Joining the leapfrog intersection of | ||
| // PhraseQuery, it restricts doc-list intersection and position verification to | ||
| // the candidate set (two-phase evaluation: this iterator drives the | ||
| // approximation, real term iterators keep the position semantics). It never | ||
| // joins a matcher's postings, so freq()/next_position()/norm() only satisfy | ||
| // the DISI interface with neutral values. | ||
| // | ||
| // The underlying bitmap is NOT owned and must outlive the iterator; leapfrog | ||
| // only moves forward, so advance() targets are monotonically non-decreasing. | ||
| class RoaringDocIdIterator { | ||
| public: | ||
| explicit RoaringDocIdIterator(const roaring::Roaring* rows) | ||
| : _rows(rows), _iter(rows->begin()) {} | ||
|
|
||
| // DISI convention: an iterator starts positioned BEFORE its first doc | ||
| // (search_by_skiplist opens with a NextDoc on the lead), so doc_id() is -1 | ||
| // until the first next_doc()/advance() moves onto a real position. | ||
| int32_t doc_id() const { | ||
| if (!_started) { | ||
| return -1; | ||
| } | ||
| return _iter == _rows->end() ? INT_MAX : static_cast<int32_t>(*_iter); | ||
| } | ||
|
|
||
| int32_t freq() const { return 1; } | ||
|
|
||
| int32_t next_doc() { | ||
| if (!_started) { | ||
| _started = true; | ||
| } else if (_iter != _rows->end()) { | ||
| ++_iter; | ||
| } | ||
| return doc_id(); | ||
| } | ||
|
|
||
| int32_t advance(int32_t target) { | ||
| _started = true; | ||
| if (target > 0) { | ||
| _iter.equalorlarger(static_cast<uint32_t>(target)); | ||
| } | ||
| return doc_id(); | ||
| } | ||
|
|
||
| int32_t doc_freq() const { | ||
| uint64_t cardinality = _rows->cardinality(); | ||
| return cardinality > INT_MAX ? INT_MAX : static_cast<int32_t>(cardinality); | ||
| } | ||
|
|
||
| int32_t next_position() { return 0; } | ||
|
|
||
| int32_t norm() const { return 1; } | ||
|
|
||
| private: | ||
| const roaring::Roaring* _rows; | ||
| roaring::Roaring::const_iterator _iter; | ||
| bool _started = false; | ||
| }; | ||
| using RoaringDocIdIterPtr = std::shared_ptr<RoaringDocIdIterator>; | ||
|
|
||
| } // namespace doris::segment_v2 |
Uh oh!
There was an error while loading. Please reload this page.