Пример 1

Запрос к БД

Задача: хотим получить количество записей с разбивкой по датам (на основе поля created_at типа TIMESTAMP) в формате JSON.

{
  "visits": {
    "2024-11-13": "250",
    "2024-11-14": "54",
    "2024-11-15": "263",
    "2024-11-16": "63",
    "2024-11-17": "26",
    "2024-11-18": "30",
    "2024-11-19": "14",
    "2024-11-20": "9",
    "2024-11-21": "4",
    "2024-11-22": "4"
  }
}

В результате запроса, из БД придёт одна строка с одним столбцом json_build_object с приведённым выше JSON.

Запрос:

SELECT
    json_build_object(
        'visits', visits
    )
FROM (
	select json_object(
		array_agg(v1.created_date::text),
		array_agg(v1.visits::text)
	) as visits
	from (
		select
			date(created_at) as created_date,
			COUNT(*) as visits
		from visits
		where link_id = 372
		group by created_date
		order by created_date asc
	) v1
)

В данном случае, json_object принимает два массива и попарно соединяет их элементы в качестве ключа и значения в объекте JSON.

Работа из кода на Go

type Visits struct {
	Visits map[string]string `json:"visits"`
}
 
func (s *Visits) Scan(value interface{}) error {
	b, ok := value.([]byte)
	if !ok {
		return errors.New("type assertion to []byte failed")
	}
	return json.Unmarshal(b, &s)
}
 
// ...
 
visitsQuery := fmt.Sprintf(`
	SELECT
	json_build_object(
		'visits', visits
	)
	FROM (
		SELECT json_object(
			array_agg(v1.created_date::text),
			array_agg(v1.visits::text)
		) AS visits
		FROM (
			SELECT
				date(created_at) AS created_date,
				COUNT(*) AS visits
			FROM visits
			GROUP BY created_date
			ORDER BY created_date ASC
		) v1
	)`,
)
 
ctx, cancel := context.WithTimeout(context.Background(), defaultQueryTimeout)
defer cancel()
 
rows, err := m.db.QueryContext(ctx, query)
if err != nil {
	return nil, err
}
 
rows.Next()
visits := Visits{}
err = rows.Scan(&visits)
if err != nil {
	return nil, err
}
 

Пример 2

Запрос к БД

Задача: хотим получить из БД список записей в формате JSON в таком виде:

{
      "oses": [
        {
          "os": "iOS",
          "visit_count": 343
        },
        {
          "os": "Android",
          "visit_count": 290
        },
        {
          "os": "Windows",
          "visit_count": 32
        },
        {
          "os": "MacOS",
          "visit_count": 19
        },
        {
          "os": "",
          "visit_count": 18
        },
        {
          "os": "Linux",
          "visit_count": 15
        }
      ]
    }

Запрос:

SELECT
    json_build_object(
        'oses', oses
    )
FROM (
	select json_agg(v1) as oses
	from (
		select os, count(os) as visit_count
		from visits 
		where link_id = 372
		and (date(created_at) between '2024-11-13' and '2024-11-24')
		group by os
		order by visit_count desc
	) v1
)

В результате запроса, из БД придёт одна строка с одним столбцом json_build_object с приведённым выше JSON.

Работа из кода на Go

Аналогично первому примеру, но нужно по другому определить тип.

type OsStatsItem struct {
	OS         string `json:"os"`
	VisitCount int64  `json:"visit_count"`
}
 
type OsStats struct {
	Oses []*OsStatsItem `json:"oses"`
}
 
func (s *OsStats) Scan(value interface{}) error {
	b, ok := value.([]byte)
	if !ok {
		return errors.New("type assertion to []byte failed")
	}
	return json.Unmarshal(b, &s)
}

В остальном, получение этой записи из БД будет аналогично.

Справочные материалы


📂 PostgreSQL | Go


📂 SQL | Последнее изменение: 27.11.2024 13:44