[analytics-engine] Add json_valid Rust UDF to the DataFusion backend - #22139
Conversation
PR Reviewer Guide 🔍(Review updated until commit cf0a4de)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to cf0a4de Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 01134b0
Suggestions up to commit d834702
|
d834702 to
01134b0
Compare
|
Persistent review updated to latest commit 01134b0 |
Wires PPL json_valid through the analytics-engine route (PPL -> Calcite -> Substrait -> DataFusion): ScalarFunction.JSON_VALID enum, JsonValidAdapter (rewrites Calcite IS_JSON_VALUE -> local json_valid op), project + filter op registration, substrait FunctionMapping, yaml signature, and the Rust UDF. json_valid is registered as a FILTER op (not just PROJECT) so it works as a WHERE predicate (e.g. `where json_valid(col)` / `where not json_valid(col)`), same shape as cidrmatch. Semantics match the legacy SQL-plugin JsonUtils.isValidJson (Jackson readTree): malformed -> false, NULL -> NULL, and empty/whitespace -> true (Jackson returns MissingNode without throwing; serde rejects empty input, so is_valid_json special-cases it to preserve parity with the JsonFunctionsIT fixture). Complements opensearch-project#22130 (json/json_object/json_array); json_valid is independent. Rust unit tests 10/10, Java adapter tests 9/9. Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
01134b0 to
cf0a4de
Compare
|
Persistent review updated to latest commit cf0a4de |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #22139 +/- ##
============================================
- Coverage 73.37% 73.31% -0.06%
+ Complexity 75836 75816 -20
============================================
Files 6064 6064
Lines 344498 344498
Branches 49575 49575
============================================
- Hits 252773 252585 -188
- Misses 71561 71762 +201
+ Partials 20164 20151 -13 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…pensearch-project#22139) Wires PPL json_valid through the analytics-engine route (PPL -> Calcite -> Substrait -> DataFusion): ScalarFunction.JSON_VALID enum, JsonValidAdapter (rewrites Calcite IS_JSON_VALUE -> local json_valid op), project + filter op registration, substrait FunctionMapping, yaml signature, and the Rust UDF. json_valid is registered as a FILTER op (not just PROJECT) so it works as a WHERE predicate (e.g. `where json_valid(col)` / `where not json_valid(col)`), same shape as cidrmatch. Semantics match the legacy SQL-plugin JsonUtils.isValidJson (Jackson readTree): malformed -> false, NULL -> NULL, and empty/whitespace -> true (Jackson returns MissingNode without throwing; serde rejects empty input, so is_valid_json special-cases it to preserve parity with the JsonFunctionsIT fixture). Complements opensearch-project#22130 (json/json_object/json_array); json_valid is independent. Rust unit tests 10/10, Java adapter tests 9/9. Signed-off-by: Eric Wei <mengwei.eric@gmail.com>
Description
Adds the PPL
json_validscalar function to the analytics-engine (DataFusion) route, sojson_valid(...)runs on parquet-backed indices instead of failing withNo backend supports scalar function [JSON_VALID].Wires it through the full chain:
ScalarFunction.JSON_VALIDenum,JsonValidAdapter(rewrites Calcite'sIS_JSON_VALUEpostfix operator → a localjson_validop), the project- and filter-op registries, the substraitFunctionMapping, theopensearch_scalar_functions.yamlsignature, and the Rust UDF (rust/src/udf/json_valid.rs).Complements #22130 (which adds
json/json_object/json_array);json_validis independent and not covered there.Semantics
Match the legacy SQL-plugin
JsonUtils.isValidJson(JacksonObjectMapper.readTree):truefalsefalse(not NULL) — legacy returnsLITERAL_FALSEfor null/missing, sowhere not json_valid(col)includes NULL rows (matchesJsonFunctionsIT.test_not_json_valid).true— JacksonreadTree("")returns aMissingNodewithout throwing, so the legacy function and theJsonFunctionsIT"json empty string" fixture row treat it as valid.serde_json::from_strrejects empty input, sois_valid_jsonspecial-cases empty/whitespace to preserve parity.Filter support
json_validreturns BOOLEAN, so it is registered as a filter op (not only project), letting it work as a WHERE predicate —where json_valid(col)/where not json_valid(col)— same shape ascidrmatch.Testing
Unit
JsonFunctionAdaptersTests): 9/9 — rewrite-to-local-op + original-return-type preservation.ScalarFunctionTests: 19/19 — incl. a new test pinning the production resolver pathSqlStdOperatorTable.IS_JSON_VALUE(SqlKind.OTHER) → reference-operator identity →ScalarFunction.JSON_VALID.End-to-end (force-routed analytics-engine cluster,
-Dtests.analytics.parquet_indices=true, AE route confirmed via_explain) — all green:JsonFunctionsIT.test_json_valid✅ —where json_valid(json_string)over the full json_test dataset (incl. empty-string → valid).JsonFunctionsIT.test_not_json_valid✅ —where not json_valid(...)(incl. NULL → false, so NULL rows are returned).CalcitePPLJsonBuiltinFunctionIT.testJsonValid✅ — valid/malformed projection.The empty-string and NULL behaviors were also confirmed live on the rebuilt cluster (
where not json_validreturns the NULL and malformed rows; empty-string excluded as valid), proving the Calcite → Substrait → DataFusion chain preserves the legacy Jackson contract.Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.