如何使用 SqlAlchemy 在 Postgres 中查询 JSON 数组?

tah*_*yon 11 sqlalchemy flask-sqlalchemy

我定义了一个 SqlAlchemy 模型

from sqlalchemy.dialects.postgresql import JSONB

class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(255), nullable=False)
    city = db.Column(db.String(255))
    contact_list = db.Column(JSONB)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

def add_user():
    user = User(nickname="Mike")
    user.contact_list = [{"name": "Sam", "phone": ["123456", "654321"]}, 
                         {"name": "John", "phone": ["159753"]},
                         {"name": "Joe", "phone": ["147889", "98741"]}]
    db.session.add(user)
    db.session.commit()

if __name__ == "__main__":
    add_user()
Run Code Online (Sandbox Code Playgroud)

如何使用电话从我的contact_list 中检索姓名?例如,我有147889,我如何检索Joe

我试过这个

User.query.filter(User.contact_list.contains({"phone": ["147889"]})).all()

但是,它返回给我一个空列表, []

我怎样才能做到这一点?

Ilj*_*ilä 11

您只是忘记了您的 JSON 路径也应该包含最外层的数组:

User.query.filter(User.contact_list.contains([{"phone": ["147889"]}])).all()
Run Code Online (Sandbox Code Playgroud)

将返回您正在寻找的用户。如果您的 JSON 包含带有键“phone”等的对象,则原始查询将匹配。请注意,这将返回有User问题的对象,而不是 JSON 结构中的特定对象/名称。如果您希望这样做,这似乎是最终目标,您可以展开每个用户的数组元素,根据结果记录进行过滤,然后选择名称:

val = db.column('value', type_=JSONB)
db.session.query(val['name'].astext).\
    select_from(User,
                db.func.jsonb_array_elements(User.contact_list).alias()).\
    filter(val.contains({"phone": ["147889"]})).\
    all()
Run Code Online (Sandbox Code Playgroud)

另一方面,上面的查询不像第一个查询那样对索引友好,因为它必须在过滤之前扩展所有数组,因此首先在联系人列表中找到包含电话的用户可能是有益的子查询或 CTE,然后展开和过滤。

  • 啊,你是这个意思。请注意,`filter(val.contains({"phone": ["147889"]}))` 只是检查该号码是否包含在该行中,但在返回该行作为结果时,它不会删除其他号码。为此,您需要一个额外的步骤,或者完全不同类型的查询。 (2认同)