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

Gorm Nested Preloading

Expected answer

Code snippet for using Preload recursively