Your Question
I have a reply table that looks like this:
type Reply struct {
ID string `gorm:"column:id; type:varchar(36); not null; primaryKey"`
PostID sql.NullString `gorm:"column:post_id; type:varchar(36); default:null"`
ReplyID sql.NullString `gorm:"column:reply_id; type:varchar(36); default:null"`
UserID string `gorm:"column:user_id; type:varchar(36); not null"`
Caption sql.NullString `gorm:"column:caption; type:text"`
CreatedAt time.Time `gorm:"column:created_at; type:timestamp; not null; autoCreateTime"`
UpdatedAt sql.NullTime `gorm:"column:updated_at; type:timestamp; default:null; autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at; type:timestamp; default:null"`
Likes []*Like `gorm:"foreignKey:ReplyID"`
Replies []*Reply `gorm:"foreignKey:ReplyID"`
User *User
}
my attempt was using Preload() but it doesn't seem to work since I don't know how deep the replies are
var res []domain.Post
err = db.
Preload("User").
Preload("Attachments").
Preload("Likes").
Preload("Replies").
Preload("Replies.Replies").
Preload("Replies.Likes").
Find(&res).Error
Attempt 2:
func ReplyPreload(db *gorm.DB, depth int) *gorm.DB {
if depth == 10 {
return db.
Preload(clause.Associations).
Order("replies.created_at").
Limit(10).
Offset(0)
}
return db.
Preload("Replies", ReplyPreload(db, depth+1)).
Order("replies.created_at").
Limit(10).
Offset(0)
}
err = db.
Joins("User").
Preload("Attachments", func(db *gorm.DB) *gorm.DB {
return db.Order("attachments.index_in_post ASC")
}).
Preload("Replies", ReplyPreload(db, 0)).
Find(&post)
attempt 2 doesn't work either.
But when I use something like:
err = db.
Joins("User").
Preload("Attachments", func(db *gorm.DB) *gorm.DB {
return db.Order("attachments.index_in_post ASC")
}).
Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Preload(clause.Associations)
})
})
})
})
})
})
}).
Order("created_at desc").
Limit(1).
Find(&res).Error
works, but it's not pretty
this also works but still not pretty
err = db.
Preload(clause.Associations).
Preload("Attachments", func(db *gorm.DB) *gorm.DB {
return db.Order("attachments.index_in_post ASC")
}).
Preload("Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Preload("Replies.Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Preload("Replies.Replies.Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Preload("Replies.Replies.Replies.Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Preload("Replies.Replies.Replies.Replies.Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Preload("Replies.Replies.Replies.Replies.Replies.Replies", func(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}).
Order("created_at desc").
Limit(1).
Find(&res).Error
but when I do something like
func ReplyPreload(db *gorm.DB) *gorm.DB {
return db.Order("replies.created_at").Preload(clause.Associations)
}
Preload("Replies", ReplyPreload(db)).
Preload("Replies.Replies", ReplyPreload(db)).
it doesn't work
attempt 3:
currently, my best solution is something like this:
repliesPreload := func(db *gorm.DB) *gorm.DB {
return db.
Preload(clause.Associations).
Limit(10).
Offset(0).
Order("replies.created_at")
}
err := db.
Preload(clause.Associations).
Preload("Replies", repliesPreload).
Preload("Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies", repliesPreload).
Preload("Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies.Replies", repliesPreload).
Find(&res).Error
if err != nil {
panic(err)
}
but I'm still looking for more pretty solution
attempt 4:
finally i made it
var depth = 1
var recursivePreload func(db *gorm.DB) *gorm.DB
recursivePreload = func(db *gorm.DB) *gorm.DB {
if depth == 10 {
return db.
Preload(clause.Associations).
Limit(10).
Offset(0).
Order("replies.created_at")
}
depth++
return db.
Preload(clause.Associations).
Limit(10).
Offset(0).
Order("replies.created_at").
Preload("Replies", recursivePreload)
}
// caller
err := db.
Where("id = ?", id).
Preload(clause.Associations).
Preload("Replies", recursivePreload).
Find(&replies).Error
if err != nil {
panic(err)
}
The document you expected this should be explained
Expected answer
Code snippet for using Preload recursively