Apache Spark SQL JSON_EXISTS 函数详解:SQL:2016 标准的 JSON 存在性谓词

发布时间:2026/9/20 13:38:10
Apache Spark SQL JSON_EXISTS 函数详解:SQL:2016 标准的 JSON 存在性谓词 Apache Spark SQL JSON_EXISTS 函数详解SQL:2016 标准的 JSON 存在性谓词【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址: https://gitcode.com/gh_mirrors/sp/sparkJSON_EXISTS 是 Apache Spark 实现的 SQL:2016 标准谓词特性 T821用于测试一条 SQL/JSON 路径是否在 JSON 文档中至少匹配到一个条目返回BOOLEAN。本文将以 docs/sql-ref-syntax-qry-select-json-exists.md 为主体结合 Spark 源码jsonExpressions.scala、JsonExpressionEvalUtils.scala、FunctionRegistry.scala等深入讲解其语法、lax 模式路径语义、ON ERROR行为与实现原理帮助你写出可迁移、可落地的 JSON 存在性查询。JSON_EXISTS 是什么JSON_EXISTS是一个 SQL 谓词它接受一个 JSON 文档表达式和一条 SQL/JSON 路径判断路径是否匹配到文档中至少一个条目并返回BOOLEAN路径匹配到至少一个条目包括匹配到的值是 JSONnull的情况→true路径没有匹配到任何条目 →false。它是 SQL 标准SQL:2016中测试 JSON 值是否存在的方式常用于把其他系统如 Oracle、DB2、PostgreSQL的查询迁移到 Spark。与 Spark 传统函数get_json_object(json_expr, path) IS NOT NULL不同JSON_EXISTS能区分路径存在但值是 JSONnull返回true和路径完全不存在返回false这两种情况——这正是它作为标准谓词的关键价值。语法JSON_EXISTS ( json_expr, path [ { TRUE | FALSE | UNKNOWN | ERROR } ON ERROR ] )其中json_expr必填求值结果为包含 JSON 文档的STRING表达式。path必填常量 SQL/JSON 路径字面量如$.a.b、$.tags[0]、$.a[*].b。{ TRUE | FALSE | UNKNOWN | ERROR } ON ERROR可选控制当输入不是单一格式良好的 JSON 值时返回什么默认是FALSE ON ERROR。参数详解json_expr一个求值结果为STRING的表达式包含要测试的 JSON 文档。当输入是 SQLNULL时结果固定为NULL即 SQL 的 UNKNOWN 三值逻辑无论ON ERROR子句如何设置都不受影响——这一点由实现强制保证见下文NULL 输入部分。path一条常量SQL/JSON 路径字面量。路径在lax宽松模式下求值与 Oracle 和 PostgreSQL 的行为一致支持数组通配符[*]和成员通配符.*/[*]数组会自动解包/包装auto-unwrap/auto-wrap对数组应用成员、下标或通配符步骤时会依次应用到每个元素非数组值会被视为单元素数组例如对[0]、[*]步骤而言结构性不匹配structural mismatch视为不匹配而非错误——例如读取不存在的键、对标量取值、数组下标越界、对空数组使用[*]语法上非法的路径会在分析阶段analysis就被拒绝不会等到运行时。{ TRUE | FALSE | UNKNOWN | ERROR } ON ERROR当json_expr不是单一格式良好的 JSON 值时即输入格式错误或有效值后跟了额外内容该子句控制结果选项行为TRUE ON ERROR返回trueFALSE ON ERROR返回false默认值UNKNOWN ON ERROR返回UNKNOWN即BOOLEAN类型的NULLERROR ON ERROR抛出错误返回值规则JSON_EXISTS的返回值遵循以下完整规则输入情况结果路径匹配到至少一个条目包括值为 JSONnulltrue路径没有匹配到任何条目falsejson_expr是 SQLNULLNULLUNKNOWNjson_expr不是单一格式良好的 JSON 值按ON ERROR行为处理默认false需要再次强调结构性不匹配被当作无匹配false而不是错误。例如读取不存在的键、对标量值取键、数组下标越界、对空数组使用[*]都返回false而非报错。与 get_json_object 的区别这是使用JSON_EXISTS时最容易踩坑的地方。Spark 传统函数get_json_object(json_expr, path) IS NOT NULL无法区分存在但值为 JSONnull与路径不存在——因为get_json_object在两种情况下都返回NULL。而JSON_EXISTS明确区分{a: null}中的$.a存在但值为 JSONnull→JSON_EXISTS返回true{a: 1}中的$.b路径不存在→JSON_EXISTS返回false。从源码看这一语义在 jsonExpressions.scala 中通过JsonExists表达式的文档注释与pathExists求值器明确实现anyMatch在路径匹配结束时直接返回true无论当前值是否为 JSONnullJsonExpressionEvalUtils.anyMatch中case Nil分支parser.skipChildren(); true。完整示例基础匹配SELECT json_exists({a:{b:1}}, $.a.b) AS matched; ------- |matched| ------- | true| -------存在但为 null 与完全缺失的区分-- Present but JSON null - true; absent - false SELECT json_exists({a:null}, $.a) AS present_null, json_exists({a:1}, $.b) AS absent; ------------------ |present_null|absent| ------------------ | true| false| ------------------NULL 输入不受 ON ERROR 影响-- NULL input - NULL (Unknown), regardless of the ON ERROR clause SELECT json_exists(CAST(NULL AS STRING), $.a TRUE ON ERROR) AS r; ---- | r| ---- |NULL| ----格式错误输入遵循 ON ERROR 子句-- Malformed input follows the ON ERROR clause (default FALSE) SELECT json_exists(not json, $.a) AS default_false, json_exists(not json, $.a TRUE ON ERROR) AS true_on_error, json_exists(not json, $.a UNKNOWN ON ERROR) AS unknown_on_error; ------------------------------------------ |default_false|true_on_error|unknown_on_error| ------------------------------------------ | false| true| NULL| ------------------------------------------Lax 通配符与数组自动解包-- Lax wildcards: [*] is true iff the array has elements; auto-unwrap applies a step to each element SELECT json_exists({a:[1,2]}, $.a[*]) AS has_elems, json_exists({a:[]}, $.a[*]) AS empty_array, json_exists({a:[{b:1},{c:2}]}, $.a[*].b) AS any_elem_has_b; ---------------------------------- |has_elems|empty_array|any_elem_has_b| ---------------------------------- | true| false| true| ----------------------------------第三个例子充分体现了 lax 模式的核心能力$.a[*].b在a是对象数组时b步骤会被自动解包应用到每个数组元素上只要任一元素有b键即返回true。在 WHERE 子句中作为谓词使用SELECT id FROM docs WHERE json_exists(doc, $.address.city);这是JSON_EXISTS最常见的实战用法对每一行的doc字段测试路径$.address.city是否存在作为过滤条件。由于JsonExists的数据类型是BooleanType它可以直接作为谓词参与过滤、Join 条件等实现上其nullable只在输入可空或UNKNOWN ON ERROR时为true这使优化器能把 WHERE 谓词当作确定的布尔值处理。源码级实现剖析JSON_EXISTS在 Spark 中的完整链路为SQL 解析AstBuilder→ 表达式构造JsonExists→ 路径解析JsonPathParser→ 运行时求值pathExists/anyMatch。下面按这条链路逐一说明。语法解析AstBuilder在 AstBuilder.scala 的visitJsonExists中语法树节点JsonExistsContext被翻译为表达式从语法节点取出jsonExpr与path解析ON ERROR子句TRUE→JsonExistsBehavior.True、FALSE→False、UNKNOWN→Unknown否则为Error未指定ON ERROR时默认JsonExistsBehavior.False即FALSE ON ERROR。对应的语法解析测试见 ExpressionParserSuite.scala覆盖了四种ON ERROR写法。函数注册FunctionRegistryjson_exists通过 FunctionRegistry.scala 的expressionBuilder(json_exists, JsonExistsExpressionBuilder)注册为内置函数。JsonExistsExpressionBuilder位于 jsonExpressions.scala它与JSON_VALUE、JSON_QUERY共用JsonPathExpressionBuilder路径必须是常量字符串字面量构造JsonExists(jsonExpr, path, JsonExistsBehavior.False)。表达式JsonExistsJsonExists 是核心表达式类关键设计点ON ERROR行为模型JsonExistsBehavior是一个 sealed trait包含True、False、Unknown、Error四个单例jsonExpressions.scala#L882-L888。可空性nullable只有当输入表达式可空、或ON ERROR为Unknown时结果才可能为NULLchild.nullable || onError JsonExistsBehavior.Unknown。因此非空输入且其他ON ERROR行为下结果是确定的布尔值有利于优化器处理。输入类型要求STRING支持 collation 的字符串类型StringTypeWithCollation(supportsTrimCollation true)输出类型为BooleanType。路径预解析与校验由于语法上path是字符串字面量路径只需解析一次parsedPath是懒加载的Option[Seq[PathInstruction]]checkInputDataTypes在分析期校验路径是否合法——非法路径直接抛出INVALID_JSON_PATH错误因此语法错误在分析阶段就被拒绝该错误信息与同样拒绝通配符的JSON_VALUE共用。求值逻辑eval输入为 SQLNULL时直接返回nullUNKNOWN不走ON ERROR路径否则调用evaluator.pathExists(json)返回Some(exists)时直接给出布尔结果返回None输入非单一格式良好的 JSON时进入onErrorResult()。路径解析JsonPathParserJsonExpressionEvalUtils.scala 中的JsonPathParser是一个基于 ScalaRegexParsers的路径解析器把 SQL/JSON 路径编译为PathInstruction指令序列第37-44行Key :: Named(name)成员访问对应.name或[name]Subscript :: Index(index)下标访问对应[123]解析时用toLongOption防护超大下标导致的NumberFormatExceptionSubscript :: Wildcard数组通配符[*]Wildcard成员通配符.*/[*]。运行时求值pathExists 与 anyMatchpathExistsJsonExpressionEvalUtils.scala#L708-L721使用单个 Jackson 解析器同时完成路径导航和格式校验匹配完成后通过drainToRootEnd消费根值的剩余部分拒绝任何尾随内容因此合法前缀 垃圾或第二个根值与完全损坏的文档一样被视为格式错误且避免了对每个输入做额外一次 O(文档大小) 的格式校验扫描。anyMatchJsonExpressionEvalUtils.scala#L737-L829按PathInstruction递归匹配完整实现了 lax 模式语义成员访问对象上取命名键只跟踪第一个同名成员与JSON_VALUE、JSON_TABLE的 first-match 语义一致对数组自动解包逐元素匹配对标量直接返回false成员访问器不适用于标量不匹配而非错误下标访问数组按下标匹配非数组自动包装为单元素数组——[0]匹配、[i0]不匹配标量已被完整消费以维持解析器位置不变量数组通配符[*]数组上逐元素匹配非数组按单元素数组匹配成员通配符.*/[*]对象上遍历所有成员数组上自动解包标量上无成员返回false路径结束时Nil分支当前值存在即匹配包括 JSONnull同时skipChildren()消费完整值。辅助函数forEachElementJsonExpressionEvalUtils.scala#L838-L846负责数组遍历一旦找到匹配即短路存在性判断无需继续匹配但会排干整个数组以保持解析器位置不变量供drainToRootEnd校验尾随内容。迁移与使用建议从 Oracle / DB2 / PostgreSQL 迁移这些系统的JSON_EXISTS用法可直接移植到 Spark。路径求值同为 lax 模式ON ERROR的四种取值语义也保持一致。判断 JSONnull是否存在如果需要区分键存在但值为 null与键不存在请使用JSON_EXISTS不要用get_json_object(...) IS NOT NULL。处理脏数据数据质量不可控时优先用默认的FALSE ON ERROR把格式错误行静默过滤或按业务需要选择UNKNOWN/TRUE/ERROR。路径必须为常量path是字符串字面量无法动态传入列值与其他 JSON 函数要求一致。相关语句SELECTWHERE ClauseJSON_TABLE【免费下载链接】sparkApache Spark - A unified analytics engine for large-scale data processing项目地址: https://gitcode.com/gh_mirrors/sp/spark创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考