Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang gorm - preload with deeply nested models

Tags:

go

go-gorm

I have the following contrived example:

type Top struct {
  ID     uint `gorm:"primary_key"`
  Name   string
  Middle []*Middle
}

type Middle struct {
  ID    uint `gorm:"primary_key"`
  TopID int
  Name  string
  Low   []*Low
}

type Low struct {
  ID       uint `gorm:"primary_key"`
  MiddleID int
  Name     string
  Bottom   []*Bottom
}

type Bottom struct {
  ID    uint `gorm:"primary_key"`
  LowID int
  Name  string
}
top := Top{
  Name: "Top",
  Middle: []*Middle{
    {
      Name: "Middle",
      Low: []*Low{
        {
          Name: "Low",
          Bottom: []*Bottom{
            {
              Name: "Bottom",
            },
          },
        },
      },
    },
  },
}

if err := db.Save(&top).Error; err != nil {
  log.Fatal("Got errors when saving calc", err.Error())
}

if err := db.Where("id = 1").
  Preload("Middle.Low.Bottom").
  First(&top).Error; err != nil {
  log.Fatal(err)
}

This gives me the following output:

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [4.37ms]  INSERT INTO "tops" ("name") VALUES ('Top') RETURNING "tops"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [3.49ms]  INSERT INTO "middles" ("name","top_id") VALUES ('Middle','1') RETURNING "middles"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [1.07ms]  INSERT INTO "lows" ("middle_id","name") VALUES ('1','Low') RETURNING "lows"."id"

()
[2016-03-18 01:53:23]  [9.16ms]  INSERT INTO "bottoms" ("low_id","name") VALUES ('1','Bottom') RETURNING "bottoms"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:86)
[2016-03-18 01:53:23]  [1.54ms]  SELECT  * FROM "tops"   ORDER BY "tops"."id" ASC LIMIT 1

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [2.63ms]  SELECT  * FROM "tops"  WHERE ("id" = '1') ORDER BY "tops"."id" ASC LIMIT 1

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [1.65ms]  SELECT  * FROM "middles"  WHERE (top_id IN ('1')) ORDER BY "middles"."id" ASC

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [4.20ms]  SELECT  * FROM "lows"  WHERE (middle_id IN ('1')) ORDER BY "lows"."id" ASC

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  can't find field Bottom in []**main.Low

If I were to only nest Top -> Middle -> Low and called Preload("Middle.Low") it works fine. The clue is probably in the double pointer reference []**main.Low but I'm unable to work out how to do this properly.

It's possible that nested preloading is not supported to this level. Anyone know?

like image 966
Jim Gaylard Avatar asked Mar 18 '16 01:03

Jim Gaylard


1 Answers

This issue seems to have been present in earlier versions of gorm as described in: Issue1, Issue2, Issue3, but the lastest preload issue fix has been in this one.

It appears it has been resolved for quite some time but the exact version it was fixed is not documented unless someone searches git commit dates.

Setting up a current example with the latest version (v1.9.11 as of this writing) held no issues.

package main

import (
    "log"

    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
)

type Top struct {
    ID     uint `gorm:"primary_key"`
    Name   string
    Middle []*Middle
}

type Middle struct {
    ID    uint `gorm:"primary_key"`
    TopID int
    Name  string
    Low   []*Low
}

type Low struct {
    ID       uint `gorm:"primary_key"`
    MiddleID int
    Name     string
    Bottom   []*Bottom
}

type Bottom struct {
    ID    uint `gorm:"primary_key"`
    LowID int
    Name  string
}

func main() {
    db, err := gorm.Open("sqlite3", "test.db")
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    // Enable Logger, show detailed log
    db.LogMode(true)
    // Migrate the schema
    db.AutoMigrate(&Top{})
    db.AutoMigrate(&Middle{})
    db.AutoMigrate(&Low{})
    db.AutoMigrate(&Bottom{})

    top := Top{
        Name: "Top",
        Middle: []*Middle{
            {
                Name: "Middle",
                Low: []*Low{
                    {
                        Name: "Low",
                        Bottom: []*Bottom{
                            {
                                Name: "Bottom",
                            },
                        },
                    },
                },
            },
        },
    }

    if err := db.Save(&top).Error; err != nil {
        log.Fatal("Got errors when saving calc", err.Error())
    }
    var ntop Top
    if err := db.Where("id = 1").
        Preload("Middle.Low.Bottom").
        First(&ntop).Error; err != nil {
        log.Fatal(err)
    }

    spew.Dump(&ntop)
}

$ ./main.exe

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [2.00ms]  INSERT  INTO "tops" ("name") VALUES ('Top')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [0.00ms]  INSERT  INTO "middles" ("top_id","name") VALUES (1,'Middle')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [0.99ms]  INSERT  INTO "lows" ("middle_id","name") VALUES (1,'Low')
[1 rows affected or returned ]

()
[2019-10-31 14:44:23]  [0.00ms]  INSERT  INTO "bottoms" ("low_id","name") VALUES (1,'Bottom')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "tops"  WHERE (id = 1) ORDER BY "tops"."id" ASC LIMIT 1
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "middles"  WHERE ("top_id" IN (1)) ORDER BY "middles"."id" ASC
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "lows"  WHERE ("middle_id" IN (1)) ORDER BY "lows"."id" ASC
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.99ms]  SELECT * FROM "bottoms"  WHERE ("low_id" IN (1)) ORDER BY "bottoms"."id" ASC
[1 rows affected or returned ]
(*main.Top)(0xc00015f950)({
 ID: (uint) 1,
 Name: (string) (len=3) "Top",
 Middle: ([]*main.Middle) (len=1 cap=1) {
  (*main.Middle)(0xc000150fc0)({
   ID: (uint) 1,
   TopID: (int) 1,
   Name: (string) (len=6) "Middle",
   Low: ([]*main.Low) (len=1 cap=1) {
    (*main.Low)(0xc000151080)({
     ID: (uint) 1,
     MiddleID: (int) 1,
     Name: (string) (len=3) "Low",
     Bottom: ([]*main.Bottom) (len=1 cap=1) {
      (*main.Bottom)(0xc0001929c0)({
       ID: (uint) 1,
       LowID: (int) 1,
       Name: (string) (len=6) "Bottom"
      })
     }
    })
   }
  })
 }
})

like image 153
will7200 Avatar answered Oct 25 '22 07:10

will7200