Вы могли бы в значительной степени реализовать свой оригинальный подход, используя выражение с псевдонимом функции :
misc.setup_query(db_session, User).\
select_from(
User,
func.jsonb_array_elements(User.payment_info['subscriptions']).
alias('subs')).\
filter(column('subs', type_=JSONB)['external_id'].astext == 'sub_3Q9Q4bP2zW')
, которое компилируется в
SELECT "user".id AS user_id, "user".payment_info AS user_payment_info
FROM "user", jsonb_array_elements("user".payment_info -> %(payment_info_1)s) AS subs
WHERE (subs ->> %(subs_1)s) = %(param_1)s
С другой стороны, вы могли быиспользуйте оператор :
misc.setup_query(db_session, User).\
filter(User.payment_info['subscriptions'].contains(
[{'external_id': 'sub_3Q9Q4bP2zW'}]))
Обратите внимание, что требуется внешний список, так как он является частью «пути» для проверки.Используя ту же логику, вы можете опустить извлечение массива:
misc.setup_query(db_session, User).\
filter(User.payment_info.contains(
{'subscriptions': [{'external_id': 'sub_3Q9Q4bP2zW'}]}))
Приведенные выше @>
с использованием подходов индексируются с использованием GIN index .1-й требует функционального индекса, потому что сначала он извлекает массив:
CREATE INDEX user_payment_info_subscriptions_idx ON "user"
USING GIN ((payment_info -> 'subscriptions'));
2-й требует индексации всего столбца payment_info
jsonb.Создание индексов GIN можно выполнить в определениях модели SQLAlchemy с помощью специфичных для Postgresql параметров индекса :
class User(Base):
...
Index('user_payment_info_subscriptions_idx',
User.payment_info['subscriptions'],
postgresql_using='gin')
Почему различные попытки оказались безуспешными:
Вы не должны напрямую обращаться к компаратору.Предоставляет операторы для типа.Кроме того, вы передаете contains()
результат выражения
('subscriptions', 'external_id') == payment_subscription_id
, который, скорее всего, ложен (зависит от того, что payment_subscription_id
).То есть он оценивается в Python .
В Postgresql нет функции json_contains()
(в отличие от MySQL).Используйте оператор @>
.
У вас неверный путь.User.payment_info['subscriptions', 'external_id'].astext
будет соответствовать что-то вроде {"subscriptions": {"external_id": "foo"}}
, но в ваших данных subscriptions
ссылается на массив.