Your Question

How can I add Limit clause to Association Preload to reduce the number association of rows if many to improve performance.

The document you expected this should be explained

type User struct { ID int
Fullname string
Email string Post []Post }

type Post struct { ID int
Content string
UserID int }

If user's posts are more than a thousand, it would not be wise to preload it all with user, I want to get only the last ten posts.

db.Preload("Post").Find(&user) would fetch all the posts associated with the user.

I want something like db.Preload("Post", "limit:10,DESC").Find(&user).

I only used "limit:10,DESC" to explain my point I know there's nothing like that.

Please I need help with this ASAP.

Thanks

Comment From: a631807682

    DB.Preload("Post", func(tx *gorm.DB) *gorm.DB {
        return tx.Limit(10)
    }).Find(&user)

https://gorm.io/docs/preload.html#Custom-Preloading-SQL

Comment From: Mrbeyond

go DB.Preload("Post", func(tx *gorm.DB) *gorm.DB { return tx.Limit(10) }).Find(&user)

https://gorm.io/docs/preload.html#Custom-Preloading-SQL

This is perfect. Thanks.

Comment From: ElegantSoft

@Mrbeyond This will only get 10 posts for all user or users. is there any solution if I will get many users and want to get only 10 posts for each user

Comment From: Mrbeyond

@ElegantSoft I later faced the same issue.

I created and issue on that as well but no response from anybody yet.

Comment From: ElegantSoft

I found a solution with lateral join but it has bad performance. Finally I decided to set column for last comment and get only one comment text and user name from post table. And when user open post I will request the others. This for your example. In my situation ai have rooms and message and I am storing last message text on room table and get messages when user click on room

Comment From: Mrbeyond

@ElegantSoft Thanks for your feedback. That's creative.

This is a serious issue that go-gorm has to look into.

I remember having similar issue with laravel back then in 2019. I was able to resolve it quickly through nested callback query having similar approach of gorm.DB callback.

Comment From: jinzhu

This is not something could be resolved by GORM w/o causing the N+1 problem or bad performance by using lateral join.

You should think about your data structure, for example, maybe you can add a index to user's posts and only query posts with index <= 10, e.g:

DB.Preload("Post", func(tx *gorm.DB) *gorm.DB {
  return tx.Where("index < ?", 10)
}).Find(&user)

Comment From: Mrbeyond

@jinzhu Thanks. Much appreciated.

Comment From: ZheruiL

Hi @jinzhu , in sql there's a solution for preloading:

SELECT *
FROM posts
WHERE posts.id IN (select max(id)
                        from posts
                        WHERE posts.user_id IN (1, 2)
                        group by user_id);

But with gorm what I can do is:

func(tx *gorm.DB) *gorm.DB {
    return tx.Where("posts.id in (?)",
        tx.Select("max(id)").Model(&model.Post{}).Group("user_id"),
    )
}
SELECT *
FROM posts
WHERE posts.id IN (select max(id)
                        from posts
--                      WHERE posts.user_id IN (1, 2) <- this line is missing
                        group by user_id);

The problem here is that the where in condition can not be added in the preload subquery as I don't know the ids here. Is there a way to add the where in conditions for the sub query ?