package cursor import ( "fmt" "gitea.auvem.com/go-toolkit/dbx" "github.com/go-jet/jet/v2/mysql" ) // PageQuery runs a paginated SELECT and builds a Connection from the results. type PageQuery[T any, IE mysql.Expression, IC mysql.Column] struct { Sqlo dbx.Queryable Stmt mysql.SelectStatement Conds mysql.BoolExpression Cursor *Cursor[IE, IC] Default func() *Cursor[IE, IC] Limit int CountFn QueryCountFn ToEdge func(active *Cursor[IE, IC], item *T) (GenericCursor, error) // Scan runs the paginated query into dest. When nil, Run uses Stmt.Query. Scan func(stmt mysql.SelectStatement, dest *[]*T) error // AfterScan optionally transforms rows after the query and before building edges. AfterScan func(items []*T) ([]*T, error) } // Run executes the paginated query and returns a Connection. func (q PageQuery[T, IE, IC]) Run() (*Connection[T], error) { active := q.Cursor if active == nil { active = q.Default() } stmt := q.Stmt stmt.WHERE(q.Conds.AND(PaginateConds(q.Cursor))) stmt.ORDER_BY(OrderByClauses(active)...) if q.Limit > 0 { stmt = stmt.LIMIT(int64(q.Limit)) } var items []*T scan := q.Scan if scan == nil { scan = func(s mysql.SelectStatement, dest *[]*T) error { return s.Query(q.Sqlo, dest) } } if err := scan(stmt, &items); err != nil { return nil, fmt.Errorf("failed to run paginated query: %w", err) } if q.AfterScan != nil { var err error items, err = q.AfterScan(items) if err != nil { return nil, err } } return BuildEdges(q.Sqlo, q.CountFn, items, func(item *T) (GenericCursor, error) { return q.ToEdge(active, item) }) } // ConnectionFromRelayArgs parses Relay after/first args and calls list with the // decoded cursor and limit. newZero returns a template cursor used to decode after // (same shape as the cursor factory for the resource, e.g. newUserCursor). func ConnectionFromRelayArgs[T any, IE mysql.Expression, IC mysql.Column]( after *string, first *int, newZero func() *Cursor[IE, IC], list func(cursor *Cursor[IE, IC], limit int) (*Connection[T], error), ) (*Connection[T], error) { limit := 0 if first != nil { limit = *first } c, err := NewCursorFromAfterPtr(newZero, after) if err != nil { return nil, err } return list(c, limit) }