airborne12 commented on code in PR #67918:
URL: https://github.com/apache/doris/pull/67918#discussion_r4068728241


##########
be/src/storage/index/inverted/tokenizer/tokenizer.h:
##########
@@ -39,12 +45,148 @@ class DorisTokenizer : public Tokenizer, public 
DorisTokenStream {
 
     using Tokenizer::reset;
     // Only use the parameterless reset method
-    void reset() override { _in = _in_pending; };
+    void reset() override {
+        _in = _in_pending;
+        _source_byte_offsets.clear();
+        _source_byte_end_offsets.clear();
+        release_oversized_scratch(_source_byte_offsets);
+        release_oversized_scratch(_source_byte_end_offsets);
+    };
+
+    std::span<const int32_t> get_source_byte_offsets() const override {
+        return _source_byte_offsets_enabled ? std::span<const int32_t> 
{_source_byte_offsets}
+                                            : std::span<const int32_t> {};
+    }
+
+    std::span<const int32_t> get_source_byte_end_offsets() const override {
+        return _source_byte_offsets_enabled ? std::span<const int32_t> 
{_source_byte_end_offsets}
+                                            : std::span<const int32_t> {};
+    }
+
+    void set_source_byte_offsets_enabled(bool enabled) override {
+        _source_byte_offsets_enabled = enabled;
+    }
+
+    size_t source_byte_offsets_capacity_for_test() const {
+        return _source_byte_offsets.capacity() + 
_source_byte_end_offsets.capacity();
+    }
 
 protected:
+    int32_t correct_source_offset(int32_t offset) const {
+        const auto* char_filter = dynamic_cast<const 
DorisCharFilter*>(_in.get());
+        return char_filter == nullptr ? offset : 
char_filter->correct_offset(offset);
+    }
+
+    void set_source_byte_offsets(std::string_view term, int32_t source_start) {
+        set_source_byte_offsets(term, term, source_start);
+    }
+
+    void set_source_byte_offsets(std::string_view term, std::string_view 
source,
+                                 int32_t source_start) {
+        _source_byte_offsets.clear();
+        _source_byte_end_offsets.clear();
+        if (!_source_byte_offsets_enabled) {
+            return;
+        }
+
+        const auto* char_filter = dynamic_cast<const 
DorisCharFilter*>(_in.get());
+        const int32_t corrected_start =
+                char_filter == nullptr ? source_start : 
char_filter->correct_offset(source_start);
+        std::vector<int32_t> source_offsets {0};

Review Comment:
   Fixed in db18af01772. Reproduced first: with offsets enabled, a 26-rune 
token followed by two-rune tokens left the tokenizer's published capacity at 32 
and then 4, i.e. every token re-grew a fresh local vector and dropped the 
previous buffer (same for IK, which moved its per-token vector into the publish 
path).
   
   `DorisTokenizer` now builds boundaries into a reusable 
`_source_offsets_scratch` and `publish_source_byte_offsets()` swaps it with the 
published vector in the strictly-increasing case (the widened case fills the 
published vectors in place), so after two tokens every following token lands in 
one of the two warmed buffers without allocating. IK's 
`regularize_with_source_byte_offsets()` writes into its own reusable vector and 
publishes through the same swap. The oversized-reset high-water release now 
also covers the scratch vectors. 
`TestOffsetTrackingReusesTokenizerScratchAcrossTokens` asserts the alternating 
steady state for the standard and IK tokenizers, and the existing ASAN 
allocation bound for a 4 MiB token still holds.



##########
fe/fe-core/src/main/java/org/apache/doris/analysis/invertedindex/AnalyzerIdentityBuilder.java:
##########
@@ -226,26 +671,115 @@ private static String resolveTokenFilterIdentity(String 
filterList) {
      * IMPORTANT: Order is preserved because filter order is semantically 
significant.
      */
     private static String resolveCharFilterIdentity(String filterList) {
+        return resolveCharFilterIdentity(filterList, false);
+    }
+
+    private static String resolveCharFilterIdentity(String filterList, boolean 
lowercaseDownstream) {
         if (Strings.isNullOrEmpty(filterList)) {
             return "";
         }
 
-        StringBuilder sb = new StringBuilder();
+        ArrayDeque<String> identities = new ArrayDeque<>();
         String[] filters = filterList.split(",\\s*");
         // DO NOT sort - filter order is semantically significant
 
-        for (int i = 0; i < filters.length; i++) {
-            String filter = filters[i].trim();
-            if (i > 0) {
-                sb.append(",");
+        for (int i = filters.length - 1; i >= 0; --i) {
+            String filterName = filters[i].trim();
+            String filter = resolveComponentIdentity(
+                    filterName, IndexPolicyTypeEnum.CHAR_FILTER, 
lowercaseDownstream);
+            if (Strings.isNullOrEmpty(filter)) {
+                continue;
             }
+            identities.addFirst(filter);
+            lowercaseDownstream = isCaseFoldingCharFilter(filterName);

Review Comment:
   Fixed in f5d7670ec2f. Reproduced first: `lower_a,x_to_y,fold` still carried 
`{pattern=A, replacement=a, ...}` in its identity while `x_to_y,fold` did not, 
so both CREATE (`canHaveMultipleInvertedIndexes` returned true) and ALTER 
admitted the pair.
   
   The reverse walk now carries a fold context instead of a boolean: after a 
case-folding filter the context is empty, a `char_replace` filter in between 
adds the bytes it rewrites (its pattern), and any other filter ends the 
context. `canonicalizeCharReplacePattern` drops `X -> x` only when neither `X` 
nor `x` is rewritten between that filter and the fold, which is exactly when 
the fold makes the mapping unobservable. 
`testCaseFoldCarriesThroughNonInteractingCharReplace` covers the intervening 
`x_to_y` case, keeps `lower_a,a_to_b,fold` distinct from `a_to_b,fold`, and 
adds an `A -> z` interacting negative; the CREATE and ALTER duplicate cases are 
in `InvertedIndexPropertiesTest` and `SchemaChangeHandlerTest`.



-- 
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]

Reply via email to