airborne12 commented on code in PR #67918:
URL: https://github.com/apache/doris/pull/67918#discussion_r4000586555
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -152,35 +153,29 @@ private static String resolveComponentIdentity(String
name, IndexPolicyTypeEnum
return "";
}
- // Check if it's a built-in component
- if (expectedType == IndexPolicyTypeEnum.TOKENIZER
- && IndexPolicy.BUILTIN_TOKENIZERS.contains(name)) {
- return name;
- }
-
- // For custom component, get its properties
+ // Existing named policies take precedence over built-ins for upgrade
compatibility.
try {
Env env = Env.getCurrentEnv();
- if (env == null || env.getIndexPolicyMgr() == null) {
- return name;
- }
-
- IndexPolicy policy = env.getIndexPolicyMgr().getPolicyByName(name);
- if (policy == null || policy.getType() != expectedType) {
- return name;
- }
-
- Map<String, String> props = policy.getProperties();
- if (props == null || props.isEmpty()) {
- return name;
+ if (env != null && env.getIndexPolicyMgr() != null) {
+ IndexPolicy policy =
env.getIndexPolicyMgr().getPolicyByName(name);
+ if (policy != null && policy.getType() == expectedType) {
+ Map<String, String> props = policy.getProperties();
+ if (props != null && !props.isEmpty()) {
+ TreeMap<String, String> sortedProps = new
TreeMap<>(props);
+ return sortedProps.toString();
+ }
+ }
}
-
- // Build identity from sorted properties
- TreeMap<String, String> sortedProps = new TreeMap<>(props);
- return sortedProps.toString();
} catch (RuntimeException e) {
- return name;
+ // Fall through to built-in resolution or the original name.
+ }
+
+ String normalizedName = name.toLowerCase(Locale.ROOT);
Review Comment:
Fixed in c5976f7cff0. AnalyzerIdentityBuilder now canonicalizes built-in
tokenizer, token-filter, and char-filter names through one Locale.ROOT helper.
The identity test covers PINYIN -> pinyin and ICU_NORMALIZER -> icu_normalizer;
the combined FE test run passes all 29 tests.
##########
be/src/storage/index/inverted/analyzer/ik/IKTokenizer.cpp:
##########
@@ -31,27 +66,55 @@ Token* IKTokenizer::next(Token* token) {
return nullptr;
}
- std::string& token_text = tokens_text_[buffer_index_++];
+ TokenData& token_data = tokens_[buffer_index_++];
// full-width to half-width, and lowercase
// TODO(ryan19929): do regularizeString in fillBuffer.
- CharacterUtil::regularizeString(token_text, this->lowercase);
- size_t size = std::min(token_text.size(),
static_cast<size_t>(LUCENE_MAX_WORD_LEN));
- token->setNoCopy(token_text.data(), 0, static_cast<int32_t>(size));
+ CharacterUtil::regularizeString(token_data.text, this->lowercase);
+ current_token_ = &token_data;
+ size_t size = std::min(token_data.text.size(),
static_cast<size_t>(LUCENE_MAX_WORD_LEN));
+ set(token, std::string_view(token_data.text.data(), size));
+ token->setStartOffset(token_data.start_offset);
+ token->setEndOffset(token_data.end_offset);
return token;
}
+std::span<const int32_t> IKTokenizer::get_source_byte_offsets(std::string_view
term) const {
+ if (current_token_ == nullptr || term != current_token_->text) {
+ return {};
+ }
+ return current_token_->source_byte_offsets;
+}
+
+void IKTokenizer::reset() {
+ if (_in_pending == nullptr) {
+ return;
+ }
+ inverted_index::DorisTokenizer::reset();
+ _in_pending.reset();
+ reset(_in.get());
+}
+
void IKTokenizer::reset(lucene::util::Reader* reader) {
+ _in_pending.reset();
this->input = reader;
this->buffer_index_ = 0;
this->data_length_ = 0;
- this->tokens_text_.clear();
+ this->tokens_.clear();
+ this->current_token_ = nullptr;
try {
buffer_.reserve(input->size());
ik_segmenter_->reset(reader);
Lexeme lexeme;
while (ik_segmenter_->next(lexeme)) {
- tokens_text_.emplace_back(lexeme.getText());
+ TokenData token_data {
+ .text = lexeme.getText(),
+ .start_offset =
static_cast<int32_t>(lexeme.getByteBeginPosition()),
+ .end_offset =
static_cast<int32_t>(lexeme.getByteEndPosition()),
+ .source_byte_offsets = {}};
+ token_data.source_byte_offsets =
Review Comment:
Fixed in 441846bb324c2772c5382845cd6bc3df2c6c524a. PinyinFilterFactory now
opts the upstream chain into provenance only when ignore_pinyin_offset=false.
IK fuses normalization and source-boundary collection in one pass only when
enabled, retains one current-token vector, and no longer stores a vector in
every eagerly buffered token. TestIKSourceOffsetsAreOptIn verifies the ordinary
IK path has no map and the consuming chain enables it. The final affected ASAN
run passed all 113 tests.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -152,35 +182,49 @@ private static String resolveComponentIdentity(String
name, IndexPolicyTypeEnum
return "";
}
- // Check if it's a built-in component
- if (expectedType == IndexPolicyTypeEnum.TOKENIZER
- && IndexPolicy.BUILTIN_TOKENIZERS.contains(name)) {
- return name;
- }
-
- // For custom component, get its properties
+ // Existing named policies take precedence over built-ins for upgrade
compatibility.
try {
Env env = Env.getCurrentEnv();
- if (env == null || env.getIndexPolicyMgr() == null) {
- return name;
- }
-
- IndexPolicy policy = env.getIndexPolicyMgr().getPolicyByName(name);
- if (policy == null || policy.getType() != expectedType) {
- return name;
+ if (env != null && env.getIndexPolicyMgr() != null) {
+ IndexPolicy policy =
env.getIndexPolicyMgr().getPolicyByName(name);
+ if (policy != null && policy.getType() == expectedType) {
+ Map<String, String> props = policy.getProperties();
+ if (props != null && !props.isEmpty()) {
+ TreeMap<String, String> sortedProps = new
TreeMap<>(props);
+ String type = sortedProps.get(IndexPolicy.PROP_TYPE);
+ String normalizedType =
normalizeBuiltinComponentName(type, expectedType);
+ if (normalizedType != null) {
+ if (sortedProps.size() == 1) {
+ return normalizedType;
+ }
+ sortedProps.put(IndexPolicy.PROP_TYPE,
normalizedType);
+ }
+ return sortedProps.toString();
+ }
+ }
}
+ } catch (RuntimeException e) {
+ // Fall through to built-in resolution or the original name.
+ }
- Map<String, String> props = policy.getProperties();
- if (props == null || props.isEmpty()) {
- return name;
- }
+ String normalizedName = normalizeBuiltinComponentName(name,
expectedType);
+ return normalizedName == null ? name : normalizedName;
+ }
- // Build identity from sorted properties
- TreeMap<String, String> sortedProps = new TreeMap<>(props);
- return sortedProps.toString();
- } catch (RuntimeException e) {
- return name;
+ private static String normalizeBuiltinComponentName(String name,
IndexPolicyTypeEnum expectedType) {
+ if (Strings.isNullOrEmpty(name)) {
+ return null;
+ }
+ String normalizedName = name.toLowerCase(Locale.ROOT);
Review Comment:
Fixed in 74488270e32. Built-in component normalization now trims the
reference before Locale.ROOT lowercasing. The focused unit test covers a padded
IK_SMART reference, and the regression suite verifies both CREATE and ALTER
duplicate rejection against the equivalent unpadded analyzer.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -152,35 +153,49 @@ private static String resolveComponentIdentity(String
name, IndexPolicyTypeEnum
return "";
}
- // Check if it's a built-in component
- if (expectedType == IndexPolicyTypeEnum.TOKENIZER
- && IndexPolicy.BUILTIN_TOKENIZERS.contains(name)) {
- return name;
- }
-
- // For custom component, get its properties
+ // Existing named policies take precedence over built-ins for upgrade
compatibility.
try {
Env env = Env.getCurrentEnv();
- if (env == null || env.getIndexPolicyMgr() == null) {
- return name;
- }
-
- IndexPolicy policy = env.getIndexPolicyMgr().getPolicyByName(name);
- if (policy == null || policy.getType() != expectedType) {
- return name;
+ if (env != null && env.getIndexPolicyMgr() != null) {
+ IndexPolicy policy =
env.getIndexPolicyMgr().getPolicyByName(name);
+ if (policy != null && policy.getType() == expectedType) {
+ Map<String, String> props = policy.getProperties();
+ if (props != null && !props.isEmpty()) {
+ TreeMap<String, String> sortedProps = new
TreeMap<>(props);
+ String type = sortedProps.get(IndexPolicy.PROP_TYPE);
+ String normalizedType =
normalizeBuiltinComponentName(type, expectedType);
+ if (normalizedType != null) {
+ if (sortedProps.size() == 1) {
+ return normalizedType;
+ }
+ sortedProps.put(IndexPolicy.PROP_TYPE,
normalizedType);
+ }
+ return sortedProps.toString();
+ }
+ }
}
+ } catch (RuntimeException e) {
+ // Fall through to built-in resolution or the original name.
+ }
- Map<String, String> props = policy.getProperties();
- if (props == null || props.isEmpty()) {
- return name;
- }
+ String normalizedName = normalizeBuiltinComponentName(name,
expectedType);
+ return normalizedName == null ? name : normalizedName;
Review Comment:
Fixed in 0e66fd9522552abd9efa2bb685db6970859b19d1. Unfiltered legacy IK now
resolves smart (including the omitted-mode default) and max-word modes to the
same semantic identities as custom analyzers using ik_smart and ik_max_word. A
focused FE unit test covers both modes plus the filtered-legacy distinction
(9/9 passed), and the isolated regression suite now verifies duplicate
rejection in both CREATE TABLE and ALTER TABLE paths; the full suite passed.
##########
be/src/storage/index/inverted/token_filter/token_filter.h:
##########
@@ -28,9 +28,15 @@ class DorisTokenFilter : public TokenFilter, public
DorisTokenStream {
void reset() override { _in->reset(); }
+ std::span<const int32_t> get_source_byte_offsets(std::string_view term)
const override {
Review Comment:
Fixed in 441846bb324c2772c5382845cd6bc3df2c6c524a. Source-byte provenance is
now attached to the current token, and WordDelimiterFilter slices or composes
it for generated parts and concatenations before PinyinFilter consumes it.
TestWordDelimiterPreservesIKSourceOffsets covers full-width LIUDE123 through
ik_smart, word_delimiter, and pinyin and verifies liu [0,9), de [9,15), and 123
[15,24). The final affected ASAN run passed all 113 tests.
##########
fe/fe-core/src/main/java/org/apache/doris/indexpolicy/IndexPolicyMgr.java:
##########
@@ -373,6 +374,10 @@ private void validateTokenizerProperties(Map<String,
String> properties) throws
case "basic":
validator = new BasicTokenizerValidator();
Review Comment:
Fixed in c5976f7cff0. AnalyzerIdentityBuilder canonicalizes a named policy's
built-in type and collapses a type-only wrapper to the direct factory identity,
while retaining named-policy precedence for migration compatibility. The new
test verifies named {type=ik_smart} equals direct ik_smart. Both CREATE and
ALTER duplicate-index checks consume this shared identity builder.
##########
be/src/storage/index/inverted/tokenizer/ik/ik_tokenizer_factory.h:
##########
@@ -0,0 +1,49 @@
+// 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 "common/config.h"
+#include "storage/index/inverted/analyzer/ik/IKTokenizer.h"
+#include "storage/index/inverted/analyzer/ik/dic/Dictionary.h"
+#include "storage/index/inverted/tokenizer/tokenizer_factory.h"
+
+namespace doris::segment_v2::inverted_index {
+
+class IKTokenizerFactory : public TokenizerFactory {
+public:
+ explicit IKTokenizerFactory(bool use_smart) : _use_smart(use_smart) {}
+ ~IKTokenizerFactory() override = default;
+
+ void initialize(const Settings& settings) override {}
+
+ TokenizerPtr create() override {
+ auto ik_config = std::make_shared<Configuration>(_use_smart, true);
+ ik_config->setDictPath(config::inverted_index_dict_path + "/ik");
+ Dictionary::initial(*ik_config);
Review Comment:
Fixed in 441846bb324c2772c5382845cd6bc3df2c6c524a. CustomAnalyzer now
translates CLuceneError while creating the lazy tokenizer/filter chain into
INVERTED_INDEX_ANALYZER_ERROR. IKTokenizerTest.TestDictionaryExceptionHandling
exercises a named ik_smart analyzer on its first use with a missing dictionary
and verifies that a Doris analyzer exception, rather than raw CLuceneError,
crosses the public token-stream boundary. The final affected ASAN run passed
all 113 tests.
##########
be/src/storage/index/inverted/analyzer/ik/IKTokenizer.cpp:
##########
@@ -31,27 +68,57 @@ Token* IKTokenizer::next(Token* token) {
return nullptr;
}
- std::string& token_text = tokens_text_[buffer_index_++];
+ TokenData& token_data = tokens_[buffer_index_++];
// full-width to half-width, and lowercase
// TODO(ryan19929): do regularizeString in fillBuffer.
- CharacterUtil::regularizeString(token_text, this->lowercase);
- size_t size = std::min(token_text.size(),
static_cast<size_t>(LUCENE_MAX_WORD_LEN));
- token->setNoCopy(token_text.data(), 0, static_cast<int32_t>(size));
+ if (source_byte_offsets_enabled_) {
+ current_source_byte_offsets_ =
+ regularize_with_source_byte_offsets(token_data.text,
this->lowercase);
+ } else {
+ CharacterUtil::regularizeString(token_data.text, this->lowercase);
+ current_source_byte_offsets_.clear();
+ }
+ current_token_ = &token_data;
+ size_t size = std::min(token_data.text.size(),
static_cast<size_t>(LUCENE_MAX_WORD_LEN));
Review Comment:
Fixed in 12d0c12afd44719995027609346fc73f3ce83590. A discriminating ASAN
test using repeated full-width LIUDE reproduced the fallback first-token range
[0,3) instead of the source range [0,9). IKTokenizer now chooses a UTF-8-safe
published prefix at or below the Lucene byte limit, truncates provenance to the
same rune boundary, and sets the clipped token end offset to that exclusive
source boundary. The test also resets and reuses the tokenizer. The final
affected ASAN run passed all 82 tests, the full ASAN BE build passed, and
changed-line clang-tidy passed.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -50,9 +52,32 @@ public static String buildAnalyzerIdentity(
if (Strings.isNullOrEmpty(parser) ||
parserNone.equalsIgnoreCase(parser)) {
return defaultAnalyzerKey;
}
+ String legacyIkIdentity = resolveLegacyIkIdentity(properties, parser);
+ if (legacyIkIdentity != null) {
+ return legacyIkIdentity;
+ }
return parser;
}
+ private static String resolveLegacyIkIdentity(Map<String, String>
properties, String parser) {
+ if
(!InvertedIndexProperties.INVERTED_INDEX_PARSER_IK.equalsIgnoreCase(parser)
+ || !Strings.isNullOrEmpty(properties.get(
+
InvertedIndexProperties.INVERTED_INDEX_PARSER_CHAR_FILTER_TYPE))) {
+ return null;
+ }
+
+ String mode =
properties.get(InvertedIndexProperties.INVERTED_INDEX_PARSER_MODE_KEY);
+ if (Strings.isNullOrEmpty(mode)) {
+ mode = InvertedIndexProperties.INVERTED_INDEX_PARSER_SMART;
+ }
+ String tokenizer = normalizeBuiltinComponentName(mode,
IndexPolicyTypeEnum.TOKENIZER);
+ if (!"ik_smart".equals(tokenizer) && !"ik_max_word".equals(tokenizer))
{
+ return null;
+ }
+ return buildIdentityFromPolicyProperties(
Review Comment:
Fixed in 441846bb324c2772c5382845cd6bc3df2c6c524a. Legacy IK collapses to a
custom identity only when lower_case is absent or true, and its synthetic
identity now names the literal built-in mode instead of resolving a shadowing
policy. AnalyzerIdentityBuilderTest covers lower_case=false and a replayed
ik_smart policy whose type is standard; the focused FE run passed all 10 tests.
##########
be/src/storage/index/inverted/token_filter/pinyin_filter.cpp:
##########
@@ -421,8 +434,18 @@ void PinyinFilter::setTokenAttributes(Token* token, const
std::string& term, int
int end_offset, int position) {
set_text(token, term);
- token->setStartOffset(start_offset);
- token->setEndOffset(end_offset);
+ int absolute_start = current_start_offset_;
+ int absolute_end = current_end_offset_;
+ const bool is_whole_token =
+ start_offset == 0 && std::cmp_equal(end_offset,
current_source_.length());
+ if (!config_->ignorePinyinOffset && !is_whole_token && start_offset >= 0
&& end_offset > 0 &&
+ std::cmp_less(start_offset, current_runes_.size()) &&
+ std::cmp_less_equal(end_offset, current_runes_.size())) {
+ absolute_start += current_runes_[start_offset].byte_start;
Review Comment:
Fixed in 12d0c12afd44719995027609346fc73f3ce83590. A discriminating test
reproduced the reported connector gap in both ik_smart and ik_max_word:
full-width LIU-DE produced de [9,13) rather than [10,16). PinyinFilter now
retains the original source-rune index for every ASCII character kept in the
compact buffer and maps each alphabet-token span through those indices. The
final affected ASAN run passed all 82 tests, the full ASAN BE build passed, and
changed-line clang-tidy passed.
##########
fe/fe-core/src/main/java/org/apache/doris/indexpolicy/IndexPolicyMgr.java:
##########
@@ -80,6 +81,36 @@ private void readUnlock() {
lock.readLock().unlock();
}
+ // Legacy metadata may contain names that collide after locale-independent
normalization.
+ // Policy IDs are allocated monotonically, so the higher ID reproduces the
latest definition.
+ // Callers must hold the write lock.
+ private void registerPolicyNameLocked(IndexPolicy indexPolicy) {
+ String normalizedName = normalizeKey(indexPolicy.getName());
+ IndexPolicy current = nameToIndexPolicy.get(normalizedName);
+ if (current == null || indexPolicy.getId() > current.getId()) {
+ nameToIndexPolicy.put(normalizedName, indexPolicy);
Review Comment:
Fixed in 8995f270. FE now transmits only the authoritative normalized-name
winner. BE retains colliders but deterministically selects the highest policy
ID for both arrival orders; deleting the winner clears only that authoritative
entry and restores the highest survivor. Added FE winner-only/restoration
coverage and BE both-arrival, drop, and winner-restoration coverage. Verified
with the focused ASAN BE suite (53/53), FE tests (36/36), and the isolated
regression suite twice.
##########
fe/fe-core/src/main/java/org/apache/doris/indexpolicy/IndexPolicyMgr.java:
##########
@@ -61,7 +62,7 @@ public class IndexPolicyMgr implements Writable,
GsonPostProcessable {
* Policy names are case-insensitive in Doris.
*/
private static String normalizeKey(String name) {
- return name == null ? null : name.trim().toLowerCase();
+ return name == null ? null : name.trim().toLowerCase(Locale.ROOT);
Review Comment:
Fixed in 74488270e32. Policy-name registration and removal are now
centralized under the write lock. Legacy normalized-name collisions
deterministically select the highest policy ID, dropping a hidden older policy
preserves the current mapping, and dropping the current policy restores the
newest survivor. Replay tests cover both drop orders, and a serialized round
trip with colliding HashMap buckets verifies deterministic image reconstruction.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -43,16 +45,64 @@ public static String buildAnalyzerIdentity(
}
if (!Strings.isNullOrEmpty(preferredAnalyzer)) {
+ String builtinIkIdentity =
resolveBuiltinIkAnalyzerIdentity(properties, preferredAnalyzer);
+ if (builtinIkIdentity != null) {
+ return builtinIkIdentity;
+ }
// For custom analyzer/normalizer, resolve to underlying config to
build identity
return resolveAnalyzerIdentity(preferredAnalyzer,
defaultAnalyzerKey, log);
Review Comment:
Fixed in 8995f270. Named and custom analyzer identities now include a
length-prefixed outer character-replacement type, pattern, and replacement,
including the legacy parser fallback. Regression coverage keeps named IK with a
replacement filter distinct from the matching legacy IK form through both
CREATE and ALTER. Verified with the focused ASAN BE suite (53/53), FE tests
(36/36), and the isolated regression suite twice.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -50,9 +52,37 @@ public static String buildAnalyzerIdentity(
if (Strings.isNullOrEmpty(parser) ||
parserNone.equalsIgnoreCase(parser)) {
return defaultAnalyzerKey;
}
+ String legacyIkIdentity = resolveLegacyIkIdentity(properties, parser);
Review Comment:
Fixed in 74488270e32. The identity builder now matches the BE runtime
default: analyzer=ik uses max-word mode. It shares the synthetic ik_max_word
identity only when no character filter is configured and lower_case is absent
or true; negative tests keep character-filter and lower_case=false
configurations distinct. CREATE and ALTER regression cases cover the equivalent
custom analyzer.
##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -230,11 +290,7 @@ private static String resolveCharFilterIdentity(String
filterList) {
sb.append(",");
}
- if (IndexPolicy.BUILTIN_CHAR_FILTERS.contains(filter)) {
- sb.append(filter);
- } else {
- sb.append(resolveComponentIdentity(filter,
IndexPolicyTypeEnum.CHAR_FILTER));
- }
+ sb.append(resolveComponentIdentity(filter,
IndexPolicyTypeEnum.CHAR_FILTER));
Review Comment:
Fixed in 8995f270. AnalyzerIdentityBuilder now omits built-in empty filters
and named filters whose normalized type is empty before writing identity keys
or delimiters. The unit tests and the custom-analyzer regression suite cover
duplicate rejection through both CREATE and ALTER. Verified with the focused
ASAN BE suite (53/53), FE tests (36/36), and the isolated regression suite
twice.
##########
be/src/storage/index/inverted/analyzer/ik/IKTokenizer.cpp:
##########
@@ -31,27 +88,70 @@ Token* IKTokenizer::next(Token* token) {
return nullptr;
}
- std::string& token_text = tokens_text_[buffer_index_++];
+ TokenData& token_data = tokens_[buffer_index_++];
// full-width to half-width, and lowercase
// TODO(ryan19929): do regularizeString in fillBuffer.
- CharacterUtil::regularizeString(token_text, this->lowercase);
- size_t size = std::min(token_text.size(),
static_cast<size_t>(LUCENE_MAX_WORD_LEN));
- token->setNoCopy(token_text.data(), 0, static_cast<int32_t>(size));
+ if (source_byte_offsets_enabled_) {
Review Comment:
Fixed in 8995f270. DorisCharFilter now exposes compositional source-offset
correction; the ICU normalizer maps normalized byte boundaries back to its
source and IK applies that nested correction to propagated rune offsets. The
Pinyin test covers the actual ICU nfkc_cf to IK smart to Pinyin chain with
full-width LIUDE input and reset/reuse, preserving original spans [0,9) and
[9,15). Verified with the focused ASAN BE suite (53/53) and the isolated
regression suite twice.
##########
be/src/storage/index/inverted/token_filter/pinyin_filter.cpp:
##########
@@ -421,8 +423,18 @@ void PinyinFilter::setTokenAttributes(Token* token, const
std::string& term, int
int end_offset, int position) {
set_text(token, term);
- token->setStartOffset(start_offset);
- token->setEndOffset(end_offset);
+ int absolute_start = current_start_offset_;
+ int absolute_end = current_end_offset_;
+ const bool is_whole_token =
+ start_offset == 0 && std::cmp_equal(end_offset,
current_source_.length());
Review Comment:
Fixed in c5976f7cff0. IKTokenizer records original UTF-8 rune boundaries
before regularization when normalization changes byte width, and Doris token
filters forward that mapping to PinyinFilter.
TestIKOffsetsPreserveFullwidthSourceBytes verifies that fullwidth LIUDE splits
to liu [0,9) and de [9,15); the final IKTokenizerTest plus PinyinFilterTest
ASAN run passes all 62 tests.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]