PSU2017
PSU2017

Reputation: 133

GORM Recursive Preloading

Using Go 1.10, and CockroachDB via the Postgres pq driver.

I have a GORM model that looks like this:

type User struct {
    ID            string       `gorm:"type:UUID;primary_key;NOT_NULL"`
    UserName      string
    ... <other misc things here>
    EnclosedUsers []User    `gorm:"many2many:enclosed_user;jointable_foreignkey:parent_id;association_jointable_foreignkey:child_id"`
}

where ecnlosed_user is (specifically defined because reasons :) )

type EnclosedUsers struct {
    ParentID string `gorm:"type:UUID;default:'00000000-0000-0000-0000-000000000000'"`
    ChildID  string `gorm:"type:UUID;default:'00000000-0000-0000-0000-000000000000'"`
}

That is, each user can have 0 or many enclosed users, and each user may have many parent users. Im trying to preload all of the enclosed users for each user, however GORM only preloads the first level.

i.e.:

usr1
   |-> usr 2
   |     |->usr3
   |
   |-> usr4
         |->usr6

 usr5
   |->usr7

The only users that are loaded are usr1, usr2, usr4, usr5, and usr7. usr3 or usr 6 aren't. Im currently trying to recursively force the enclosed users to load with an AfterFind callback:

func (u *User) AfterFind(scope *gorm.Scope, role *CRRole) error {
    var childUsers []User

    if err := scope.DB().Model(&u).Related(&childUsers, "EnclosedUsers").Error; err != nil {
        return err
    }
    role.EnclosedRoles = childRoles

    return nil
}

However this generates the following SQL:

SELECT "users".* FROM "users" INNER JOIN "enclosed_users" ON "enclosed_users"."child_id" = "enclosed_users"."id" WHERE (user_name = 'test user 1') AND ("enclosed_users"."parent_id" IN ('<UUID HERE>')) 

if the user_name = ... wasn't there, the query would work perfectly.

Any ideas would be greatly appreciated.

Upvotes: 3

Views: 2200

Answers (2)

phantom_wizard
phantom_wizard

Reputation: 3168

I used this approach https://github.com/go-gorm/gorm/issues/4027 and worked for me perfect.

copy paste from example:

type Menu struct{
   Id          int       `json:"id" gorm:"primaryKey;type:int"`
   Pid         int       `json:"pid" gorm:"primaryKey;type:int"`
   Children    []Menu  `json:"children" gorm:"foreignKey:pid"`
}
func  GetMenusTree() ([]Menu, error) {
    var views []Menu
    err := db.Model(&Menu{}).Where("pid = ?", 0).Preload(clause.Associations, preload).Find(&views).Error
    if err != nil {
        return nil, err
    }
    return views, nil
}

func preload(d *gorm.DB) *gorm.DB {
    return d.Preload("Children", preload)
}

Upvotes: 0

PSU2017
PSU2017

Reputation: 133

I ended up forking the repository and removing callback_query_preload.go at line 331. Works like a charm.

Upvotes: 2

Related Questions