JSON 支持¶
Lance 提供对存储和查询 JSON 数据的全面支持,使您能够高效地处理半结构化数据。本指南介绍如何在 Lance 数据集中存储 JSON 数据,以及使用 JSON 函数查询和过滤数据。
先决条件¶
JSON 支持需要 Lance 数据存储版本 2.2 或更高版本
import lance
import pyarrow as pa
import json
# Create a table with JSON data
json_data = {"name": "Alice", "age": 30, "city": "New York"}
json_arr = pa.array([json.dumps(json_data)], type=pa.json_())
table = pa.table({"id": [1], "data": json_arr})
# Write with version 2.2 (required for JSON support)
lance.write_dataset(table, "dataset.lance", data_storage_version="2.2")
存储格式¶
Lance 内部使用 lance.json
扩展类型将 JSON 数据存储为 JSONB(二进制 JSON)。这提供了: - 通过二进制编码实现高效存储 - 嵌套字段访问的快速查询性能 - 与 Apache Arrow 的 JSON 类型兼容
当您从 Lance 读取 JSON 数据时,它会自动转换为 Arrow 的 JSON 类型,以便与您的数据处理管道无缝集成。
JSON 函数¶
Lance 提供了一套全面的 JSON 函数,用于查询和过滤 JSON 数据。这些函数可以在 to_table()
、scanner()
等方法中与过滤表达式一起使用,并通过 DataFusion 集成进行 SQL 查询。
数据访问函数¶
json_extract¶
使用 JSONPath 语法从 JSON 中提取值。
语法: json_extract(json_column, json_path)
返回: JSON 值作为字符串(包括字符串的引号)
示例
# Sample data: {"user": {"name": "Alice", "age": 30}}
result = dataset.to_table(
filter="json_extract(data, '$.user.name') = '\"Alice\"'"
)
注意
json_extract
返回的字符串值包含 JSON 引号。与 '"Alice"'
而不是 'Alice'
进行比较。
json_get¶
从 JSON 中检索字段或数组元素,并将其作为 JSON 返回。
语法: json_get(json_column, key)
返回: JSON 值(可用于嵌套访问)
示例
# Access nested JSON by chaining json_get calls
# Sample data: {"user": {"profile": {"name": "Alice"}}}
result = dataset.to_table(
filter="json_get_string(json_get(json_get(data, 'user'), 'profile'), 'name') = 'Alice'"
)
类型安全的数值提取¶
这些函数提取值时包含类型转换和验证
json_get_string¶
从 JSON 中提取字符串值。
语法: json_get_string(json_column, key)
返回: 字符串值(不带 JSON 引号)
示例
json_get_int¶
使用类型强制转换提取整数值。
语法: json_get_int(json_column, key)
返回: 64 位整数(如果可能,转换字符串)
示例
# Works with both numeric and string values
# {"age": 30} or {"age": "30"} both work
result = dataset.to_table(
filter="json_get_int(data, 'age') > 25"
)
json_get_float¶
使用类型强制转换提取浮点值。
语法: json_get_float(json_column, key)
返回: 64 位浮点数(如果可能,转换字符串)
示例
json_get_bool¶
使用类型强制转换提取布尔值。
语法: json_get_bool(json_column, key)
返回: 布尔值(转换“true”/“false”等字符串、数字)
示例
存在性和数组函数¶
json_exists¶
检查 JSONPath 是否存在于 JSON 数据中。
语法: json_exists(json_column, json_path)
返回: 布尔值
示例
# Find records that have an age field
result = dataset.to_table(
filter="json_exists(data, '$.user.age')"
)
json_array_contains¶
检查 JSON 数组是否包含特定值。
语法: json_array_contains(json_column, json_path, value)
返回: 布尔值
示例
# Sample data: {"tags": ["python", "ml", "data"]}
result = dataset.to_table(
filter="json_array_contains(data, '$.tags', 'python')"
)
json_array_length¶
返回 JSON 数组的长度。
语法: json_array_length(json_column, json_path)
返回: 整数(对于非数组或缺失路径返回 0)
示例
# Find records with more than 3 tags
result = dataset.to_table(
filter="json_array_length(data, '$.tags') > 3"
)
使用示例¶
处理嵌套 JSON¶
import lance
import pyarrow as pa
import json
# Create nested JSON data
data = [
{
"id": 1,
"user": {
"profile": {
"name": "Alice",
"settings": {
"theme": "dark",
"notifications": True
}
},
"scores": [95, 87, 92]
}
},
{
"id": 2,
"user": {
"profile": {
"name": "Bob",
"settings": {
"theme": "light",
"notifications": False
}
},
"scores": [88, 91, 85]
}
}
]
# Convert to Lance dataset
json_strings = [json.dumps(d) for d in data]
table = pa.table({
"data": pa.array(json_strings, type=pa.json_())
})
lance.write_dataset(table, "nested.lance", data_storage_version="2.2")
dataset = lance.dataset("nested.lance")
# Query nested fields using JSONPath
dark_theme_users = dataset.to_table(
filter="json_extract(data, '$.user.profile.settings.theme') = '\"dark\"'"
)
# Or using chained json_get
high_scorers = dataset.to_table(
filter="json_array_length(data, '$.user.scores') >= 3"
)
JSON 与其他数据类型结合¶
# Create mixed-type table with JSON metadata
products = pa.table({
"id": [1, 2, 3],
"name": ["Laptop", "Phone", "Tablet"],
"price": [999.99, 599.99, 399.99],
"specs": pa.array([
json.dumps({"cpu": "i7", "ram": 16, "storage": 512}),
json.dumps({"screen": 6.1, "battery": 4000, "5g": True}),
json.dumps({"screen": 10.5, "battery": 7000, "stylus": True})
], type=pa.json_())
})
lance.write_dataset(products, "products.lance", data_storage_version="2.2")
dataset = lance.dataset("products.lance")
# Find products with specific specs
result = dataset.to_table(
filter="price < 600 AND json_get_bool(specs, '5g') = true"
)
处理 JSON 中的数组¶
# Create data with JSON arrays
records = pa.table({
"id": [1, 2, 3],
"data": pa.array([
json.dumps({"name": "Project A", "tags": ["python", "ml", "production"]}),
json.dumps({"name": "Project B", "tags": ["rust", "systems"]}),
json.dumps({"name": "Project C", "tags": ["python", "web", "api", "production"]})
], type=pa.json_())
})
lance.write_dataset(records, "projects.lance", data_storage_version="2.2")
dataset = lance.dataset("projects.lance")
# Find projects with Python
python_projects = dataset.to_table(
filter="json_array_contains(data, '$.tags', 'python')"
)
# Find projects with more than 3 tags
complex_projects = dataset.to_table(
filter="json_array_length(data, '$.tags') > 3"
)
性能考量¶
- 使用特定的提取函数:像
json_get_string
这样的函数比json_extract
对于简单的字段访问更高效。 - 索引频繁查询的路径:考虑为频繁访问的 JSON 路径创建计算列,以提高查询性能。
- 尽量减少深度嵌套:虽然 Lance 支持任意嵌套,但更扁平的结构通常性能更好。
- 类型安全函数:当您知道预期类型时,使用特定类型的函数(
json_get_int
、json_get_bool
),因为它们可以有效地处理类型强制转换。
与 DataFusion 集成¶
当 Lance 与 Apache DataFusion 一起用于 SQL 查询时,所有 JSON 函数都可用。有关在 SQL 上下文中使用 JSON 函数的更多详细信息,请参阅DataFusion 集成指南。
限制¶
- JSON 支持需要数据存储版本 2.2 或更高版本
- JSONPath 支持遵循标准 JSONPath 语法,但可能不支持所有高级功能
- 大型 JSON 文档可能会影响查询性能
- JSON 函数目前仅适用于过滤,不适用于查询结果中的投影