Как сформировать деревья в json используя golang?

Добрый день! Для хранения деревьев использую ltree в postgres 14.
В результате sql запроса получаю данные
6390a06eecfd9972471517.png
path это по сути хлебные крошки (ветвь дерева) состящая из id

Нужно отдать их в виде дерева в формате json
[
    {
        "id": 1,
        "name": "Беларусь",
        "childrens": [
            {
                "id": 3,
                "name": "Минск",
                "childrens": []
            }
        ]
    },
    {
        "id": 2,
        "name": "Россия",
        "childrens": [
            {
                "id": 4,
                "name": "Москва",
                "childrens": []
            }, 
            {
                "id": 5,
                "name": "Санкт-Петербург",
                "childrens": [
                    {
                        "id": 6,
                        "name": "Невский р-н",
                        "childrens": []
                    }
                ]
            }
        ]
    }
]


Вытащить данные бд в слайс структур не проблема, а вот как сформировать деревья?

Шаблон по запросу из комментариев
spoiler
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/jackc/pgx/v4"
)

// Структура бд
//CREATE EXTENSION ltree;
//CREATE TABLE IF NOT EXISTS "objects" (
//   id SERIAL PRIMARY KEY,
//   name TEXT NOT NULL,
//   path ltree
//);

type DBData struct {
	ID        int64    `json:"id"`
	Name      string   `json:"name" validate:"required,max=50"`
	Path      string   `json:"path"`
}

type Tree struct {
	ID        int64    `json:"id"`
	Name      string   `json:"name" validate:"required,max=50"`
	Path      string   `json:"path"`
	Childrens []Folder `json:"childrens"`
}

func GetDBData() []DBData {
	conn, _ := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
	defer conn.Close(context.Background())

	// например получаю деревья с 3 уровнями вложенности, но может быть больше
	query := `select * from objects where path ~ '*{1,3}'`  

	rows, _ := conn.Query(ctx, query, userID)
	defer rows.Close()

	var data []DBData 
	for rows.Next() {
		var d DBData

		_ = rows.Scan(&d.ID, &d.Name, &d.Path);

		data = append(data, d)
	}

	return data
}

func main() {
	// data := GetDBData() 
	// В результате этого запроса получаю
	data := []DBData{
		{ID: 1, Name: "Беларусь", Path: "1"},
		{ID: 2, Name: "Россия", Path: "2"},
		{ID: 3, Name: "Минск", Path: "1.3"},
		{ID: 4, Name: "Москва", Path: "2.4"},
		{ID: 5, Name: "Санкт-Петербург", Path: "2.5"},
		{ID: 6, Name: "Приморский р-н", Path: "2.5.6"},
	}

}
  • Вопрос задан
  • 414 просмотров
Пригласить эксперта
Ответы на вопрос 2
mayton2019
@mayton2019
Bigdata Engineer
Я-бы предложил для начала уйти от этого странного способа описанию деревьев к инверсному списку
По сути - добавить еще одну колонку которая указывает на родителя.

id name     path  parent
-----------------------
1  Беларусь 1     null
2  Россия   2     null
3  Минск    1.3   1
4  Москва   2.4   2
5  СПБ      2.5   2
6  Невский  2.5.6 5


Ну а дальше - select... connect prior и получим ранжированый курсор по дереву. Там-же будет
доступно виртуальное поле level. И обходом этого курсора можно сделать JSON документ.
Если level растет - то увеличиваем indentation и открываем вложенный элемент соотв.

Если go-lang поддерживает stremable json writers то даже лучше.
Ответ написан
@falconandy
1. Достаете из БД все записи.
2. Для каждой записи строковый path конвертируете в массив (слайс) числовых индексов (1.2.3 -> [1, 2, 3])
3. Сортируете записи по этому массиву в лексикографическом порядке: [1], [1,3], [2], [2,4], [2,5], [2,5,6]
4. За один проход по отсортированному списку записей вручную формируете итоговый json. Можно реализовать рекурсивно или итеративно.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы