web-dev-qa-db-ja.com

SqlAlchemyを使用してPostgresでJSON配列をクエリする方法は?

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()
_

phoneを使用してcontact_listからnameを取得するにはどうすればよいですか?たとえば、147889がありますが、どうすれば取得できますかJoe

私はこれを試しました

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

しかし、それは私に空のリスト_[]_を返します

これどうやってするの?

6
tahmid.choyon

JSONパスに最も外側の配列も含める必要があることを忘れただけです。

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

探しているユーザーを返します。 JSONにキー「phone」などのオブジェクトが含まれている場合、元のクエリは一致します。これにより、JSON構造の特定のオブジェクト/名前ではなく、問題のUserオブジェクトが返されることに注意してください。それが必要な場合は、最終目標のように思われるように、各ユーザーの配列要素を展開し、結果のレコードに基づいてフィルタリングし、名前を選択できます。

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()

一方、上記のクエリは、フィルタリングする前にすべての配列を展開する必要があるため、最初のクエリほどインデックスフレンドリーではありません。そのため、連絡先リストに電話を含むユーザーを最初に見つけることが有益な場合があります。サブクエリまたはCTEを実行してから、展開してフィルタリングします。

5
Ilja Everilä