Defining Queries
A query describes what someone wants to know. Define it as a struct, and answer it with a function that reads a view. To turn the items into a slice, use slices.Collect:
type ListBooks struct {
OnlyAvailable bool
Limit int
}
func listBooks(catalog architecturekit.View[BookItem]) func(context.Context, ListBooks) ([]BookItem, error) {
return func(ctx context.Context, q ListBooks) ([]BookItem, error) {
items, err := catalog.All(ctx)
if err != nil {
return nil, err
}
// ...
return slices.Collect(items), nil
}
}
To filter, order, page, and transform the items, use the query package:
import "github.com/thenativeweb/architecturekit-golang/architecturekit/query"
All of its functions take an iterator, and those that return items return an iterator again, so they can be combined without collecting anything in between.
Filtering and Transforming Items#
To keep only some items, call the Where function with a function that selects them:
if q.OnlyAvailable {
items = query.Where(items, func(item BookItem) bool {
return !item.IsBorrowed
})
}
To turn every item into something else, call the Select function:
titles := query.Select(items, func(item BookItem) string {
return item.Title
})
Ordering Items#
To order items by a key, call the OrderBy function, or the OrderByDescending function for the reverse order:
items = query.OrderBy(items, func(item BookItem) string {
return item.Title
})
To order items by a comparison function, for example by several fields, call the OrderByFunc function. It expects the same kind of function as slices.SortFunc, so cmp.Or and strings.Compare work with it:
items = query.OrderByFunc(items, func(left, right BookItem) int {
return cmp.Or(
strings.Compare(left.Author, right.Author),
strings.Compare(left.Title, right.Title),
)
})
Note that ordering reads all items, and that it is stable, so items that compare as equal keep their order.
Paging Items#
To skip a number of items, call the Skip function. To stop after a number of items, call the Take function:
if q.Limit > 0 {
items = query.Take(items, q.Limit)
}
Both can be combined, for example to get the third page of ten items:
items = query.Take(query.Skip(items, 20), 10)
Getting a Single Item#
To get the first item, call the First function. It returns false if there is none:
first, ok := query.First(items)
To get the only item, call the Single function. It returns query.ErrNoItems if there is none, and query.ErrTooManyItems if there are several:
type GetBook struct {
BookID string
}
func getBook(catalog architecturekit.View[BookItem]) func(context.Context, GetBook) (BookItem, error) {
return func(ctx context.Context, q GetBook) (BookItem, error) {
items, err := catalog.All(ctx)
if err != nil {
return BookItem{}, err
}
return query.Single(query.Where(items, func(item BookItem) bool {
return item.ID == q.BookID
}))
}
}
Counting Items#
To count items, call the Count function. To check whether at least one item matches, call the Any function, which stops at the first match:
count := query.Count(items)
hasBorrowedBooks := query.Any(items, func(item BookItem) bool {
return item.IsBorrowed
})