xiangfu0 commented on code in PR #18758:
URL: https://github.com/apache/pinot/pull/18758#discussion_r4528990968


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/text/TextIndexType.java:
##########
@@ -103,8 +111,29 @@ protected ColumnConfigDeserializer<TextIndexConfig> 
createDeserializerForLegacyC
   @Override
   public TextIndexCreator createIndexCreator(IndexCreationContext context, 
TextIndexConfig indexConfig)
       throws IOException {
-    
Preconditions.checkState(context.getFieldSpec().getDataType().getStoredType() 
== FieldSpec.DataType.STRING,
+    
Preconditions.checkState(context.getFieldSpec().getDataType().getStoredType() 
== DataType.STRING,
         "Text index is currently only supported on STRING type columns");
+    if (indexConfig.isBuildOnDictionary()) {
+      Preconditions.checkState(context.getFieldSpec().isSingleValueField(),
+          "buildOnDictionary TEXT index requires a single-value column: %s", 
context.getFieldSpec().getName());
+      Preconditions.checkState(context.hasDictionary(),
+          "buildOnDictionary TEXT index requires a dictionary-encoded column: 
%s", context.getFieldSpec().getName());
+      LuceneTextIndexCreator creator = new LuceneTextIndexCreator(context, 
indexConfig);
+      // Columnar segment-creation / realtime-conversion path: build one 
Lucene document per distinct dictionary value
+      // (the stored id is the dictId). On the reload path ColumnStatistics is 
null and TextIndexHandler feeds the
+      // dictionary values via add(String) instead.
+      ColumnStatistics columnStatistics = context.getColumnStatistics();
+      if (columnStatistics != null) {
+        Object uniqueValues = columnStatistics.getUniqueValuesSet();

Review Comment:
   Added the cardinality check before opening the Lucene creator in 
[7680774f71](https://github.com/apache/pinot/commit/7680774f713912c9f7cac323451a1150b3f935f1).
 IndexCreationContext has no Dictionary handle; BaseSegmentCreator and the TEXT 
creator consume the same sorted statistics values, so their array offsets match 
dictionary IDs.
   
   A new negative case rejects mismatched cardinality. 
LuceneTextIndexCreatorTest (24 cases) and RealtimeSegmentConverterTest (227 
cases, including normal and compacted dictionary conversion) pass on JDK 25. 
All 575 focused tests, affected-module checks, and independent review pass.



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/filter/FilterOperatorUtils.java:
##########
@@ -86,21 +86,27 @@ public static boolean hasNulls(DataSource dataSource) {
     return getNullBitmap(dataSource) != null;
   }
 
+  static boolean isNullHandlingEnabled(QueryContext queryContext, 
PredicateEvaluator predicateEvaluator) {

Review Comment:
   Removed the TEXT_MATCH exception from the generic filters in 
[7680774f71](https://github.com/apache/pinot/commit/7680774f713912c9f7cac323451a1150b3f935f1).
 Dictionary-mode filters now honor ordinary query null handling; 
TextMatchFilterOperator remains unchanged for a separate follow-up, as 
requested.
   
   Updated the null regression with explicit dictionary/per-row expectations 
for positive, NOT, constant, mixed predicates, and selected row IDs. 
DictionaryBasedTextIndexQueriesTest (12 cases) and 
NullHandlingEnabledQueriesTest (159 cases) pass on JDK 25; all 575 focused 
tests and affected-module checks pass.



##########
pinot-core/src/main/java/org/apache/pinot/core/plan/FilterPlanNode.java:
##########
@@ -306,6 +308,26 @@ private BaseFilterOperator 
constructPhysicalOperator(FilterContext filter, int n
               Preconditions.checkState(textIndexReader != null,
                   "Cannot apply TEXT_MATCH on column: %s without text index", 
column);
 
+              if (textIndexReader.isBuildOnDictionary()) {
+                // Dictionary-based text index: search returns matching 
dictIds, resolved to docIds by the standard
+                // dictionary-based filter operators (inverted index / scan), 
exactly like FST/REGEXP_LIKE.
+                TextMatchPredicate textMatchPredicate = (TextMatchPredicate) 
predicate;
+                Preconditions.checkState(textMatchPredicate.getOptions() == 
null,

Review Comment:
   Implemented in 
[7680774f71](https://github.com/apache/pinot/commit/7680774f713912c9f7cac323451a1150b3f935f1):
 a backward-compatible getDictIds(query, options) overload forwards options to 
the existing Lucene query parser. Both filter and projection paths now pass 
options through.
   
   Mixed per-row/dictionary segment tests cover CLASSIC AND/OR, leading 
wildcards, and MATCHPHRASE with directory and packed storage. All 575 focused 
tests, affected-module checks, and independent review pass.



##########
pinot-core/src/main/java/org/apache/pinot/core/plan/FilterPlanNode.java:
##########
@@ -306,6 +308,26 @@ private BaseFilterOperator 
constructPhysicalOperator(FilterContext filter, int n
               Preconditions.checkState(textIndexReader != null,
                   "Cannot apply TEXT_MATCH on column: %s without text index", 
column);
 
+              if (textIndexReader.isBuildOnDictionary()) {
+                // Dictionary-based text index: search returns matching 
dictIds, resolved to docIds by the standard
+                // dictionary-based filter operators (inverted index / scan), 
exactly like FST/REGEXP_LIKE.
+                TextMatchPredicate textMatchPredicate = (TextMatchPredicate) 
predicate;
+                Preconditions.checkState(textMatchPredicate.getOptions() == 
null,
+                    "TEXT_MATCH options are not supported for a 
dictionary-based text index on column: %s", column);
+                predicateEvaluator = 
TextMatchDictIdPredicateEvaluatorFactory.newDictIdBasedEvaluator(
+                    textMatchPredicate, textIndexReader, 
dataSource.getDictionary());
+                BaseFilterOperator filterOperator =
+                    FilterOperatorUtils.getLeafFilterOperator(_queryContext, 
predicateEvaluator, dataSource, numDocs);
+                if (filterOperator instanceof ScanBasedFilterOperator && 
dataSource.getForwardIndex() != null

Review Comment:
   Changed in 
[7680774f71](https://github.com/apache/pinot/commit/7680774f713912c9f7cac323451a1150b3f935f1):
 the factory chooses dictionary or raw evaluation before constructing the leaf, 
using the existing per-query index eligibility helper. Constant matches retain 
their shortcuts. Raw scans and projections precompute a matching-string set, so 
there is no per-row dictionary.indexOf.
   
   This fallback is limited to raw forward indexes with a retained dictionary 
and no allowed dictionary-consuming index. Reload tests cover raw scans, 
inverted filtering, and skipIndexes forcing a scan; counts agree and 
scanned-entry checks confirm the selected path. All 575 focused tests pass on 
JDK 25.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/loader/invertedindex/TextIndexHandler.java:
##########
@@ -410,32 +421,43 @@ private void processMVField(SegmentDirectory.Writer 
segmentWriter, boolean hasDi
     }
   }
 
-  /// Checks if text index configuration has changed based on 
storeInSegmentFile field config flag.
-  ///
-  /// The method uses TextIndexUtils.hasTextIndex() to determine the current 
format and compare with desired format:
-  /// - If TextIndexUtils.hasTextIndex() returns true: means text index in 
directory format
-  /// - If TextIndexUtils.hasTextIndex() returns false means no text index in 
directory format
-  ///
-  /// @param columnName the column name to check
-  /// @param segmentReader the segment reader to access the segment
-  /// @return true if the text index configuration has changed and needs 
reprocessing, false otherwise
-  /// @throws Exception if there's an error checking the text index directory 
structure
+  /// Checks whether the persisted text-index storage format or build mode 
differs from the desired configuration.
   private boolean hasTextIndexConfigurationChanged(String columnName, 
SegmentDirectory.Reader segmentReader)
       throws Exception {
-    // Get current configuration
     TextIndexConfig currentConfig = 
_fieldIndexConfigs.get(columnName).getConfig(StandardIndexes.text());
-
-    // Check if current config expects combined format
-    boolean expectedFormat = currentConfig.isStoreInSegmentFile();
-
-    // Check if existing index is in combined format using TextIndexUtils
     File indexDir = _segmentDirectory.getSegmentMetadata().getIndexDir();
     File segmentDirectory =
         SegmentDirectoryPaths.segmentDirectoryFor(indexDir, 
_segmentDirectory.getSegmentMetadata().getVersion());
     boolean currentFormat = !TextIndexUtils.hasTextIndex(segmentDirectory, 
columnName);
+    if (currentConfig.isStoreInSegmentFile() != currentFormat) {
+      return true;
+    }
 
-    // If the expected format doesn't match the existing format, we need an 
update
-    return expectedFormat != currentFormat;
+    boolean buildOnDictionary = false;

Review Comment:
   Centralized property extraction and persisted build-mode parsing in 
TextIndexUtils for the handler and reader in 
[7680774f71](https://github.com/apache/pinot/commit/7680774f713912c9f7cac323451a1150b3f935f1);
 missing flags still mean per-row, and matching modes skip rebuilding. 
Reload/mode-flip tests pass for both storage formats.
   
   The initial persisted read is still necessary when the config is off: 
segment metadata has no build-mode flag, so skipping that read would miss 
dictionary-to-per-row rebuilds. This keeps downgrade/mode-flip safety without 
adding format metadata or a stale global cache. All 575 focused tests and 
affected-module checks pass.



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