Answer the question
In order to leave comments, you need to log in
What is the correct way to sterilize such a Golang + Postgres query?
Actually there is such a request
select u.username, p.post_id, p.post_title, p.post_text
from posts p join users u on u.user_id = p.post_author
group by u.username, p.post_id;
type userposts struct {
username string
posts [массив постов]
}
Answer the question
In order to leave comments, you need to log in
In fact, there are several solutions to the problem, and all of them are implemented at the time of the table scan.
First you will need to pull out all the data, either into some variables or into an intermediate structure. If there are few fields, then you can use variables, if there are many, then automapping to a structure is more convenient ( sqlx with line scan or sqlstruct )
I will make examples based on manual scanning to make it clearer.
Our foundation:
type Post struct {
ID int64
Title string
Text string
}
type UserPosts struct {
Username string
Posts []Post
}
func qna864719(db *sql.DB) ([]UserPosts, error) {
query := `select u.username
, p.post_id
, p.post_title
, p.post_text
from posts p
join users u on u.user_id = p.post_author
group by u.username, p.post_id;`
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
defer rows.Close()
var result []UserPosts
for rows.Next() {
var username string
var post Post
err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
if err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
// Здесь логика преобразования результата в массив UserPosts
}
err = rows.Err()
if err != nil {
return nil, fmt.Errorf("after scan: %w", err)
}
return result, nil
}
func qna864719_1(db *sql.DB) ([]UserPosts, error) {
query := `select u.username
, p.post_id
, p.post_title
, p.post_text
from posts p
join users u on u.user_id = p.post_author
group by u.username, p.post_id
order by u.username` // <-------- ОБЯЗАТЕЛЬНАЯ СОРТИРОВКА
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
defer rows.Close()
var result []UserPosts
// Храним промежуточный результат по юзеру в переменной,
// перед добавлением в основной массив
var lastResult *UserPosts // <--------
for rows.Next() {
var username string
var post Post
err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
if err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
// Здесь логика преобразования результата в массив UserPosts
// Если промежуточный результат существует, но username отличается
// от текущего, то добавляем промежуточный результат в основной массив
// обнуляя промежуточный результат
if lastResult != nil && lastResult.Username != username {
result = append(result, *lastResult)
lastResult = nil
}
// Если промежуточного результата нет, иницилизируем его
if lastResult == nil {
lastResult = &UserPosts{Username: username}
}
// Добавляем посты
lastResult.Posts = append(lastResult.Posts, post)
}
err = rows.Err()
if err != nil {
return nil, fmt.Errorf("after scan: %w", err)
}
// После выхода из сканирования, у нас может остаться промежуточный результат
// который необходимо добавить в основной массив
if lastResult != nil {
result = append(result, *lastResult)
}
return result, nil
}
func qna864719_2(db *sql.DB) ([]UserPosts, error) {
query := `select u.username
, p.post_id
, p.post_title
, p.post_text
from posts p
join users u on u.user_id = p.post_author
group by u.username, p.post_id;`
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("query: %w", err)
}
defer rows.Close()
var result []UserPosts
// Иницилизируем хеш-мап который будет содержать посты по username
postsByUsername := make(map[string][]Post)
for rows.Next() {
var username string
var post Post
err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
if err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
// Здесь логика преобразования результата в массив UserPosts
// Добавляем посты в мап
postsByUsername[username] = append(postsByUsername[username], post)
}
err = rows.Err()
if err != nil {
return nil, fmt.Errorf("after scan: %w", err)
}
// Преобразовываем мап в массив
for username, posts := range postsByUsername {
result = append(result, UserPosts{
Username: username,
Posts: posts,
})
}
// Сортируем, так как в мапе записи хранятся в "случайном" порядке
sort.Slice(result, func(i, j int) bool {
return result[i].Username < result[j].Username
})
return result, nil
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question