Как объединить связанные данные

#json #go

#json #Вперед

Вопрос:

Мне нужна некоторая теоретическая / практическая помощь в организации кода.

У меня есть такая таблица в PostgreSQL базе данных. Таблица показывает взаимосвязь между организациями.

 | ORGANIZATION_ID | ORGANIZATION_NAME | PARENT_ORGANIZATION_ID | ORGANIZATION_RANG | TREE_ORGANIZATION_ID | TREE_ORGANIZATION_ NAME |
|-----------------|-------------------|------------------------|-------------------|----------------------|-------------------------|
| 1               | Google            |                        | 1                 | 1                   | Google                 |
| 2               | Nest              | 1                      | 2                 | 12                 | GoogleNest            |
| 3               | Verily            | 1                      | 2                 | 13                 | GoogleVerily          |
| 4               | Calico            |                        | 1                 | 4                   | Calico                 |
| 5               | ATAP              | 4                      | 2                 | 45                 | CalicoATAP            |
  

В моем приложении Go я создаю struct для этой таблицы, а затем выполняю SQL-запрос.

 type Organization struct {
    ID int `json:"organization_id"`
    Name string `json:"organization_name"`
    Rang int `json:"organization_rang"`
    Children []Organization `json:"children"`
}

var GetOrganizations = func(responseWriter http.ResponseWriter, request *http.Request) {
    rows,err := db.Query("select * from ORG")
    if err != nil {
        fmt.Println(err)
        return
    }

    defer rows.Close()

    var organizations []Organization

    for rows.Next() {
        var organization Organization

        err = rows.Scan(amp;organization.ID, amp;organization.Name, amp;organization.Rang)
        if err != nil {
            fmt.Println(err)
            return
        }

        organizations = append(organizations, organization)
    }

    utils.Response(responseWriter, http.StatusOK, organizations)
}
  

Мне нужно дать такой ответ. Что бы вы посоветовали реорганизовать в моем текущем коде?

 [
    {
        "organization_id": 1,
        "organization_name": "Google",
        "organization_rang": 1,
        "children": [
            {
                "organization_id": 2,
                "organization_name": "Nest",
                "organization_rang": 2,
                "children": null
            },
            {
                "organization_id": 3,
                "organization_name": "Verily",
                "organization_rang": 2,
                "children": null
            }
        ]
    },
    {
        "organization_id": 4,
        "organization_name": "Calico",
        "organization_rang": 1,
        "children": [
            {
                "organization_id": 2,
                "organization_name": "Nest",
                "organization_rang": 2,
                "children": null
            }
        ]
    }
]
  

Редактировать:

@antham например, я добавляю новую запись с именем Telsa . Как вы можете видеть, его родительским является Nest объект.

 | ORGANIZATION_ID | ORGANIZATION_NAME | PARENT_ORGANIZATION_ID | ORGANIZATION_RANG | TREE_ORGANIZATION_ID | TREE_ORGANIZATION_ NAME |
|-----------------|-------------------|------------------------|-------------------|----------------------|-------------------------|
| 1               | Google            |                        | 1                 | 1                   | Google                 |
| 2               | Nest              | 1                      | 2                 | 12                 | GoogleNest            |
| 3               | Verily            | 1                      | 2                 | 13                 | GoogleVerily          |
| 4               | Calico            |                        | 1                 | 4                   | Calico                 |
| 5               | ATAP              | 4                      | 2                 | 45                 | CalicoATAP            |
| 6               | Tesla             | 2                      | 3                 | 126               | GoogleNestTesla      |
  

Результат вашего кода:

 [
    {
        "organization_id": 1,
        "organization_name": "Google",
        "organization_rang": 1,
        "children": [
            {
                "organization_id": 3,
                "organization_name": "Verily",
                "organization_rang": 2,
                "children": null
            },
            {
                "organization_id": 2,
                "organization_name": "Nest",
                "organization_rang": 2,
                "children": [
                    {
                        "organization_id": 6,
                        "organization_name": "Tesla",
                        "organization_rang": 3,
                        "children": null
                    }
                ]
            }
        ]
    },
    {
        "organization_id": 2,
        "organization_name": "Nest",
        "organization_rang": 2,
        "children": [
            {
                "organization_id": 6,
                "organization_name": "Tesla",
                "organization_rang": 3,
                "children": null
            }
        ]
    },
    {
        "organization_id": 4,
        "organization_name": "Calico",
        "organization_rang": 1,
        "children": [
            {
                "organization_id": 5,
                "organization_name": "ATAP",
                "organization_rang": 2,
                "children": null
            }
        ]
    }
]
  

Комментарии:

1. Как select * ... из таблицы с 6 столбцами выполняется сканирование в 3 поля без ошибок?

2. gorm.io/docs/has_many.html если вы можете использовать 3-ю библиотеку, вы можете сохранить свою структуру практически такой же.

3. @mkopriva привет! Вы правы. SQL-запрос должен быть select * ORGANIZATION_ID, ORGANIZATION_NAME, ORGANIZATION_RANG from ORG . Текущий код возвращает мне 5 записей, и в конечном ответе у меня есть массив с 5 объектами, который не является тем, что я хочу. Как бы вы реализовали вложенность, как в моем случае?

4. @nvcnvn привет! Как именно этот ORM мог бы мне помочь? Можете ли вы показать практический пример для моего случая?

Ответ №1:

Если я правильно понимаю, что вы хотите сделать, здесь у вас есть неоптимизированный пример, это сделано с помощью sqlite, но то же самое будет работать с postgres :

 package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "sort"

    _ "github.com/mattn/go-sqlite3"
)

type Organization struct {
    ID       int             `json:"organization_id"`
    Name     string          `json:"organization_name"`
    Rang     int             `json:"organization_rang"`
    Children []*Organization `json:"children"`
}

func main() {
    db, err := sql.Open("sqlite3", "./database")
    if err != nil {
        log.Fatal(err)
    }

    defer db.Close()

    rows, err := db.Query("select ORGANIZATION_ID,ORGANIZATION_NAME,ORGANIZATION_RANG,PARENT_ORGANIZATION_ID from ORG")
    if err != nil {
        log.Fatal(err)
    }

    defer rows.Close()

    orgs := map[int]*Organization{}

    for rows.Next() {
        organization := amp;Organization{}
        var parentID sql.NullInt64

        if err = rows.Scan(amp;organization.ID, amp;organization.Name, amp;organization.Rang, amp;parentID); err != nil {
            log.Fatal(err)
        }

        if parentID.Valid {
            if parentOrg, ok := orgs[int(parentID.Int64)]; ok {
                parentOrg.Children = append(parentOrg.Children, organization)
            } else {
                orgs[int(parentID.Int64)] = amp;Organization{ID: int(parentID.Int64)}
                orgs[int(parentID.Int64)].Children = append(orgs[int(parentID.Int64)].Children, organization)
            }
        }

        if _, ok := orgs[organization.ID]; ok {
            orgs[organization.ID].Name = organization.Name
            orgs[organization.ID].Rang = organization.Rang
            continue
        }

        orgs[organization.ID] = organization
    }

    IDs := []int{}
    for k := range orgs {
        IDs = append(IDs, k)
    }
    sort.Ints(IDs)

    organizations := []Organization{}
    for _, ID := range IDs {
        if len(orgs[ID].Children) > 0 amp;amp; orgs[ID].Rang == 1 {
            organizations = append(organizations, *orgs[ID])
        }
    }

    content, err := json.MarshalIndent(organizations, "", "  ")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(content))
}
  

Я понимаю это :

 [
  {
    "organization_id": 1,
    "organization_name": "Google",
    "organization_rang": 1,
    "children": [
      {
        "organization_id": 2,
        "organization_name": "Nest",
        "organization_rang": 2,
        "children": null
      },
      {
        "organization_id": 3,
        "organization_name": "Verily",
        "organization_rang": 2,
        "children": null
      }
    ]
  },
  {
    "organization_id": 4,
    "organization_name": "Calico",
    "organization_rang": 1,
    "children": [
      {
        "organization_id": 5,
        "organization_name": "ATAP",
        "organization_rang": 2,
        "children": null
      }
    ]
  }
]
  

Комментарии:

1. Здравствуйте! Спасибо. На самом деле мне нужно показывать ответ только организациям верхнего уровня с выделенными дочерними элементами. Должен быть массив, содержащий только объекты Google и Calico . У каждого объекта есть дочерний элемент. Вы меня понимаете? Что мне нужно изменить в вашем коде для этой задачи? У вас есть какие-нибудь идеи?

2. Похоже, что ваш код работает только для постоянного уровня вложенности (rang). Можете ли вы добавить несколько дочерних элементов к Nest объекту только для теста? В моем случае уровень вложенности (rang) не является постоянным. Объекты, имеющие дочерние элементы, дублируются в основном массиве. Например, если вы добавите некоторый дочерний элемент к Nest объекту, вы заметите, что этот объект дублируется в основном массиве. У вас есть какие-нибудь идеи по этому поводу?

3. Если вы хотите знать, работает ли это для нескольких дочерних уровней, ответ — да, попробуйте, и вы увидите. Здесь это простой ответ, основанный на том, что я понял из того, что вы сбросили, поэтому вам нужно адаптировать этот скрипт в соответствии именно с вашими потребностями.

4. Не могли бы вы еще раз проверить мой пост, пожалуйста? Я добавляю информацию о результате вашего последнего кода. К сожалению, это работает некорректно.

5. Итак, что предполагается делать?