This is an automated email from the ASF dual-hosted git repository.

airborne pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new 8d32c8fd517 [feature](inverted index) add ordered functionality to 
match_phrase query (#37752)
8d32c8fd517 is described below

commit 8d32c8fd517bd3b4b24b172bcddb0a59dbebc0e7
Author: zzzxl <33418555+zzzxl1...@users.noreply.github.com>
AuthorDate: Mon Jul 15 18:44:47 2024 +0800

    [feature](inverted index) add ordered functionality to match_phrase query 
(#37752)
    
    ## Proposed changes
    
    1. select count() from tbl where b match_phrase 'the brown ~2+';
    
    pick from #36356
---
 be/src/clucene                                     |   2 +-
 .../inverted_index/query/phrase_query.cpp          | 317 ++++++++++++++++++++-
 .../segment_v2/inverted_index/query/phrase_query.h | 101 ++++++-
 .../rowset/segment_v2/inverted_index_reader.cpp    |  23 +-
 .../test_index_match_phrase_ordered.out            |  67 +++++
 .../test_index_match_phrase_ordered.groovy         |  87 ++++++
 6 files changed, 574 insertions(+), 23 deletions(-)

diff --git a/be/src/clucene b/be/src/clucene
index 51f15724f5b..d83b1628694 160000
--- a/be/src/clucene
+++ b/be/src/clucene
@@ -1 +1 @@
-Subproject commit 51f15724f5bdb7ae5f6f5e5d7072d43a5bda63f8
+Subproject commit d83b1628694a22c7bc7e12e0d8511363b1b770bd
diff --git 
a/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.cpp 
b/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.cpp
index 9e57ee6e29d..4fa98741176 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.cpp
+++ b/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.cpp
@@ -21,7 +21,286 @@
 
 namespace doris::segment_v2 {
 
-Status PhraseQuery::parser_slop(std::string& query, int32_t& slop) {
+template <typename Derived>
+bool PhraseMatcherBase<Derived>::matches(int32_t doc) {
+    reset(doc);
+    return static_cast<Derived*>(this)->next_match();
+}
+
+template <typename Derived>
+void PhraseMatcherBase<Derived>::reset(int32_t doc) {
+    for (PostingsAndPosition& posting : _postings) {
+        if (posting._postings.docID() != doc) {
+            posting._postings.advance(doc);
+        }
+        posting._freq = posting._postings.freq();
+        posting._pos = -1;
+        posting._upTo = 0;
+    }
+}
+
+template <typename Derived>
+bool PhraseMatcherBase<Derived>::advance_position(PostingsAndPosition& 
posting, int32_t target) {
+    while (posting._pos < target) {
+        if (posting._upTo == posting._freq) {
+            return false;
+        } else {
+            posting._pos = posting._postings.nextPosition();
+            posting._upTo += 1;
+        }
+    }
+    return true;
+}
+
+bool ExactPhraseMatcher::next_match() {
+    PostingsAndPosition& lead = _postings[0];
+    if (lead._upTo < lead._freq) {
+        lead._pos = lead._postings.nextPosition();
+        lead._upTo += 1;
+    } else {
+        return false;
+    }
+
+    while (true) {
+        int32_t phrasePos = lead._pos - lead._offset;
+
+        bool advance_head = false;
+        for (size_t j = 1; j < _postings.size(); ++j) {
+            PostingsAndPosition& posting = _postings[j];
+            int32_t expectedPos = phrasePos + posting._offset;
+            // advance up to the same position as the lead
+            if (!advance_position(posting, expectedPos)) {
+                return false;
+            }
+
+            if (posting._pos != expectedPos) { // we advanced too far
+                if (advance_position(lead, posting._pos - posting._offset + 
lead._offset)) {
+                    advance_head = true;
+                    break;
+                } else {
+                    return false;
+                }
+            }
+        }
+        if (advance_head) {
+            continue;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+bool OrderedSloppyPhraseMatcher::next_match() {
+    PostingsAndPosition* prev_posting = _postings.data();
+    while (prev_posting->_upTo < prev_posting->_freq) {
+        prev_posting->_pos = prev_posting->_postings.nextPosition();
+        prev_posting->_upTo += 1;
+        if (stretchToOrder(prev_posting) && _match_width <= _allowed_slop) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool OrderedSloppyPhraseMatcher::stretchToOrder(PostingsAndPosition* 
prev_posting) {
+    _match_width = 0;
+    for (size_t i = 1; i < _postings.size(); i++) {
+        PostingsAndPosition& posting = _postings[i];
+        if (!advance_position(posting, prev_posting->_pos + 1)) {
+            return false;
+        }
+        _match_width += (posting._pos - (prev_posting->_pos + 1));
+        prev_posting = &posting;
+    }
+    return true;
+}
+
+PhraseQuery::PhraseQuery(const std::shared_ptr<lucene::search::IndexSearcher>& 
searcher,
+                         const TQueryOptions& query_options)
+        : _searcher(searcher) {}
+
+PhraseQuery::~PhraseQuery() {
+    for (auto& term_doc : _term_docs) {
+        if (term_doc) {
+            _CLDELETE(term_doc);
+        }
+    }
+    for (auto& term : _terms) {
+        if (term) {
+            _CLDELETE(term);
+        }
+    }
+}
+
+void PhraseQuery::add(const std::wstring& field_name, const 
std::vector<std::string>& terms,
+                      int32_t slop, bool ordered) {
+    if (terms.empty()) {
+        _CLTHROWA(CL_ERR_IllegalArgument, "PhraseQuery::add: terms empty");
+    }
+
+    if (slop == 0 || ordered) {
+        add(field_name, terms, slop);
+    } else {
+        auto query = std::make_unique<CL_NS(search)::PhraseQuery>();
+        for (const auto& term : terms) {
+            std::wstring ws_term = StringUtil::string_to_wstring(term);
+            auto* t = _CLNEW lucene::index::Term(field_name.c_str(), 
ws_term.c_str());
+            query->add(t);
+            _CLDECDELETE(t);
+        }
+        query->setSlop(slop);
+        _matcher = std::move(query);
+    }
+}
+
+void PhraseQuery::add(const std::wstring& field_name, const 
std::vector<std::string>& terms,
+                      int32_t slop) {
+    if (terms.empty()) {
+        _CLTHROWA(CL_ERR_IllegalArgument, "PhraseQuery::add: terms empty");
+    }
+
+    if (terms.size() == 1) {
+        std::wstring ws_term = StringUtil::string_to_wstring(terms[0]);
+        Term* t = _CLNEW Term(field_name.c_str(), ws_term.c_str());
+        _terms.push_back(t);
+        TermDocs* term_doc = _searcher->getReader()->termDocs(t);
+        _term_docs.push_back(term_doc);
+        _lead1 = TermIterator(term_doc);
+        return;
+    }
+
+    std::vector<TermIterator> iterators;
+    auto ensureTermPosition = [this, &iterators, &field_name](const 
std::string& term) {
+        std::wstring ws_term = StringUtil::string_to_wstring(term);
+        Term* t = _CLNEW Term(field_name.c_str(), ws_term.c_str());
+        _terms.push_back(t);
+        TermPositions* term_pos = _searcher->getReader()->termPositions(t);
+        _term_docs.push_back(term_pos);
+        iterators.emplace_back(term_pos);
+        return term_pos;
+    };
+
+    if (slop == 0) {
+        ExactPhraseMatcher matcher;
+        for (size_t i = 0; i < terms.size(); i++) {
+            const auto& term = terms[i];
+            auto* term_pos = ensureTermPosition(term);
+            matcher._postings.emplace_back(term_pos, i);
+        }
+        _matcher = matcher;
+    } else {
+        OrderedSloppyPhraseMatcher matcher;
+        for (size_t i = 0; i < terms.size(); i++) {
+            const auto& term = terms[i];
+            auto* term_pos = ensureTermPosition(term);
+            matcher._postings.emplace_back(term_pos, i);
+        }
+        matcher._allowed_slop = slop;
+        _matcher = matcher;
+    }
+
+    std::sort(iterators.begin(), iterators.end(), [](const TermIterator& a, 
const TermIterator& b) {
+        return a.docFreq() < b.docFreq();
+    });
+
+    _lead1 = iterators[0];
+    _lead2 = iterators[1];
+    for (int32_t i = 2; i < iterators.size(); i++) {
+        _others.push_back(iterators[i]);
+    }
+}
+
+void PhraseQuery::search(roaring::Roaring& roaring) {
+    if (std::holds_alternative<PhraseQueryPtr>(_matcher)) {
+        _searcher->_search(
+                std::get<PhraseQueryPtr>(_matcher).get(),
+                [&roaring](const int32_t docid, const float_t /*score*/) { 
roaring.add(docid); });
+    } else {
+        if (_lead1.isEmpty()) {
+            return;
+        }
+        if (_lead2.isEmpty()) {
+            search_by_bitmap(roaring);
+            return;
+        }
+        search_by_skiplist(roaring);
+    }
+}
+
+void PhraseQuery::search_by_bitmap(roaring::Roaring& roaring) {
+    DocRange doc_range;
+    while (_lead1.readRange(&doc_range)) {
+        if (doc_range.type_ == DocRangeType::kMany) {
+            roaring.addMany(doc_range.doc_many_size_, 
doc_range.doc_many->data());
+        } else {
+            roaring.addRange(doc_range.doc_range.first, 
doc_range.doc_range.second);
+        }
+    }
+}
+
+void PhraseQuery::search_by_skiplist(roaring::Roaring& roaring) {
+    int32_t doc = 0;
+    while ((doc = do_next(_lead1.nextDoc())) != INT32_MAX) {
+        if (matches(doc)) {
+            roaring.add(doc);
+        }
+    }
+}
+
+int32_t PhraseQuery::do_next(int32_t doc) {
+    while (true) {
+        assert(doc == _lead1.docID());
+
+        // the skip list is used to find the two smallest inverted lists
+        int32_t next2 = _lead2.advance(doc);
+        if (next2 != doc) {
+            doc = _lead1.advance(next2);
+            if (next2 != doc) {
+                continue;
+            }
+        }
+
+        // if both lead1 and lead2 exist, use skip list to lookup other 
inverted indexes
+        bool advance_head = false;
+        for (auto& other : _others) {
+            if (other.isEmpty()) {
+                continue;
+            }
+
+            if (other.docID() < doc) {
+                int32_t next = other.advance(doc);
+                if (next > doc) {
+                    doc = _lead1.advance(next);
+                    advance_head = true;
+                    break;
+                }
+            }
+        }
+        if (advance_head) {
+            continue;
+        }
+
+        return doc;
+    }
+}
+
+bool PhraseQuery::matches(int32_t doc) {
+    return std::visit(
+            [&doc](auto&& m) -> bool {
+                using T = std::decay_t<decltype(m)>;
+                if constexpr (std::is_same_v<T, PhraseQueryPtr>) {
+                    _CLTHROWA(CL_ERR_IllegalArgument,
+                              "PhraseQueryPtr does not support matches 
function");
+                } else {
+                    return m.matches(doc);
+                }
+            },
+            _matcher);
+}
+
+void PhraseQuery::parser_slop(std::string& query, int32_t& slop, bool& 
ordered_phrase) {
     auto is_digits = [](const std::string_view& str) {
         return std::all_of(str.begin(), str.end(), [](unsigned char c) { 
return std::isdigit(c); });
     };
@@ -32,17 +311,37 @@ Status PhraseQuery::parser_slop(std::string& query, 
int32_t& slop) {
         if (tilde_pos < query.size() - 1 && query[tilde_pos] == '~') {
             size_t slop_pos = tilde_pos + 1;
             std::string_view slop_str(query.data() + slop_pos, query.size() - 
slop_pos);
-            if (is_digits(slop_str)) {
-                auto result = std::from_chars(slop_str.begin(), 
slop_str.end(), slop);
-                if (result.ec != std::errc()) {
-                    return 
Status::Error<doris::ErrorCode::INVERTED_INDEX_INVALID_PARAMETERS>(
-                            "PhraseQuery parser failed: {}", query);
+            do {
+                if (slop_str.empty()) {
+                    break;
                 }
-                query = query.substr(0, last_space_pos);
-            }
+
+                bool ordered = false;
+                if (slop_str.size() == 1) {
+                    if (!std::isdigit(slop_str[0])) {
+                        break;
+                    }
+                } else {
+                    if (slop_str.back() == '+') {
+                        ordered = true;
+                        slop_str.remove_suffix(1);
+                    }
+                }
+
+                if (is_digits(slop_str)) {
+                    auto result = std::from_chars(slop_str.begin(), 
slop_str.end(), slop);
+                    if (result.ec != std::errc()) {
+                        break;
+                    }
+                    ordered_phrase = ordered;
+                    query = query.substr(0, last_space_pos);
+                }
+            } while (false);
         }
     }
-    return Status::OK();
 }
 
+template class PhraseMatcherBase<ExactPhraseMatcher>;
+template class PhraseMatcherBase<OrderedSloppyPhraseMatcher>;
+
 } // namespace doris::segment_v2
\ No newline at end of file
diff --git a/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.h 
b/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.h
index 1b6c559c849..053a001b95d 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.h
+++ b/be/src/olap/rowset/segment_v2/inverted_index/query/phrase_query.h
@@ -17,13 +17,110 @@
 
 #pragma once
 
-#include "common/status.h"
+// clang-format off
+#include <CLucene.h> // IWYU pragma: keep
+#include <CLucene/index/Term.h>
+#include <CLucene/search/query/TermIterator.h>
+#include <CLucene/search/query/TermPositionIterator.h>
+#include "CLucene/search/PhraseQuery.h"
+// clang-format on
+
+#include <gen_cpp/PaloInternalService_types.h>
+
+#include <memory>
+#include <variant>
+
+#include "roaring/roaring.hh"
+
+CL_NS_USE(index)
+CL_NS_USE(search)
+CL_NS_USE(util)
 
 namespace doris::segment_v2 {
 
+class PostingsAndPosition {
+public:
+    PostingsAndPosition(const TermPositionIterator& postings, int32_t offset)
+            : _postings(postings), _offset(offset) {}
+
+    TermPositionIterator _postings;
+    int32_t _offset = 0;
+    int32_t _freq = 0;
+    int32_t _upTo = 0;
+    int32_t _pos = 0;
+};
+
+template <typename Derived>
+class PhraseMatcherBase {
+public:
+    bool matches(int32_t doc);
+
+private:
+    void reset(int32_t doc);
+
+protected:
+    bool advance_position(PostingsAndPosition& posting, int32_t target);
+
+public:
+    std::vector<PostingsAndPosition> _postings;
+};
+
+class ExactPhraseMatcher : public PhraseMatcherBase<ExactPhraseMatcher> {
+public:
+    bool next_match();
+};
+
+class OrderedSloppyPhraseMatcher : public 
PhraseMatcherBase<OrderedSloppyPhraseMatcher> {
+public:
+    bool next_match();
+
+private:
+    bool stretchToOrder(PostingsAndPosition* prev_posting);
+
+public:
+    int32_t _allowed_slop = 0;
+
+private:
+    int32_t _match_width = -1;
+};
+
+using PhraseQueryPtr = std::unique_ptr<CL_NS(search)::PhraseQuery>;
+using Matcher = std::variant<ExactPhraseMatcher, OrderedSloppyPhraseMatcher, 
PhraseQueryPtr>;
+
 class PhraseQuery {
 public:
-    static Status parser_slop(std::string& query, int32_t& slop);
+    PhraseQuery(const std::shared_ptr<lucene::search::IndexSearcher>& searcher,
+                const TQueryOptions& query_options);
+    ~PhraseQuery();
+
+    void add(const std::wstring& field_name, const std::vector<std::string>& 
terms, int32_t slop,
+             bool ordered);
+    void add(const std::wstring& field_name, const std::vector<std::string>& 
terms, int32_t slop);
+    void search(roaring::Roaring& roaring);
+
+private:
+    void search_by_bitmap(roaring::Roaring& roaring);
+    void search_by_skiplist(roaring::Roaring& roaring);
+
+    int32_t do_next(int32_t doc);
+    bool matches(int32_t doc);
+
+public:
+    static void parser_slop(std::string& query, int32_t& slop, bool& 
ordered_phrase);
+
+private:
+    std::shared_ptr<lucene::search::IndexSearcher> _searcher;
+
+    TermIterator _lead1;
+    TermIterator _lead2;
+    std::vector<TermIterator> _others;
+
+    std::vector<PostingsAndPosition> _postings;
+
+    std::vector<Term*> _terms;
+    std::vector<TermDocs*> _term_docs;
+
+    Matcher _matcher;
 };
 
 } // namespace doris::segment_v2
\ No newline at end of file
diff --git a/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp 
b/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp
index f944ad7a510..12ac45db929 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp
+++ b/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp
@@ -270,12 +270,13 @@ Status FullTextIndexReader::query(OlapReaderStatistics* 
stats, RuntimeState* run
 
     try {
         int32_t slop = 0;
+        bool ordered = false;
         std::vector<std::string> analyse_result;
         if (query_type == InvertedIndexQueryType::MATCH_REGEXP_QUERY) {
             analyse_result.emplace_back(search_str);
         } else {
             if (query_type == InvertedIndexQueryType::MATCH_PHRASE_QUERY) {
-                RETURN_IF_ERROR(PhraseQuery::parser_slop(search_str, slop));
+                PhraseQuery::parser_slop(search_str, slop, ordered);
             }
 
             InvertedIndexCtxSPtr inverted_index_ctx = 
std::make_shared<InvertedIndexCtx>();
@@ -327,6 +328,7 @@ Status FullTextIndexReader::query(OlapReaderStatistics* 
stats, RuntimeState* run
             }
             if (query_type == InvertedIndexQueryType::MATCH_PHRASE_QUERY) {
                 str_tokens += " " + std::to_string(slop);
+                str_tokens += " " + std::to_string(ordered);
             }
 
             auto* cache = InvertedIndexQueryCache::instance();
@@ -351,17 +353,16 @@ Status FullTextIndexReader::query(OlapReaderStatistics* 
stats, RuntimeState* run
 
                 Status res = Status::OK();
                 if (query_type == InvertedIndexQueryType::MATCH_PHRASE_QUERY) {
-                    auto* phrase_query = new lucene::search::PhraseQuery();
-                    for (auto& token : analyse_result) {
-                        std::wstring wtoken = 
StringUtil::string_to_wstring(token);
-                        auto* term = _CLNEW 
lucene::index::Term(field_ws.c_str(), wtoken.c_str());
-                        phrase_query->add(term);
-                        _CLDECDELETE(term);
+                    TQueryOptions queryOptions = 
runtime_state->query_options();
+                    try {
+                        
SCOPED_RAW_TIMER(&stats->inverted_index_searcher_search_timer);
+                        PhraseQuery query(index_searcher, queryOptions);
+                        query.add(field_ws, analyse_result, slop, ordered);
+                        query.search(*term_match_bitmap);
+                    } catch (const CLuceneError& e) {
+                        return 
Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
+                                "CLuceneError occured: {}", e.what());
                     }
-                    phrase_query->setSlop(slop);
-                    query.reset(phrase_query);
-                    res = normal_index_search(stats, query_type, 
index_searcher,
-                                              null_bitmap_already_read, query, 
term_match_bitmap);
                 } else if (query_type == 
InvertedIndexQueryType::MATCH_PHRASE_PREFIX_QUERY) {
                     res = match_phrase_prefix_index_search(stats, 
runtime_state, field_ws,
                                                            analyse_result, 
index_searcher,
diff --git 
a/regression-test/data/inverted_index_p0/test_index_match_phrase_ordered.out 
b/regression-test/data/inverted_index_p0/test_index_match_phrase_ordered.out
new file mode 100644
index 00000000000..d1e04ececd5
--- /dev/null
+++ b/regression-test/data/inverted_index_p0/test_index_match_phrase_ordered.out
@@ -0,0 +1,67 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !sql --
+11
+
+-- !sql --
+11
+
+-- !sql --
+11
+
+-- !sql --
+0
+
+-- !sql --
+11
+
+-- !sql --
+2
+
+-- !sql --
+2
+
+-- !sql --
+2
+
+-- !sql --
+7
+
+-- !sql --
+7
+
+-- !sql --
+7
+
+-- !sql --
+7
+
+-- !sql --
+7
+
+-- !sql --
+7
+
+-- !sql --
+11
+
+-- !sql --
+7
+
+-- !sql --
+11
+
+-- !sql --
+7
+
+-- !sql --
+11
+
+-- !sql --
+7
+
+-- !sql --
+11
+
+-- !sql --
+7
+
diff --git 
a/regression-test/suites/inverted_index_p0/test_index_match_phrase_ordered.groovy
 
b/regression-test/suites/inverted_index_p0/test_index_match_phrase_ordered.groovy
new file mode 100644
index 00000000000..137bab70f05
--- /dev/null
+++ 
b/regression-test/suites/inverted_index_p0/test_index_match_phrase_ordered.groovy
@@ -0,0 +1,87 @@
+// 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.
+
+
+suite("test_index_match_phrase_ordered", "p0"){
+    def indexTbName1 = "test_index_match_phrase_ordered"
+
+    sql "DROP TABLE IF EXISTS ${indexTbName1}"
+
+    sql """
+      CREATE TABLE ${indexTbName1} (
+      `a` int(11) NULL COMMENT "",
+      `b` string NULL COMMENT "",
+      INDEX b_idx (`b`) USING INVERTED PROPERTIES("parser" = "english", 
"support_phrase" = "true") COMMENT ''
+      ) ENGINE=OLAP
+      DUPLICATE KEY(`a`)
+      COMMENT "OLAP"
+      DISTRIBUTED BY RANDOM BUCKETS 1
+      PROPERTIES (
+      "replication_allocation" = "tag.location.default: 1"
+      );
+    """
+
+    sql """ INSERT INTO ${indexTbName1} VALUES (1, "the quick brown fox jumped 
over the lazy dog"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (2, "the quick brown fox jumped 
over the lazy dog over"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (3, "the quick brown fox jumped 
over the lazy dog jumped"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (4, "the quick brown fox jumped 
over the lazy dog fox"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (5, "the quick brown fox jumped 
over the lazy dog brown"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (6, "the quick brown fox jumped 
over the lazy dog quick"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (7, "quick brown fox jumped 
over the lazy dog over"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (8, "quick brown fox jumped 
over the lazy dog jumped"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (9, "quick brown fox jumped 
over the lazy dog fox"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (10, "quick brown fox jumped 
over the lazy dog brown"); """
+    sql """ INSERT INTO ${indexTbName1} VALUES (11, "quick brown fox jumped 
over the lazy dog quick"); """
+
+    try {
+        sql "sync"
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the lazy'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the lazy ~1'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the lazy ~1+'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the lazy ~1+ '; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the over ~2'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the over ~2+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the jumped ~2'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the jumped ~2+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the fox ~2'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the fox ~2+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the brown ~2'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the brown ~2+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the quick ~2'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the quick ~2+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the jumped ~3'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the jumped ~3+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the fox ~4'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the fox ~4+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the brown ~5'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the brown ~5+'; """
+
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the quick ~6'; """
+        qt_sql """ select count() from ${indexTbName1} where b match_phrase 
'the quick ~6+'; """
+    } finally {
+        //try_sql("DROP TABLE IF EXISTS ${testTable}")
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to