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

jianliangqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new fa4d035ccaa [opt](inverted index) the "unicode" tokenizer can be 
configured to disable stop words. (#33982)
fa4d035ccaa is described below

commit fa4d035ccaab07b3c7f7b8a10c9dd3043bd2a90f
Author: zzzxl <[email protected]>
AuthorDate: Mon May 6 16:08:17 2024 +0800

    [opt](inverted index) the "unicode" tokenizer can be configured to disable 
stop words. (#33982)
    
    1. properties: "parser" = "unicode", "use_stopwords" = "none" disable stop 
words.
---
 be/src/clucene                                     |  2 +-
 be/src/olap/inverted_index_parser.cpp              |  9 ++++
 be/src/olap/inverted_index_parser.h                |  5 ++
 .../char_filter/char_replace_char_filter.h         |  4 +-
 .../rowset/segment_v2/inverted_index_reader.cpp    | 30 ++++++++---
 .../olap/rowset/segment_v2/inverted_index_reader.h |  5 ++
 .../rowset/segment_v2/inverted_index_writer.cpp    | 14 ++++-
 be/src/vec/functions/function_tokenize.cpp         |  4 ++
 .../apache/doris/analysis/InvertedIndexUtil.java   | 11 +++-
 .../data/inverted_index_p0/test_stopwords.out      | 23 +++++++++
 .../data/inverted_index_p0/test_tokenize.out       |  6 +++
 .../suites/inverted_index_p0/test_stopwords.groovy | 60 ++++++++++++++++++++++
 .../suites/inverted_index_p0/test_tokenize.groovy  |  3 ++
 13 files changed, 165 insertions(+), 11 deletions(-)

diff --git a/be/src/clucene b/be/src/clucene
index 9f849a47f70..d3de160871d 160000
--- a/be/src/clucene
+++ b/be/src/clucene
@@ -1 +1 @@
-Subproject commit 9f849a47f70625a57fedbaa1f5a6f89bc8f32967
+Subproject commit d3de160871dc1e2e293e5702e5b870e220ed42e4
diff --git a/be/src/olap/inverted_index_parser.cpp 
b/be/src/olap/inverted_index_parser.cpp
index 07a587dd2dd..a9ed7ec062e 100644
--- a/be/src/olap/inverted_index_parser.cpp
+++ b/be/src/olap/inverted_index_parser.cpp
@@ -126,4 +126,13 @@ std::string get_parser_ignore_above_value_from_properties(
     }
 }
 
+std::string get_parser_stopwords_from_properties(
+        const std::map<std::string, std::string>& properties) {
+    if (properties.find(INVERTED_INDEX_PARSER_STOPWORDS_KEY) != 
properties.end()) {
+        return properties.at(INVERTED_INDEX_PARSER_STOPWORDS_KEY);
+    } else {
+        return "";
+    }
+}
+
 } // namespace doris
diff --git a/be/src/olap/inverted_index_parser.h 
b/be/src/olap/inverted_index_parser.h
index 2eff4d7caf3..87ea7267237 100644
--- a/be/src/olap/inverted_index_parser.h
+++ b/be/src/olap/inverted_index_parser.h
@@ -79,6 +79,8 @@ const std::string INVERTED_INDEX_PARSER_IGNORE_ABOVE_VALUE = 
"256";
 
 const std::string INVERTED_INDEX_PARSER_LOWERCASE_KEY = "lower_case";
 
+const std::string INVERTED_INDEX_PARSER_STOPWORDS_KEY = "stopwords";
+
 std::string inverted_index_parser_type_to_string(InvertedIndexParserType 
parser_type);
 
 InvertedIndexParserType get_inverted_index_parser_type_from_string(const 
std::string& parser_str);
@@ -112,4 +114,7 @@ std::string get_parser_lowercase_from_properties(
     }
 }
 
+std::string get_parser_stopwords_from_properties(
+        const std::map<std::string, std::string>& properties);
+
 } // namespace doris
diff --git 
a/be/src/olap/rowset/segment_v2/inverted_index/char_filter/char_replace_char_filter.h
 
b/be/src/olap/rowset/segment_v2/inverted_index/char_filter/char_replace_char_filter.h
index 2867890b3e0..d9e5080d2d5 100644
--- 
a/be/src/olap/rowset/segment_v2/inverted_index/char_filter/char_replace_char_filter.h
+++ 
b/be/src/olap/rowset/segment_v2/inverted_index/char_filter/char_replace_char_filter.h
@@ -28,12 +28,14 @@ class CharReplaceCharFilter : public 
lucene::analysis::CharFilter {
 public:
     CharReplaceCharFilter(lucene::util::Reader* in, const std::string& pattern,
                           const std::string& replacement);
-    virtual ~CharReplaceCharFilter() = default;
+    ~CharReplaceCharFilter() override = default;
 
     void init(const void* _value, int32_t _length, bool copyData) override;
     int32_t read(const void** start, int32_t min, int32_t max) override;
     int32_t readCopy(void* start, int32_t off, int32_t len) override;
 
+    size_t size() override { return _buf.size(); }
+
 private:
     void fill();
     void process_pattern(std::string& buf);
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 c831f086deb..dc4aa0870b6 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp
+++ b/be/src/olap/rowset/segment_v2/inverted_index_reader.cpp
@@ -329,12 +329,8 @@ Status FullTextIndexReader::query(OlapReaderStatistics* 
stats, RuntimeState* run
                     
get_parser_mode_string_from_properties(_index_meta.properties()),
                     
get_parser_char_filter_map_from_properties(_index_meta.properties()));
             auto analyzer = create_analyzer(inverted_index_ctx.get());
-            auto lowercase = 
get_parser_lowercase_from_properties(_index_meta.properties());
-            if (lowercase == "true") {
-                analyzer->set_lowercase(true);
-            } else if (lowercase == "false") {
-                analyzer->set_lowercase(false);
-            }
+            setup_analyzer_lowercase(analyzer, _index_meta.properties());
+            setup_analyzer_use_stopwords(analyzer, _index_meta.properties());
             inverted_index_ctx->analyzer = analyzer.get();
             auto reader = create_reader(inverted_index_ctx.get(), search_str);
             get_analyse_result(query_info.terms, reader.get(), analyzer.get(), 
column_name,
@@ -423,6 +419,28 @@ InvertedIndexReaderType FullTextIndexReader::type() {
     return InvertedIndexReaderType::FULLTEXT;
 }
 
+void FullTextIndexReader::setup_analyzer_lowercase(
+        std::unique_ptr<lucene::analysis::Analyzer>& analyzer,
+        const std::map<string, string>& properties) {
+    auto lowercase = get_parser_lowercase_from_properties(properties);
+    if (lowercase == INVERTED_INDEX_PARSER_TRUE) {
+        analyzer->set_lowercase(true);
+    } else if (lowercase == INVERTED_INDEX_PARSER_FALSE) {
+        analyzer->set_lowercase(false);
+    }
+}
+
+void FullTextIndexReader::setup_analyzer_use_stopwords(
+        std::unique_ptr<lucene::analysis::Analyzer>& analyzer,
+        const std::map<string, string>& properties) {
+    auto stop_words = get_parser_stopwords_from_properties(properties);
+    if (stop_words == "none") {
+        analyzer->set_stopwords(nullptr);
+    } else {
+        analyzer->set_stopwords(&lucene::analysis::standard95::stop_words);
+    }
+}
+
 Status StringTypeInvertedIndexReader::new_iterator(
         OlapReaderStatistics* stats, RuntimeState* runtime_state,
         std::unique_ptr<InvertedIndexIterator>* iterator) {
diff --git a/be/src/olap/rowset/segment_v2/inverted_index_reader.h 
b/be/src/olap/rowset/segment_v2/inverted_index_reader.h
index 48450b974ac..8ea430cd1da 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index_reader.h
+++ b/be/src/olap/rowset/segment_v2/inverted_index_reader.h
@@ -175,6 +175,11 @@ public:
 
     InvertedIndexReaderType type() override;
 
+    static void 
setup_analyzer_lowercase(std::unique_ptr<lucene::analysis::Analyzer>& analyzer,
+                                         const std::map<string, string>& 
properties);
+    static void 
setup_analyzer_use_stopwords(std::unique_ptr<lucene::analysis::Analyzer>& 
analyzer,
+                                             const std::map<string, string>& 
properties);
+
 private:
     Status match_index_search(OlapReaderStatistics* stats, RuntimeState* 
runtime_state,
                               InvertedIndexQueryType query_type,
diff --git a/be/src/olap/rowset/segment_v2/inverted_index_writer.cpp 
b/be/src/olap/rowset/segment_v2/inverted_index_writer.cpp
index 7774dc0c1dd..bc8fc1b5045 100644
--- a/be/src/olap/rowset/segment_v2/inverted_index_writer.cpp
+++ b/be/src/olap/rowset/segment_v2/inverted_index_writer.cpp
@@ -224,6 +224,7 @@ public:
                 break;
             }
             setup_analyzer_lowercase(analyzer);
+            setup_analyzer_use_stopwords(analyzer);
             return Status::OK();
         } catch (CLuceneError& e) {
             return 
Status::Error<doris::ErrorCode::INVERTED_INDEX_ANALYZER_ERROR>(
@@ -233,13 +234,22 @@ public:
 
     void setup_analyzer_lowercase(std::unique_ptr<lucene::analysis::Analyzer>& 
analyzer) {
         auto lowercase = 
get_parser_lowercase_from_properties<true>(_index_meta->properties());
-        if (lowercase == "true") {
+        if (lowercase == INVERTED_INDEX_PARSER_TRUE) {
             analyzer->set_lowercase(true);
-        } else if (lowercase == "false") {
+        } else if (lowercase == INVERTED_INDEX_PARSER_FALSE) {
             analyzer->set_lowercase(false);
         }
     }
 
+    void 
setup_analyzer_use_stopwords(std::unique_ptr<lucene::analysis::Analyzer>& 
analyzer) {
+        auto stop_words = 
get_parser_stopwords_from_properties(_index_meta->properties());
+        if (stop_words == "none") {
+            analyzer->set_stopwords(nullptr);
+        } else {
+            analyzer->set_stopwords(&lucene::analysis::standard95::stop_words);
+        }
+    }
+
     Status init_fulltext_index() {
         RETURN_IF_ERROR(open_index_directory());
         RETURN_IF_ERROR(create_char_string_reader(_char_string_reader));
diff --git a/be/src/vec/functions/function_tokenize.cpp 
b/be/src/vec/functions/function_tokenize.cpp
index ddb509ddf49..e7dc2debe62 100644
--- a/be/src/vec/functions/function_tokenize.cpp
+++ b/be/src/vec/functions/function_tokenize.cpp
@@ -26,6 +26,7 @@
 #include "CLucene/StdHeader.h"
 #include "CLucene/config/repl_wchar.h"
 #include "olap/inverted_index_parser.h"
+#include "olap/rowset/segment_v2/inverted_index_reader.h"
 #include "vec/columns/column.h"
 #include "vec/common/string_ref.h"
 #include "vec/core/block.h"
@@ -151,6 +152,9 @@ Status FunctionTokenize::execute_impl(FunctionContext* 
/*context*/, Block& block
                 return 
Status::Error<doris::ErrorCode::INVERTED_INDEX_ANALYZER_ERROR>(
                         "inverted index create analyzer failed: {}", e.what());
             }
+            
doris::segment_v2::FullTextIndexReader::setup_analyzer_lowercase(analyzer, 
properties);
+            
doris::segment_v2::FullTextIndexReader::setup_analyzer_use_stopwords(analyzer,
+                                                                               
  properties);
 
             inverted_index_ctx.analyzer = analyzer.get();
             _do_tokenize(*col_left, inverted_index_ctx, *dest_nested_column, 
dest_offsets,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/InvertedIndexUtil.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/InvertedIndexUtil.java
index b57eae7746b..a2b0aa623c4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/InvertedIndexUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/InvertedIndexUtil.java
@@ -52,6 +52,8 @@ public class InvertedIndexUtil {
 
     public static String INVERTED_INDEX_PARSER_LOWERCASE_KEY = "lower_case";
 
+    public static String INVERTED_INDEX_PARSER_STOPWORDS_KEY = "stopwords";
+
     public static String getInvertedIndexParser(Map<String, String> 
properties) {
         String parser = properties == null ? null : 
properties.get(INVERTED_INDEX_PARSER_KEY);
         // default is "none" if not set
@@ -136,7 +138,8 @@ public class InvertedIndexUtil {
                 INVERTED_INDEX_PARSER_CHAR_FILTER_PATTERN,
                 INVERTED_INDEX_PARSER_CHAR_FILTER_REPLACEMENT,
                 INVERTED_INDEX_PARSER_IGNORE_ABOVE_KEY,
-                INVERTED_INDEX_PARSER_LOWERCASE_KEY
+                INVERTED_INDEX_PARSER_LOWERCASE_KEY,
+                INVERTED_INDEX_PARSER_STOPWORDS_KEY
         ));
 
         for (String key : properties.keySet()) {
@@ -152,6 +155,7 @@ public class InvertedIndexUtil {
         String charFilterPattern = 
properties.get(INVERTED_INDEX_PARSER_CHAR_FILTER_PATTERN);
         String ignoreAbove = 
properties.get(INVERTED_INDEX_PARSER_IGNORE_ABOVE_KEY);
         String lowerCase = properties.get(INVERTED_INDEX_PARSER_LOWERCASE_KEY);
+        String stopWords = properties.get(INVERTED_INDEX_PARSER_STOPWORDS_KEY);
 
         if (parser != null && 
!parser.matches("none|english|unicode|chinese|standard")) {
             throw new AnalysisException("Invalid inverted index 'parser' 
value: " + parser
@@ -194,5 +198,10 @@ public class InvertedIndexUtil {
             throw new AnalysisException(
                     "Invalid inverted index 'lower_case' value: " + lowerCase 
+ ", lower_case must be true or false");
         }
+
+        if (stopWords != null && !stopWords.matches("none")) {
+            throw new AnalysisException("Invalid inverted index 'stopWords' 
value: " + stopWords
+                    + ", stopWords must be none");
+        }
     }
 }
diff --git a/regression-test/data/inverted_index_p0/test_stopwords.out 
b/regression-test/data/inverted_index_p0/test_stopwords.out
new file mode 100644
index 00000000000..ba4940bcc5b
--- /dev/null
+++ b/regression-test/data/inverted_index_p0/test_stopwords.out
@@ -0,0 +1,23 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !sql --
+
+-- !sql --
+
+-- !sql --
+
+-- !sql --
+1      华夏智胜新税股票A       华夏智胜新税股票A
+2      Life is like a box of chocolates, you never know what you are going to 
get.     Life is like a box of chocolates, you never know what you are going to 
get. 
+
+-- !sql --
+2      Life is like a box of chocolates, you never know what you are going to 
get.     Life is like a box of chocolates, you never know what you are going to 
get. 
+
+-- !sql --
+2      Life is like a box of chocolates, you never know what you are going to 
get.     Life is like a box of chocolates, you never know what you are going to 
get. 
+
+-- !sql --
+2      Life is like a box of chocolates, you never know what you are going to 
get.     Life is like a box of chocolates, you never know what you are going to 
get. 
+
+-- !sql --
+2      Life is like a box of chocolates, you never know what you are going to 
get.     Life is like a box of chocolates, you never know what you are going to 
get. 
+
diff --git a/regression-test/data/inverted_index_p0/test_tokenize.out 
b/regression-test/data/inverted_index_p0/test_tokenize.out
index 96902bef1c0..ae22daffe15 100644
--- a/regression-test/data/inverted_index_p0/test_tokenize.out
+++ b/regression-test/data/inverted_index_p0/test_tokenize.out
@@ -31,3 +31,9 @@
 -- !tokenize_sql --
 ["get", "images", "hm", "bg", "jpg", "http", "1", "0", "test", "abc", "bcd"]
 
+-- !tokenize_sql --
+["华", "夏", "智", "胜", "新", "税", "股", "票"]
+
+-- !tokenize_sql --
+["华", "夏", "智", "胜", "新", "税", "股", "票", "a"]
+
diff --git a/regression-test/suites/inverted_index_p0/test_stopwords.groovy 
b/regression-test/suites/inverted_index_p0/test_stopwords.groovy
new file mode 100644
index 00000000000..4f7c577dc57
--- /dev/null
+++ b/regression-test/suites/inverted_index_p0/test_stopwords.groovy
@@ -0,0 +1,60 @@
+// 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_stopwords", "p0"){
+    def indexTbName = "test_stopwords"
+
+    sql "DROP TABLE IF EXISTS ${indexTbName}"
+
+    sql """
+      CREATE TABLE ${indexTbName} (
+        `a` int(11) NULL COMMENT "",
+        `b` text NULL COMMENT "",
+        `c` text NULL COMMENT "",
+        INDEX b_idx (`b`) USING INVERTED PROPERTIES("parser" = "unicode") 
COMMENT '',
+        INDEX c_idx (`c`) USING INVERTED PROPERTIES("parser" = "unicode", 
"stopwords" = "none") COMMENT ''
+      ) ENGINE=OLAP
+        DUPLICATE KEY(`a`)
+        COMMENT "OLAP"
+        DISTRIBUTED BY RANDOM BUCKETS 1
+        PROPERTIES (
+        "replication_allocation" = "tag.location.default: 1"
+      );
+    """
+
+    sql """ INSERT INTO ${indexTbName} VALUES (1, "华夏智胜新税股票A", "华夏智胜新税股票A"); 
"""
+    sql """ INSERT INTO ${indexTbName} VALUES (2, "Life is like a box of 
chocolates, you never know what you are going to get. ", "Life is like a box of 
chocolates, you never know what you are going to get. "); """
+
+    try {
+        sql "sync"
+
+        qt_sql """ select * from ${indexTbName} where b match 'a'; """
+        qt_sql """ select * from ${indexTbName} where b match 'are'; """
+        qt_sql """ select * from ${indexTbName} where b match 'to'; """
+
+        qt_sql """ select * from ${indexTbName} where c match 'a'; """
+        qt_sql """ select * from ${indexTbName} where c match 'are'; """
+        qt_sql """ select * from ${indexTbName} where c match 'to'; """
+
+        qt_sql """ select * from ${indexTbName} where b match_phrase 'like a 
box'; """
+        qt_sql """ select * from ${indexTbName} where c match_phrase 'like a 
box'; """
+
+    } finally {
+        //try_sql("DROP TABLE IF EXISTS ${testTable}")
+    }
+}
diff --git a/regression-test/suites/inverted_index_p0/test_tokenize.groovy 
b/regression-test/suites/inverted_index_p0/test_tokenize.groovy
index bf3d958f8e2..8d7e2dac42e 100644
--- a/regression-test/suites/inverted_index_p0/test_tokenize.groovy
+++ b/regression-test/suites/inverted_index_p0/test_tokenize.groovy
@@ -95,4 +95,7 @@ suite("test_tokenize"){
 
     qt_tokenize_sql """SELECT TOKENIZE('GET /images/hm_bg.jpg HTTP/1.0 
test:abc=bcd','"parser"="unicode","char_filter_type" = 
"char_replace","char_filter_pattern" = "._=:,","char_filter_replacement" = " 
"');"""
     qt_tokenize_sql """SELECT TOKENIZE('GET /images/hm_bg.jpg HTTP/1.0 
test:abc=bcd', '"parser"="unicode","char_filter_type" = "char_replace", 
"char_filter_pattern" = "._=:,", "char_filter_replacement" = " "');"""
+
+    qt_tokenize_sql """SELECT TOKENIZE('华夏智胜新税股票A', '"parser"="unicode"');"""
+    qt_tokenize_sql """SELECT TOKENIZE('华夏智胜新税股票A', 
'"parser"="unicode","stopwords" = "none"');"""
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to