Files
ent/doc/md/aggregate.md
Shivam Sandbhor 554b9d0850 doc/website: add docs for HAVING + GROUP BY clause (#2326)
* Add docs for HAVING + GROUP BY clause

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>

* Make review changes for docs

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
2022-02-15 10:21:16 +02:00

2.4 KiB
Executable File

id, title
id title
aggregate Aggregation

Group By

Group by name and age fields of all users, and sum their total age.

package main

import (
	"context"
	
	"<project>/ent"
	"<project>/ent/user"
)

func Do(ctx context.Context, client *ent.Client) {
	var v []struct {
		Name  string `json:"name"`
		Age   int    `json:"age"`
		Sum   int    `json:"sum"`
		Count int    `json:"count"`
	}
	err := client.User.Query().
		GroupBy(user.FieldName, user.FieldAge).
		Aggregate(ent.Count(), ent.Sum(user.FieldAge)).
		Scan(ctx, &v)
}

Group by one field.

package main

import (
	"context"
	
	"<project>/ent"
	"<project>/ent/user"
)

func Do(ctx context.Context, client *ent.Client) {
	names, err := client.User.
		Query().
		GroupBy(user.FieldName).
		Strings(ctx)
}

Group By Edge

Custom aggregation functions can be useful if you want to write your own storage-specific logic.

The following shows how to group by the id and the name of all users and calculate the average age of their pets.

package main

import (
	"context"
	"log"

	"<project>/ent"
	"<project>/ent/pet"
	"<project>/ent/user"
)

func Do(ctx context.Context, client *ent.Client) {
	var users []struct {
		ID      int
		Name    string
		Average float64
	}
	err := client.User.Query().
		GroupBy(user.FieldID, user.FieldName).
		Aggregate(func(s *sql.Selector) string {
			t := sql.Table(pet.Table)
			s.Join(t).On(s.C(user.FieldID), t.C(pet.OwnerColumn))
			return sql.As(sql.Avg(t.C(pet.FieldAge)), "average")
		}).
		Scan(ctx, &users)
}

Having + Group By

This example requires custom SQL modifiers. See how to enable them here

In this example we're going to query the oldest users for each role.

package main

import (
	"context"
	"log"

	"entgo.io/ent/dialect/sql"
	"<project>/ent"
	"<project>/ent/user"
)

func Do(ctx context.Context, client *ent.Client) {
	var users []struct {
		Id    	Int
		Age     Int
		Role    string
	}

	client.User.Query().Modify(
		func(s *sql.Selector) {
			s.GroupBy(
				User.Role,
			)
			s.Having(
				sql.EQ(
					user.FieldAge,
					sql.Raw(sql.Max(user.FieldAge)),
				),
			)
		}
	).ScanX(ctx, &users)
}

Note: The sql.Raw is crucial to have. It tells the predicate that sql.Max is not an arguement.

The above code essentially generates the following SQL query:

SELECT * FROM user
GROUP BY user.role
HAVING user.age = MAX(user.age) ;