PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Postgres — Konwertuj listę sąsiedztwa na zagnieżdżony obiekt JSON

Korzystanie z WITH RECURSIVE (https://www.postgresql.org/docs/current/static/queries-with.html) i JSON Functions (https://www.postgresql.org/docs/current/static/functions-json.html) zbuduj to rozwiązanie:

db<>skrzypce

Podstawowa funkcjonalność:

    WITH RECURSIVE tree(node_id, ancestor, child, path, json) AS  (
      SELECT 
          t1.node_id, 
          NULL::int, 
          t2.node_id,
          '{children}'::text[] || 
             (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text,-- C
          jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[])) -- B
      FROM test t1
      LEFT JOIN test t2 ON t1.node_id = t2.parent_node                                   -- A
      WHERE t1.parent_node IS NULL

      UNION

      SELECT
          t1.node_id, 
          t1.parent_node, 
          t2.node_id,
          tree.path || '{children}' || (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text, 
          jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[]))
      FROM test t1
      LEFT JOIN test t2 ON t1.node_id = t2.parent_node
      INNER JOIN tree ON (t1.node_id = tree.child)
      WHERE t1.parent_node = tree.node_id                                                -- D
    )
    SELECT                                                                               -- E
        child as node_id, path, json 
    FROM tree 
    WHERE child IS NOT NULL ORDER BY path

Co WITH RECURSIVE zawiera początkowy SELECT i część rekurencji (drugi SELECT ) połączone przez UNION .

O:Dołączanie do tabeli samo w sobie w celu znalezienia dzieci node_id .

B:Budowanie obiektu json dla dziecka, które można wstawić do jego rodzica

C:Budowanie ścieżki, w której ma zostać wstawiony obiekt podrzędny (od korzenia). Funkcja okna row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html) generuje indeks dziecka w tablicy dzieci rodzica.

D:Część rekurencji działa jak część początkowa z jedną różnicą:nie szuka elementu głównego, ale elementu, który ma węzeł rodzicielski ostatniej rekurencji.

E:Wykonanie rekurencji i przefiltrowanie wszystkich elementów bez dzieci daje taki wynik:

node_id   path                      json
2         children,0                {"name": "node2", "children": []}
4         children,0,children,0     {"name": "node4", "children": []}
5         children,0,children,1     {"name": "node5", "children": []}
6         children,0,children,2     {"name": "node6", "children": []}
3         children,1                {"name": "node3", "children": []}
7         children,1,children,0     {"name": "node7", "children": []}
8         children,1,children,1     {"name": "node8", "children": []}

Chociaż nie znalazłem sposobu, aby dodać wszystkie elementy potomne w rekursji (źródło json nie jest zmienną globalną; więc zawsze zna zmiany bezpośrednich przodków, a nie ich rodzeństwa), musiałem iterować wiersze w ciągu kilku sekund.

Dlatego buduję funkcję. Tam mogę wykonać iterację dla zmiennej globalnej. Za pomocą funkcji jsonb_insert Wstawiam wszystkie obliczone elementy do głównego obiektu json - używając obliczonej ścieżki.

CREATE OR REPLACE FUNCTION json_tree() RETURNS jsonb AS $$
DECLARE
    _json_output jsonb;
    _temprow record;
BEGIN
    SELECT 
        jsonb_build_object('name', name, 'children', array_to_json(ARRAY[]::int[])) 
    INTO _json_output 
    FROM test 
    WHERE parent_node IS NULL;

    FOR _temprow IN
        /* Query above */
    LOOP
        SELECT jsonb_insert(_json_output, _temprow.path, _temprow.json) INTO _json_output;
    END LOOP;

    RETURN _json_output;
END;
$$ LANGUAGE plpgsql;

Ostatnim krokiem jest wywołanie funkcji i uczynienie JSON bardziej czytelnym (jsonb_pretty() )

{
    "name": "node1",
    "children": [{
        "name": "node2",
        "children": [{
            "name": "node4",
            "children": []
        },
        {
            "name": "node5",
            "children": []
        },
        {
            "name": "node6",
            "children": []
        }]
    },
    {
        "name": "node3",
        "children": [{
            "name": "node7",
            "children": []
        },
        {
            "name": "node8",
            "children": []
        }]
    }]
}

Jestem pewien, że można zoptymalizować zapytanie, ale w przypadku szkicu działa.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Odejmij dni od daty w PostgreSQL

  2. PostgreSQL, jak sprawdzić, które zapytania zostały uruchomione

  3. Dlaczego zapytanie nie zapisuje się w pliku csv, podczas gdy w konsoli postgresql wygląda to normalnie?

  4. Dołączanie (wypychanie) i usuwanie z tablicy JSON w PostgreSQL 9.5+

  5. Przekaż wiele wartości w jednym parametrze