Your Question

I have the following entities:

type User struct {
  ID     string
  Name   string
  Groups []Group `gorm:"many2many:users_groups"`
}

type Group struct {
  ID   string
  Name string
}

I know I can preload the groups using

var users []Users
db.Preload("Groups").Find(&users)

And I can also filter Users using

var users []Users
db.Preload("Groups").Where("name IN ?", []string{"name1", "name2"}).Find(&users)

This will bring all the User that have name equals "name1" or "name2"

But I cannot filter Users based on Groups

var users []Users
db.Preload("Groups", "name IN ?", []string{"groupname"}).Find(&users)

I expect it to bring all Users that have Group Name equal "groupname"

How can I achieve this using database only? (my database is big and I cannot load all users into memory and filter it in the application)

Comment From: jinzhu

I expect it to bring all Users that have Group Name equal "groupname"

You need to use join in this case

Comment From: lopes-gustavo

But how can I do that?

If I use .Join, I'll end up with repeated values... And the only way I could imagine using Join is mapping the rows myself into the entities. But if I have to map myself the rows into the models, there's no point in using an ORM.

Is there a way to do that automatically that I'm not seeing?

Someone suggested in this StackOverflow question to use Join inside the Preload, but that simply filter the association, instead of the main model.

Comment From: take

@lopes-gustavo were you able to find the solution for this problem? 👀 I'm having the same problem

Comment From: lopes-gustavo

No, sorry. I ended up using Join and mapping the rows myself. Not the best solution, but it worked in the timeframe we had

Comment From: AlexThurston

Is using Join the only way to solve this? I'm implementing a basic tagging structure and am surprised that this isn't simpler.

Comment From: genofire

With using Join i got duplicated entries of User if the User has both Groups in the Join(, WHERE)

Comment From: or-shek

you can use: db.Preload("Groups", "name IN (?)", []string{"name1", "name2"}).Find(&users)

Comment From: genofire

@or-shek it also just filter the Groups, not the users (by there groups)

Comment From: goxiaoy

db.Joins("JOIN user_groups on user_groups.user_id = users.id JOIN groups on user_groups.group_id = groups.id AND groups.name in ? ", []string{"name1", "name2"}).Group("users.id")

Comment From: the25x8

You may fetch users by group names very easily:

db.Preload("Groups").Where("id IN (SELECT user_id FROM users_groups WHERE name IN ?)", []string{"name1", "name2"})

or more strict way:

db.Where("id IN (?)", db.Table("users_groups").
    Select("user_id").
    Where("name IN ?", []string{"name1", "name2"}),
)

Comment From: cendiastian

db.Joins("JOIN user_groups on user_groups.user_id = users.id JOIN groups on user_groups.group_id = groups.id AND groups.name in ? ", []string{"name1", "name2"}).Group("users.id")

this is works! but you need to preload before join, coz when i test your code, my many to many field still null, thanks anyway @Goxiaoy

Comment From: genofire

I got it working with INNER join.

Comment From: yuseferi

I've provided a solution with inner join : https://stackoverflow.com/questions/63475885/how-to-query-a-many2many-relationship-with-a-where-clause-on-the-association-wit/73797763#73797763

Comment From: whyayala

db.Joins("JOIN user_groups on user_groups.user_id = users.id JOIN groups on user_groups.group_id = groups.id AND groups.name in ? ", []string{"name1", "name2"}).Group("users.id")

This worked. Thanks a lot.

Comment From: itaranto

I've provided a solution with inner join : https://stackoverflow.com/questions/63475885/how-to-query-a-many2many-relationship-with-a-where-clause-on-the-association-wit/73797763#73797763

Your solution has the right approach but it has a small mistake:

 var users []Users
 db.Preload("Groups").
 Joins("inner join user_groups ug on ug.user_id = user.id").
-Joins("inner join groups g on g.id = ug.user_id").
+Joins("inner join groups g on g.id = ug.group_id").
 Where("g.name IN ?", []string{"name1", "name2"}).Find(&users)

Comment From: yuseferi

Hey @itaranto , I don't remember. it's been a long time since we are not using GORM. but thank you for your attention. btw, why do you have makefile? :D