我有一个使用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
的比较方法仅引用键,而不引用值< / strong>。
除了使用原始查询外,我不清楚如何通过SQLAlchemy设计此查询。
Based on this answer,我尝试了以下内容。
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
所以我尝试了
session \
.query(A_Table) \
.filter(
B_Table.json_record.contains({
'path': {
'to': A_Table.name
}
})
)
至少导致了一个不同的错误,这个错误由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
索引的优化,这使我需要@>
运算符。
根据IljaEverilä的回答,我能够找到SQLAlchemy方法implemented in the source code,而using the sql-json
method能够在附近获得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
答案和源代码的方法relies on the _FunctionGenerator
都可以构建函数,但是目前尚不清楚如何在compile
的过程中将某些内容附加到方法的末尾。
NVM-答案的作者指出jsonb_build_object(...)
将适合该模型而无需标记。
答案 0 :(得分:1)
您已经注意到,链接的Q / A处理使用文字值的情况。解决方案是像以前尝试的那样,结合使用SQLA中的contains()
和Postgresql中的jsonb_build_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
的比较方法仅引用键,而不是值。
JSONB.Comparator.contains()
的SQLAlchemy文档似乎写得不好。比较
布尔表达式。测试键(或数组)是否为参数jsonb表达式的键的超集/包含键。
到@>
的Postgresql文档:
左侧的JSON值是否在顶层包含正确的JSON路径/值条目?
您可以在辅助函数中隐藏构建jsonb
的详细信息:
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}})))