Как сформировать деревья в 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"},
	}

}
  • Вопрос задан
  • 346 просмотров
Пригласить эксперта
Ответы на вопрос 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. Можно реализовать рекурсивно или итеративно.
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы