Python 使用Postgres@>;SQLAlchemy中的运算符和json_build_对象
我有一个使用几个Postgres特定操作的查询:Python 使用Postgres@>;SQLAlchemy中的运算符和json_build_对象,python,postgresql,sqlalchemy,Python,Postgresql,Sqlalchemy,我有一个使用几个Postgres特定操作的查询: SELECT a.row_id, a.name FROM a JOIN b ON b.json_record @> json_build_object('path', json_build_object('to', a.name))::jsonb 我的理解是,@操作符充当比较,但是SQLAlchemy文档中的JSONB的比较方法只引用键,而不是值 除了使用原始查询之外,我不清楚如何通过SQL
SELECT
a.row_id,
a.name
FROM
a
JOIN
b
ON
b.json_record @> json_build_object('path', json_build_object('to', a.name))::jsonb
我的理解是,@
操作符充当比较,但是SQLAlchemy文档中的JSONB
的比较方法只引用键,而不是值
除了使用原始查询之外,我不清楚如何通过SQLAlchemy设计此查询
编辑1
,我试了一下下面的
session \
.query(A_Table) \
.join(
B_Table.json_record.contains({
'path': {
'to': A_Table.name
}
})
)
但是,它导致了一个错误,该错误来自于行“to”:A_Table.name
:
AttributeError: Neither 'BinaryExpression' object nor 'Comparator' object has an attribute 'selectable'
sqlalchemy/orm/query.py", line 2206, in join
from_joinpoint=from_joinpoint,
File "<string>", line 2, in _join
这至少导致了一个不同的错误,这是一个从SQLAlchemy生成SQL的错误:
sqlalchemy.exc.StatementError: (builtins.TypeError)
Object of type 'InstrumentedAttribute' is not JSON serializable
[SQL: 'SELECT a.row_id AS a_id, a.name AS a_name FROM a, b
WHERE b.json_record @> %(json_record_1)s'] [parameters: [{}]]
这个SQL接近于我的目标,可能可以接受,但答案中提供的示例假定我提前知道值,而我要做的是与行值进行比较。我通常会这样做:
.filter([a.name == b.json_record['path']['to'].astext])
但我也在尝试利用gin
索引对JSONB
列的优化,这让我需要@
操作符
编辑2
根据Ilja Everilä的回答,我能够找到SQLAlchemy方法,并且能够在那里获得SQL
session \
.query(A_Table) \
.join(
B_Table.json_record.contains({
json_sql({'path': json_sql({
'to': A_Table.name
}
})
)
给我一个SQL:
SELECT
a.row_id,
a.name
FROM
a
JOIN
b
ON
b.json_record @> json_build_object('path', json_build_object('to', a.name))
此输出的问题在于,不是:
json_build_object(..., json_build_object(...))
有效的Postgres语法应为:
json_build_object(..., json_build_object(...))::jsonb
答案和源代码的方法都可以构建函数,但不清楚如何在编译过程中向方法末尾追加内容
编辑3
NVM-答案的作者指出,jsonb_build_object(…)
将适合这个没有标志的模型。正如您所注意到的,链接的Q/A处理使用文字值的情况。解决方案是在SQLA中使用contains()
,在Postgresql中使用jsonb\u build\u object()
,就像您之前尝试的那样:
session.query(A_Table) \
.filter(
B_Table.json_record.contains(
func.jsonb_build_object(
'path',
func.jsonb_build_object('to', A_Table.name)
)
)
)
我的理解是,@
操作符充当比较,但是SQLAlchemy文档中的JSONB
的比较方法只引用键,而不引用值
的SQLAlchemy文档似乎编写得有点糟糕。比较
布尔表达式。测试键(或数组)是否是参数jsonb表达式键的超集/包含该超集
要查看@>
的Postgresql文档,请执行以下操作:
左JSON值是否包含顶层的右JSON路径/值项
您可以在助手函数中隐藏buildingjsonb
的详细信息:
def build_jsonb(obj):
if isinstance(obj, dict):
pairs = [(k, build_jsonb(v)) for k, v in obj.items()]
return func.jsonb_build_object(*[arg for p in pairs for arg in p])
elif isinstance(obj, list):
return func.jsonb_build_array(*[build_jsonb(v) for v in obj])
else:
return obj
然后在原始查询中使用它:
session.query(A_Table) \
.filter(
B_Table.json_record.contains(
build_jsonb({'path': {'to': A_Table.name}})))
如果您希望使用显式的JOIN
语法:
session.query(A_Table).\
join(B_Table, B_Table.json_record.contains(
build_jsonb({'path': {'to': A_Table.name}})))
正如您所注意到的,链接的Q/A处理使用文本值的情况。解决方案是在SQLA中使用contains()
,在Postgresql中使用jsonb\u build\u object()
,就像您之前尝试的那样:
session.query(A_Table) \
.filter(
B_Table.json_record.contains(
func.jsonb_build_object(
'path',
func.jsonb_build_object('to', A_Table.name)
)
)
)
我的理解是,@
操作符充当比较,但是SQLAlchemy文档中的JSONB
的比较方法只引用键,而不引用值
的SQLAlchemy文档似乎编写得有点糟糕。比较
布尔表达式。测试键(或数组)是否是参数jsonb表达式键的超集/包含该超集
要查看@>
的Postgresql文档,请执行以下操作:
左JSON值是否包含顶层的右JSON路径/值项
您可以在助手函数中隐藏buildingjsonb
的详细信息:
def build_jsonb(obj):
if isinstance(obj, dict):
pairs = [(k, build_jsonb(v)) for k, v in obj.items()]
return func.jsonb_build_object(*[arg for p in pairs for arg in p])
elif isinstance(obj, list):
return func.jsonb_build_array(*[build_jsonb(v) for v in obj])
else:
return obj
然后在原始查询中使用它:
session.query(A_Table) \
.filter(
B_Table.json_record.contains(
build_jsonb({'path': {'to': A_Table.name}})))
如果您希望使用显式的JOIN
语法:
session.query(A_Table).\
join(B_Table, B_Table.json_record.contains(
build_jsonb({'path': {'to': A_Table.name}})))
相关的,有点:。运算符@>
在SQLAlchemy for JSONB列中是contains()
。你是在使用ORM还是Core?@IljaEverilä试了一下。看起来很接近,但我需要能够进行列值比较,而不是从内存/代码中传入值。有什么想法吗?使用ORM.An旁白:::jsonb
是特定于Postgresql的类型转换语法,而不是标志(除非我在这里误解了这个词的用法)。您可以将其替换为SQL标准CAST(作为jsonb)
。SQLAlchemy有多种方法来生成cast表达式,例如cast()
construct.Related,有点:。运算符@>
在SQLAlchemy for JSONB列中是contains()
。你是在使用ORM还是Core?@IljaEverilä试了一下。看起来很接近,但我需要能够进行列值比较,而不是从内存/代码中传入值。有什么想法吗?使用ORM.An旁白:::jsonb
是特定于Postgresql的类型转换语法,而不是标志(除非我在这里误解了这个词的用法)。您可以将其替换为SQL标准CAST(作为jsonb)
。SQLAlchemy有多种方法来生成cast表达式,例如cast()
construct。谢谢!参见我的第二次编辑-在sqlalchemy-utils
中有一个类似的内置方法,但是这两个方法都没有将:jsonb
附加到json\u-build\u对象
中,如果您只使用jsonb\u-build\u-object
,那么就不需要强制转换。如果必须使用sqlalchemy utils版本,请显式添加强制转换:json\u sql(…).cast(JSONB)
.Ah-我错过了diff btwnJSONB\u build\u对象
vsjson\u build\u对象
那里-太棒了!谢谢你!参见我的第二次编辑-在sqlalchemy-utils
中有一个类似的内置方法,但是这两个方法都没有将:jsonb
附加到json\u-build\u对象
中,如果您只使用jsonb\u-build\u-object
,那么就不需要强制转换。如果必须使用sqlalchemy utils版本,请显式添加强制转换:json\u sql(…).cast(JSONB)
.Ah-我错过了diff btwnJSONB\u build\u对象
vsjson\u build\u对象
那里-太棒了!