Конкатенация массивов и их преобразование в SQLAlchemy
У меня есть следующий PostgreSQL запрос:
WITH sub_query AS (
SELECT vi.idvalfield,
vi.value::text
FROM valueint_value AS vi
UNION
SELECT vt.idvalfield,
vt.value::text
FROM valuetext_value AS vt
)
SELECT
sq.idvalfield,
sq.value
FROM sub_query AS sq
JOIN valuefield AS vf ON vf.idvalfield = sq.idvalfield
JOIN event e on vf.idevent = e.idevent
WHERE NOT (e.idevent || array[]::uuid[]) && (SELECT array_agg(e.idevent) AS id_event
FROM sub_query AS sq
JOIN valuefield AS vf ON vf.idvalfield = sq.idvalfield
JOIN event e on vf.idevent = e.idevent
WHERE (idtable = 41 AND sq.value = 222)
OR (idtable = 43 AND sq.value = 18)
);
Я описал CTE. Количество таблиц в UNION меняется поэтому описал динамически:
from sqlalchemy.dialects import postgresql
from sqlalchemy import or_, and_
from sqlalchemy import cast, Table, Text
from sqlalchemy.dialects.postgresql import array_agg, array, ARRAY, UUID
models_view = [
session.query(
model.c.idvalfield.label('id_value'),
cast(model.c.value, Text).label('value')
).filter(model.c.idvalfield.in_(id_fields))
for model, id_fields in model_values.items()
]
cte_union_view = models_view[0].union_all(*models_view[1:]).cte()
Затем описал подзапрос в WHERE:
filtered_event = session.query(array_agg(Event.idevent))\
.select_from(cte_union_view)\
.join(Valuefield, cte_union_view.c.id_value == Valuefield.idvalfield)\
.join(Event, Event.idevent == Valuefield.idevent)\
.filter(or_(and_(Valuefield.idtable == 41, cte_union_view.c.value == '222'),
and_(Valuefield.idtable == 43, cte_union_view.c.value == '18'))).subquery()
Описал основной запрос:
event_all = session.query(cte_union_view)\
.join(Valuefield, cte_union_view.c.id_value == Valuefield.idvalfield)\
.join(Event, Event.idevent == Valuefield.idevent).all()
Но у меня не получается описать само условие WHERE:
WHERE NOT (e.idevent || array[]::uuid[]) && (SELECT array_agg(e.idevent)
Пробовал:
filter(cast([Event.idevent + array([])], ARRAY(UUID)).in_(filtered_event))
and
filter(cast([array([Event.idevent]) + array([])], ARRAY(UUID)).in_(filtered_event))
Но это не работает. Кто знает, как можно это описать на ORM?
Ответы (1 шт):
Автор решения: chmutovsergey
→ Ссылка
В общем, покопавшись в алхимии нашел способ решения. Может будет кому-то полезен. Преобразование UUID в массив UUID'ов
(e.idevent || array[]::uuid[])
можно выполнить используя literal_column:
literal_column('ARRAY[]::uuid[]').op('||')(Event.idevent)
И теперь весь блок WHERE можно описать так:
.filter(uuid_event_arr.notin_(filtered_event)).all()
Но, на самом деле оказалось проще переписать подзапрос в WHERE, без функции array_agg(). А это в свою очередь упростить построение запроса средствами алхимии:
filtered_event = session.query(Event.idevent)\
.select_from(cte_union_view)\
.join(Valuefield, cte_union_view.c.id_value == Valuefield.idvalfield)\
.join(Event, Event.idevent == Valuefield.idevent)\
.filter(and_(Valuefield.idtable == 41, cte_union_view.c.v_text == '222'))
views_value = session.query(cte_union_view)\
.join(Valuefield, cte_union_view.c.id_value == Valuefield.idvalfield)\
.join(Event, Event.idevent == Valuefield.idevent)\
.filter(Event.idevent.notin_(filtered_event)).all()