Возврат объединенного массива путем поиска / сравнения в массиве массивов - PullRequest
1 голос
/ 13 апреля 2019

У меня есть 2 таблицы:

категории

id | name |  | slug   | path | parent_id  | depth
1    name1     slug1    {1}      null       0
2    name2     slug2    {1,2}      1        1
3    name3     slug3    {1,2,3}    2        2
5    nam5      slug5    {5}       null      0
......
9    nam4      slug9    {5,9}       5       1

где путь имеет тип int[]array и работает как крошка

предметов

   id | name
   1    name1 

Между элементом и категорией существует отношение M2M

item_categories

 id | item_id  | category_id 
   1        1    |  3
   2        1       9   

В приведенном выше примере товар относится к 3 категориям:

Я использую следующий SQL:

SELECT c1.id, c1.name, c1.slug, c1.parent_id FROM categories AS c1 
WHERE ARRAY[c1.id] <@ (SELECT c2.path FROM categories AS c2 WHERE c2.id= 
(SELECT category_id FROM item_categories WHERE item_id=8 LIMIT 1)) order by depth

для извлечения основы крошки на пути, и это работает.

Но я хочу получить все сухарики (не одну). Удаление LIMIT 1 и изменение = to in Я получу массив массивов, а не просто массив, и вызовет ошибку:

более одной строки, возвращенной подзапросом, использованным в качестве выражения

что нормально.

Пример того, что я хочу - Элемент в:

cat1 - > cat2 - >cat3
ca5 -> cat9

и из базы данных (чтобы я мог зациклить их):

[ [{'name':cat1, 'slug':slug1}, {'name':cat2, 'slug':slug2}, {'name':cat3, 'slug':slug3}], [{'name':cat5, 'slug':slug5}, {'name':cat9, 'slug':slug9}]]

dbfiddle: https://dbfiddle.uk/?rdbms=postgres_10&fiddle=f756cfe568d38425dfe25cfec60b1b3f

Таким образом, вместо получения одной крошки, как я могу получить массив с панировочными сухарями в результате?

1 Ответ

1 голос
/ 13 апреля 2019

с использованием json_build_object, unnest и заказано json_agg:

select
     c.id,
     json_agg(
         json_build_object('name',c2.name,'slug',c2.slug)
         order by p.depth
     )
from categories as c
    inner join lateral unnest(c.path) with ordinality as p(id, depth) on true
    inner join categories as c2 on
        c2.id = p.id
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

Или вы можете использовать столбец depth из таблицы, если хотите:

select
     c.id,
     json_agg(
         json_build_object('name',c2.name,'slug',c2.slug)
         order by c2.depth
     )
from categories as c
    inner join categories as c2 on
        c2.id = any(c.path)
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

Что яне нравится, что json_build_object заключается в том, что вы должны явно указывать имена столбцов, выполняя двойную работу, поэтому я попытался использовать to_json вместо этого.Это работает, но, честно говоря, я не очень знаком с этим синтаксисом, когда псевдоним таблицы передается функции в качестве аргумента (см. Using Composite Types in Queries) и не может заставить его работать без lateral присоединиться:

select
     c.id,
     json_agg(to_json(d) order by c2.depth)
from categories as c
    inner join categories as c2 on
        c2.id = any(c.path)
    cross join lateral (select c.name, c.slug) as d
where
    exists (
        select *
        from item_categories as tt
        where
            tt.item_id = 1 and
            tt.category_id = c.id
    )
group by
    c.id

db<>fiddle demo

...