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

Closed
naltimari opened this issue Jul 7, 2022 · 7 comments · Fixed by #1099
Closed

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

naltimari opened this issue Jul 7, 2022 · 7 comments · Fixed by #1099
Assignees
Labels
bug Something isn't working help wanted Extra attention is needed

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)
}

@j2gg0s j2gg0s added the bug Something isn't working label Jan 3, 2025
@j2gg0s
Copy link
Collaborator

j2gg0s commented Jan 3, 2025

	users := make([]User, 0)
	if err := db.NewSelect().
		Model(&users).
		Relation("Profiles").
		Scan(ctx); err != nil {
		panic(err)
	}

If the dest parameter is not used, this issue can be avoided.
I will try to fix this issue.

@j2gg0s j2gg0s self-assigned this Jan 3, 2025
j2gg0s added a commit to j2gg0s/bun that referenced this issue Jan 3, 2025
@j2gg0s j2gg0s added the help wanted Extra attention is needed label Jan 3, 2025
@j2gg0s j2gg0s closed this as completed in 8296774 Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants