Skip to main content

Projections

A projection turns rows into a struct of your own. There are two ways to do it, and the difference is where the work happens.

SelectProject
RunsIn Go, once per rowIn SQL
FetchesEvery column of each joined tableOnly what you map
Can useAny Go codeColumns and expressions

Select​

Select takes a typed closure on a join. It reads naturally and can run any Go you like, because Go can't look inside a closure to turn it into SQL.

type Summary struct {
Author string
Title string
}

summaries, err := db.Posts.
Join(&P.User).
Where(ant.Eq(&P.Published, true)).
Select(func(p *Post, u *User) Summary {
return Summary{u.Name, p.Title}
}).
All(ctx)

The result has All, First, and Each, and it is []Summary, not a slice of pointers.

Project​

Project selects only the columns you map, so it's cheaper on wide tables. Each mapping is a pair of probes: where the value goes, then where it comes from.

var S = ant.Fields[Summary]()

summaries, err := ant.Project[Summary](
db.Posts.Join(&P.User).Where(ant.Eq(&P.Published, true)),
ant.Into(&S.Author, &U.Name),
ant.Into(&S.Title, &P.Title),
).All(ctx)

IntoExpr maps an expression. A column that can be NULL needs a nullable destination.

ant.Project[Summary](
db.Users.Where(ant.Eq(&U.Name, "Bob")),
ant.IntoExpr(&S.Author, ant.Upper(&U.Name)),
)

A computed field​

To keep whole models and add a value to each, tag a field computed and fill it with With. It's never written.

type Author struct {
ID int64
Name string
Posts []Post
PostCount int64 `ant:"computed"`
}

var A = ant.Fields[Author]()

authors, err := db.Authors.
With(&A.PostCount, ant.CountRelated(&A.Posts)).
All(ctx)