Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/342.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用Postgres@>;SQLAlchemy中的运算符和json_build_对象_Python_Postgresql_Sqlalchemy - Fatal编程技术网

Python 使用Postgres@>;SQLAlchemy中的运算符和json_build_对象

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

我有一个使用几个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
的比较方法只引用键,而不是

除了使用原始查询之外,我不清楚如何通过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路径/值项


您可以在助手函数中隐藏building
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}})))

正如您所注意到的,链接的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路径/值项


您可以在助手函数中隐藏building
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}})))

相关的,有点:。运算符
@>
在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 btwn
JSONB\u build\u对象
vs
json\u build\u对象
那里-太棒了!谢谢你!参见我的第二次编辑-在
sqlalchemy-utils
中有一个类似的内置方法,但是这两个方法都没有将
:jsonb
附加到
json\u-build\u对象
中,如果您只使用
jsonb\u-build\u-object
,那么就不需要强制转换。如果必须使用sqlalchemy utils版本,请显式添加强制转换:
json\u sql(…).cast(JSONB)
.Ah-我错过了diff btwn
JSONB\u build\u对象
vs
json\u build\u对象
那里-太棒了!