【问题标题】:How to query a JSON element如何查询 JSON 元素
【发布时间】:2015-07-10 12:48:57
【问题描述】:

假设我有一个 Postgres 数据库 (9.3),并且有一个名为 Resources 的表。在 Resources 表中,我有字段 id 是一个 int 和 data 这是一个 JSON 类型。

假设我在所述表中有以下记录。

  • 1, {'firstname':'Dave', 'lastname':'Gallant'}
  • 2, {'firstname':'John', 'lastname':'Doe'}

我想要做的是编写一个查询,该查询将返回所有记录,其中数据列具有一个 json 元素,其姓氏等于“Doe”

我试着写这样的东西:

records = db_session.query(Resource).filter(Resources.data->>'lastname' == "Doe").all()

然而,Pycharm 在“->>”上给了我一个编译错误

有谁知道我将如何编写过滤子句来满足我的需要?

【问题讨论】:

  • 尝试使用astext 喜欢:...filter(Resources.c.data["lastname"].astext == "Doe").all() 看看是否有效
  • 这似乎可行,但我必须删除“.c”并直接转到“Resources.data”。谢谢!
  • 啊...是的,我通常使用.c 作为列名...很高兴您解决了这个问题。然后我将在上面发布作为答案:-)
  • 你必须使用.c,如果你不使用我认为的声明性基础(table = sqlalchemy.Table(...)

标签: postgresql sqlalchemy


【解决方案1】:

尝试使用astext

records = db_session.query(Resource).filter(
              Resources.data["lastname"].astext == "Doe"
          ).all()

请注意,列必须具有 JSONB 类型。常规 JSON 列将不起作用。

【讨论】:

  • 很好的答案。此外,您提供的链接还提供了有关如何查询嵌套 json 的其他一些信息(例如 query.filter(Resources.data[("lastname", "whatever-other-nested-json-under-lastname")].astext == "Whatever Value").all() ),以及如何更改数据类型。
【解决方案2】:

您还可以将字符串显式转换为 JSON(请参阅 Postgres JSON type doc)。

from sqlalchemy.dialects.postgres import JSON
from sqlalchemy.sql.expression import cast
db_session.query(Resource).filter(
    Resources.data["lastname"] == cast("Doe", JSON)
).all()

【讨论】:

  • 您可能需要从 dialects.postgresql 导入,而不仅仅是 postgres
  • 对于较大的表,不要忘记创建部分索引。否则 JSON 查询会变得昂贵。 CREATE INDEX idx_lastname_partial ON 资源 (((data ->> 'lastname')::VARCHAR(24))) WHERE (data ->> 'lastname') IS NOT NULL;
  • 您应该使用 JSONB 而不是 JSON。然后,您可以阐明更复杂的 where 子句,例如 Resources.data["size"] > cast(0.4, JSONB)
【解决方案3】:

如果您使用的是 JSON 类型(不是 JSONB),则以下内容对我有用:

注意'"object"'

    query = db.session.query(ProductSchema).filter(
        cast(ProductSchema.ProductJSON["type"], db.String) != '"object"'
    )

【讨论】:

    【解决方案4】:

    根据sqlalchemy.types.JSON,你可以这样做

    from sqlalchemy import JSON
    from sqlalchemy import cast
    records = db_session.query(Resource).filter(Resources.data["lastname"] == cast("Doe", JSON)).all()
    

    【讨论】:

      【解决方案5】:

      我在 JSON(不是 JSONB)类型列中有一些 GeoJSON,现有的解决方案都不起作用,但事实证明,在 1.3.11 版本中添加了一些新的数据转换,所以现在您可以:

      records = db_session.query(Resource).filter(Resources.data["lastname"].as_string() == "Doe").all()
      
      

      参考:https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.JSON

      将 JSON 元素转换为其他类型

      索引操作,即通过调用表达式调用的操作 在 some_column['some key'] 中使用 Python 括号运算符, 返回一个默认类型为 JSON 的表达式对象,所以 可以根据结果调用进一步的面向 JSON 的指令 类型。但是,索引操作可能更常见 期望返回特定的标量元素,例如字符串或 整数。为了提供对这些元素的访问 与后端无关的方式,提供了一系列数据转换:

      Comparator.as_string() - return the element as a string
      
      Comparator.as_boolean() - return the element as a boolean
      
      Comparator.as_float() - return the element as a float
      
      Comparator.as_integer() - return the element as an integer
      

      这些数据转换是通过支持方言来实现的,以便 确保与上述类型的比较将按预期工作,例如 如:

      # integer comparison
      data_table.c.data["some_integer_key"].as_integer() == 5
      
      # boolean comparison
      data_table.c.data["some_boolean"].as_boolean() == True
      

      【讨论】:

      • 如果我有一个Big | Long Integer 并且没有填写Integer 类型怎么办?
      • 此评论超出问题范围。但是我在使用as_integer 函数进行比较时发现了一个错误,所以我选择了这个表达式cast(Table.column, JSONB)['json_property'].as_numeric(13, 0) == numberas_numeric 来自SQLAlchemy Docs 有帮助!
      【解决方案6】:

      根据this,1.3.11 之前的版本,最健壮的方法应该是这样,因为它适用于多种数据库类型,例如SQLite、MySQL、Postgres:

      from sqlalchemy import cast, JSON, type_coerce, String
      
      db_session.query(Resource).filter(
          cast(Resources.data["lastname"], String) == type_coerce("Doe", JSON)
      ).all()
      

      从 1.3.11 版本开始,特定类型的脚轮是处理此问题的新方法:

      db_session.query(Resource).filter(
          Resources.data["lastname"].as_string() == "Doe"
      ).all()
      

      【讨论】:

      • 谢谢@Xuan 这是唯一对我有用的答案
      猜你喜欢
      • 2023-02-25
      • 1970-01-01
      • 1970-01-01
      • 2013-11-03
      • 2016-12-01
      • 1970-01-01
      • 2021-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多