JSON 数据类型支持¶
概述¶
最近,所有主要的数据库系统都添加了对原生 JSON 数据类型的支持。JSON 支持引入了通常在 NoSQL 数据库中发现的动态数据结构。通常,它们在处理高度可变的数据或难以预测确切数据结构时使用。
Pony 允许使用 Python 语法处理存储在数据库中的 JSON 数据。
声明 JSON 属性¶
要使用 Pony 声明 JSON 属性,您应该使用 Json
类型。此类型可以从 pony.orm
包中导入
from pony.orm import *
db = Database()
class Product(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str)
info = Required(Json)
tags = Optional(Json)
db.bind('sqlite', ':memory:', create_db=True)
db.generate_mapping(create_tables=True)
Product
实体中的 info
属性被声明为 Json
。这使我们能够为不同的产品类型拥有不同的 JSON 结构,并对这些数据进行查询。
为 JSON 属性赋值¶
通常,JSON 结构包含字典和列表的组合,这些字典和列表包含简单类型,例如数字、字符串和布尔值。让我们创建几个具有简单 JSON 结构的对象
p1 = Product(name='Samsung Galaxy S7 edge',
info={
'display': {
'size': 5.5,
},
'battery': 3600,
'3.5mm jack': True,
'SD card slot': True,
'colors': ['Black', 'Grey', 'Gold'],
},
tags=['Smartphone', 'Samsung'])
p2 = Product(name='iPhone 6s',
info={
'display': {
'size': 4.7,
'resolution': [750, 1334],
'multi-touch': True,
},
'battery': 1810,
'3.5mm jack': True,
'colors': ['Silver', 'Gold', 'Space Gray', 'Rose Gold'],
},
tags=['Smartphone', 'Apple', 'Retina'])
在 Python 代码中,JSON 结构借助标准 Python dict 和 list 表示。在我们的示例中,info
属性被分配了一个 dict。tags
属性保留了一个标签列表。这些属性将被序列化为 JSON,并在提交时存储在数据库中。
读取 JSON 属性¶
您可以像读取任何其他实体属性一样读取 JSON 属性
>>> Product[1].info
{'battery': 3600, '3.5mm jack': True, 'colors': ['Black', 'Grey', 'Gold'],
'display': 5.5}
从数据库中提取 JSON 属性后,它将被反序列化并表示为 dict 和 list 的组合。您可以使用标准 Python dict 和 list API 处理该值
>>> Product[1].info['colors']
['Black', 'Grey', 'Gold']
>>> Product[1].info['colors'][0]
'Black'
>>> 'Black' in Product[1].info['colors']
True
修改 JSON 属性¶
要修改 JSON 属性值,您也可以使用标准 Python list 和 dict API
>>> Product[1].info['colors'].append('Silver')
>>> Product[1].info['colors']
['Black', 'Grey', 'Gold', 'Silver']
现在,在提交时,更改将存储在数据库中。为了跟踪 JSON 结构中所做的更改,Pony 使用它自己的 dict 和 list 实现,这些实现继承自标准 Python dict 和 list。
以下是一些关于如何修改 JSON 值的示例。
p = Product[1]
# assigning a new value
p.info['display']['size'] = 4.7
# popping a dict value
display_size = p.info['display'].pop('size')
# removing a dict key using del
del p.info['display']
# adding a dict key
p.info['display']['resolution'] = [1440, 2560]
# removing a list item
del p.info['colors'][0]
# replacing a list item
p.info['colors'][1] = ['White']
# replacing a number of list items
p.info['colors'][1:] = ['White']
所有上述操作都是对属性、列表和字典的常规 Python 操作。
查询 JSON 结构¶
数据库中的原生 JSON 支持不仅允许读取和修改结构化数据,还允许进行查询。这是一个非常强大的功能 - 查询使用相同的语法并在相同的 ACID 事务环境中运行,同时在关系数据库内部提供文档存储的 NoSQL 功能。
Pony 允许通过按 JSON 子元素过滤来选择对象。为了访问 JSON 子元素,Pony 构造 JSON 路径表达式,然后将其用于 SQL 查询中
# products with display size greater than 5
Product.select(lambda p: p.info['display']['size'] > 5)
为了指定值,您可以使用参数
x = 2048
# products with width resolution greater or equal to x
Product.select(lambda p: p.info['display']['resolution'][0] >= x)
在 MySQL、PostgreSQL、CockroachDB 和 SQLite 中,也可以在 JSON 路径表达式中使用参数
index = 0
Product.select(lambda p: p.info['display']['resolution'][index] < 2000)
key = 'display'
Product.select(lambda p: p.info[key]['resolution'][index] > 1000)
注意
Oracle 不支持 JSON 路径中的参数。使用 Oracle 时,您只能使用常量键。
对于 JSON 数组,您可以计算长度
# products with more than 2 tags
Product.select(lambda p: len(p.info['tags']) > 2)
另一个查询示例是检查字符串键是否是 JSON dict 或数组的一部分
# products which have the resolution specified
Product.select(lambda p: 'resolution' in p.info['display'])
# products of black color
Product.select(lambda p: 'Black' in p.info['colors'])
当您将 JSON 子元素与 None
进行比较时,它将在以下情况下被评估为 True
当子元素包含 JSON
null
值时当子元素不存在时
Product.select(lambda p: p.info['SD card slot'] is None)
您可以测试 JSON 子元素的真值
# products with multi-touch displays
select(p for p in Product if p.info['display']['multi-touch'])
在 Python 中,以下值在条件语句中被视为假:None
、0、False
、空字符串、空字典和空列表。Pony 在应用于 JSON 结构的条件中保留此行为。此外,如果找不到 JSON 路径,它将被评估为假。
在前面的示例中,我们在查询条件中使用了 JSON 结构。但也可以检索 JSON 结构或将其部分作为查询结果提取
select(p.info['display'] for p in Product)
以这种方式检索 JSON 结构时,它们不会链接到实体实例。这意味着对这些 JSON 结构的修改不会保存到数据库中。Pony 仅在您选择对象并修改其属性时跟踪 JSON 更改。
MySQL 和 Oracle 允许在 JSON 路径中使用通配符。Pony 通过使用特殊语法支持通配符
[…] 表示“任何字典元素”
[:] 表示“任何列表项”
以下是一个查询示例
select(p.info['display'][...] for p in Product)
此类查询的结果将是一个 JSON 子元素数组。在数据库中当前的 JSON 支持情况下,通配符只能在生成器表达式的表达式部分使用。
数据库中的 JSON 支持¶
为了将 JSON 存储在数据库中,Pony 使用以下类型
SQLite - TEXT
PostgreSQL 和 CockroachDB - JSONB(二进制 JSON)
MySQL - JSON(二进制 JSON,尽管它在名称中没有“B”)
Oracle - CLOB
从 3.9 版本开始,SQLite 提供了 JSON1 扩展模块。此扩展在处理 JSON 查询时提高了性能,尽管 Pony 即使没有此模块也可以在 SQLite 中处理 JSON。