Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scan() does not populate has-many relationships #606

Open
naltimari opened this issue Jul 7, 2022 · 6 comments
Open

Scan() does not populate has-many relationships #606

naltimari opened this issue Jul 7, 2022 · 6 comments

Comments

@naltimari
Copy link

In context of the 'has-many' example, if you do:

users := make([]User, 0)

db.NewSelect().
    Model((*User)(nil)).
    Relation("Profiles").
    Scan(ctx, &users)

users[k].Profiles will always be nil, regardless of the related Profiles they might have.

I don't think this is by design, as the documentation states that you can pass a struct as a 'destination', and no mention about the above behaviour.

@l-farrell
Copy link
Contributor

I am also experiencing this. I think I found what is happening:

The value of model used here to find joins:

https://github.com/uptrace/bun/blob/master/query_select.go#L877

Will not have any, because its derived from dst earlier:

https://github.com/uptrace/bun/blob/master/query_select.go#L849

and getModel doesn't copy across the joins when dst is non-zero:

https://github.com/uptrace/bun/blob/master/query_base.go#L188

@mehow-ves
Copy link

mehow-ves commented Nov 3, 2022

This works for me, bun 1.1.8

// models.go
type Match struct {
	bun.BaseModel `bun:"table:match,alias:m"`

	Match_id      int       `json:"id" bun:",pk,autoincrement"`
	Creator       int       `json:"creator"`
}

type User struct {
	bun.BaseModel `bun:"table:user,alias:u"`

	User_id  int      `json:"id" bun:",pk,autoincrement"`
	Username string   `json:"username"`
	Matches  []*Match `json:"matches" bun:"rel:has-many,join:user_id=creator"`
}

// schema.resolvers.go
var user []*model.User
r.DB.NewSelect().Model(&user).Relation("Matches", func(q *bun.SelectQuery) *bun.SelectQuery {
     return q.Column("match_id", "creator")
}).Scan(ctx)

Although without a data loader it is inefficient as it executes a separate query for every Match for every User.

@amamrenko
Copy link

any update on that issue?

@steve-hb
Copy link

steve-hb commented Feb 1, 2024

Same here, this behaviour took me about two days to figure out - imposter syndrome included.

@ijt
Copy link

ijt commented Jul 5, 2024

I've been bitten by this as well, ended up doing crappy workarounds until I figured out what was wrong.

@RobertV17
Copy link

Hi all! I faced a similar issue but found a solution by passing only the context to the .Scan() method without the model variable. It wasn’t obvious at first, but here's an example (I used version v1.2.3):

type Account struct {
	bun.BaseModel `bun:"table:account"`
	ID            int64         `bun:"id,pk,autoincrement"`
	Username      string        `bun:"username,notnull"`
	Email         string        `bun:"email,notnull"`
	Password      string        `bun:"password,notnull"`
	CreatedAt     time.Time     `bun:"created_at,nullzero,default:current_timestamp"`
	PayAccounts   []*PayAccount `bun:"rel:has-many,join:id=account_id"`
}

type PayAccount struct {
	bun.BaseModel `bun:"table:pay_account"`
	ID            int64     `bun:"id,pk,autoincrement"`
	Username      string    `bun:"username,notnull"`
	Email         string    `bun:"email,notnull"`
	Password      string    `bun:"password,notnull"`
	AccountId     int64     `bun:"account_id,notnull"`
	CreatedAt     time.Time `bun:"created_at,nullzero,default:current_timestamp"`
}

func main() {
	postgresURL := &url.URL{
		Scheme: "postgres",
		User:   url.UserPassword("service", "service-pass"),
		Host:   fmt.Sprintf("%s:%s", "localhost", "5433"),
		Path:   "service-db",
	}
	connString := postgresURL.String()

	config, err := pgx.ParseConfig(connString)
	if err != nil {
		log.Panic().Err(err).Msg("Failed to parse PostgreSQL config")
	}

	sqldb := stdlib.OpenDB(*config)

	err = sqldb.Ping()

	if err != nil {
		log.Panic().Err(err).Msg("Failed to ping PostgreSQL")
	}

	db := bun.NewDB(sqldb, pgdialect.New())

	var accounts []*Account

	q := db.NewSelect().Model(&accounts).Relation("PayAccounts")

	err = q.Scan(context.Background())
	fmt.Println(accounts)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants