Jackie-Jiang commented on code in PR #18756:
URL: https://github.com/apache/pinot/pull/18756#discussion_r4253123214


##########
pinot-spi/src/main/java/org/apache/pinot/spi/utils/JsonUtils.java:
##########
@@ -783,6 +792,150 @@ public static List<Map<String, String>> flatten(String 
jsonString, JsonIndexConf
     }
   }
 
+  /// Flattens an already-parsed JSON value ({@link Map} / {@link List} / 
{@link JsonNode}) for the JSON index, avoiding
+  /// the string tokenization that {@link #flatten(String, JsonIndexConfig)} 
performs. Used by the realtime JSON index
+  /// when the source value is already a parsed object (e.g. cached on the 
`GenericRow` before it is serialized to a
+  /// string for the forward index), so the document is parsed once at 
ingestion instead of being serialized and
+  /// re-parsed here. The result matches flattening the forward value: Maps 
use {@link MapUtils#toString(Map)}, while
+  /// Lists and JsonNodes use {@link #objectToString(Object)}. A String input 
uses the existing string path.
+  public static List<Map<String, String>> flattenParsed(Object jsonValue, 
JsonIndexConfig jsonIndexConfig)
+      throws IOException {
+    if (jsonValue instanceof String) {
+      return flatten((String) jsonValue, jsonIndexConfig);
+    }
+    if (!canFlattenParsedValue(jsonValue)) {
+      // Ingestion excludes these values from the cache, so its index reuses 
the existing forward string.
+      return flatten(jsonValue instanceof Map ? MapUtils.toString((Map<String, 
Object>) jsonValue)
+          : objectToString(jsonValue), jsonIndexConfig);
+    }
+    JsonNode jsonNode = jsonValue instanceof JsonNode ? (JsonNode) jsonValue
+        : (jsonValue instanceof Map ? PARSED_JSON_MAP_MAPPER : 
PARSED_JSON_MAPPER).valueToTree(jsonValue);
+    int classification = classifyForFlatten(jsonNode);

Review Comment:
   [P3] Reuse the JsonNode classification instead of traversing twice
   
   For a JsonNode input, canFlattenParsedValue() at line 806 already calls 
classifyForFlatten() on the entire tree. This line immediately repeats the same 
classification. Text-derived cached JSON therefore incurs two identical 
full-document traversals per row; inputs already supplied as JsonNode also 
undergo the earlier transformer preflight.
   
   Please compute and reuse the classification for JsonNode inputs so the 
supported path avoids this redundant walk. The duplication is clear from the 
call path; its throughput impact has not been measured.



##########
pinot-spi/src/main/java/org/apache/pinot/spi/utils/JsonUtils.java:
##########
@@ -783,6 +792,150 @@ public static List<Map<String, String>> flatten(String 
jsonString, JsonIndexConf
     }
   }
 
+  /// Flattens an already-parsed JSON value ({@link Map} / {@link List} / 
{@link JsonNode}) for the JSON index, avoiding
+  /// the string tokenization that {@link #flatten(String, JsonIndexConfig)} 
performs. Used by the realtime JSON index
+  /// when the source value is already a parsed object (e.g. cached on the 
`GenericRow` before it is serialized to a
+  /// string for the forward index), so the document is parsed once at 
ingestion instead of being serialized and
+  /// re-parsed here. The result matches flattening the forward value: Maps 
use {@link MapUtils#toString(Map)}, while
+  /// Lists and JsonNodes use {@link #objectToString(Object)}. A String input 
uses the existing string path.
+  public static List<Map<String, String>> flattenParsed(Object jsonValue, 
JsonIndexConfig jsonIndexConfig)
+      throws IOException {
+    if (jsonValue instanceof String) {
+      return flatten((String) jsonValue, jsonIndexConfig);
+    }
+    if (!canFlattenParsedValue(jsonValue)) {
+      // Ingestion excludes these values from the cache, so its index reuses 
the existing forward string.
+      return flatten(jsonValue instanceof Map ? MapUtils.toString((Map<String, 
Object>) jsonValue)
+          : objectToString(jsonValue), jsonIndexConfig);
+    }
+    JsonNode jsonNode = jsonValue instanceof JsonNode ? (JsonNode) jsonValue
+        : (jsonValue instanceof Map ? PARSED_JSON_MAP_MAPPER : 
PARSED_JSON_MAPPER).valueToTree(jsonValue);

Review Comment:
   [P1] Preserve JSON parser limits on the cached path
   
   A supported Avro Map containing "ok":"v" and a 50,001-character String key 
passes canFlattenParsedValue() and valueToTree(), but parsing its serialized 
forward value throws StreamConstraintsException because Jackson's default 
name-length limit is 50,000. JSON fields default to NO_ACTION for length 
sanitization, so the parsed cache remains eligible. With skipInvalidJson=true, 
the consuming index matches ok='v', while the immutable index rebuilt from the 
same forward value skips the entire document. This changes query results after 
sealing.
   
   Please reject values exceeding the existing reader constraints from the 
cache and reuse the forward-string path. Add regression coverage comparing 
cached and immutable indexing with skipInvalidJson enabled. A bounded 
reproduction against Jackson 2.22.3 confirms that serialization/valueToTree 
succeed while readTree rejects this input.



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