gojoinormmany-to-manygo-gorm

Traversing many-to-many relationship in condition using gorm


I use a postgres database and naming strategy

schema.NamingStrategy{
    NoLowerCase:   true,
    SingularTable: true,
}

and have a database model like this:

type Project struct {
    gorm.Model         
    UserID     uint   `gorm:"not null"`
    User       User
}

type Bar struct {
    gorm.Model 
    ProjectID  uint `gorm:"not null"`
    Project    Project
    Foos   []*Foo `gorm:"many2many:Bar_Foos;"`
}

type Foo struct {
    gorm.Model       
    Bars            []*Bar `gorm:"many2many:Bar_Foos;"`
    ProjectID        uint    `gorm:"not null"`
    Project          Project
}

There is a many-to-many relationship between Foo and Bar. I want to show the user all of his Foos after he has selected a Bar. That is, I want to query for exactly those Foos where Foo.Bar.Project.UserID = $userId and Foo.Bar.ID = $barId. $userId and $barId are given in from outside.

I know that with TypeORM, this would be modeled using inner joins. I have tried this with gorm in many variations but none of them have worked. My most common issues were that gorm discarded my conditions and that it generated queries without a FROM clause.

How can this query be performed using gorm?


Solution

  • I figured it out: InnerJoins() does not seem to support many-to-many relations and preloading seems to be very broken (and also seems to happen after the fact, not "pre", which explains why the conditions don't apply). In addition to this, gorm seems to get confused when you try to use InnerJoins() beyond a relational point where you have already used a different join function (it seems to paste SQL code in the wrong part of the query). The solution therefore is to query everything starting at the many-to-many relation via Joins() and to just tell this function that you want an inner join:

        Joins("INNER JOIN \"Bar_Foos\" ON \"Bar_Foos\".\"FooID\" = \"Foo\".\"ID\"").
        Joins("INNER JOIN \"Bar\" ON \"Bar\".\"ID\" = \"Bar_Foos\".\"BarID\"").
        Joins("INNER JOIN \"Project\" ON \"Project\".\"ID\" = \"Bar\".\"ProjectID\"").
        Where("\"Project\".\"UserID\" = ?", user.UserID)