Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a21eaa2
feat(transports/rest): add StaticEndpoint and IdentityEndpoint utils
bevzzz Feb 14, 2026
940b75a
feat(internal/api): provide C-R-D requests for collections
bevzzz Feb 14, 2026
74f0cd9
fix(dev): enable asserts if os.Args has -test.* flags
bevzzz Feb 14, 2026
c865861
feat(internal/api): unmarshal response back into Collection
bevzzz Feb 14, 2026
f9dd270
feat(collections): provide higher-level collection create API
bevzzz Feb 15, 2026
5cd8914
test(internal/testkit): support heterogenous requests in MockTransport
bevzzz Feb 16, 2026
d18382c
chore(internal/testkit): rename IsPointer -> RequirePointer
bevzzz Feb 16, 2026
5ea39a2
feat(collections): provide public API for GetConfig / List / Delete /…
bevzzz Feb 16, 2026
6fe1e5e
feat(internal/api): provide ResourceExistsResponse dest for HEAD and …
bevzzz Feb 16, 2026
96e6221
feat(collections): check collection existance with Exists
bevzzz Feb 16, 2026
6d1df91
chore(lint): appease linter
bevzzz Feb 16, 2026
9b61388
chore(api): update contracts and generated models
bevzzz Feb 20, 2026
dd2e266
feat(api): support async replication config
bevzzz Feb 20, 2026
b2e7a62
chore(ci): upgrade golangci-lint
bevzzz Feb 16, 2026
1617176
feat(collections): expose async config in collection config
bevzzz Feb 20, 2026
cc567ee
refactor(internal/api): move message-related interfaces to transport/…
bevzzz Feb 20, 2026
34e59a9
feat(internal/api): provide internal.Transport factory
bevzzz Feb 20, 2026
64d6948
feat(weaviate): provide public client factories
bevzzz Feb 20, 2026
ea23e89
feat(weaviate): add apiKey paramter to WCD connection helper
bevzzz Feb 20, 2026
9465878
refactor(weaviate): apply options once
bevzzz Feb 22, 2026
8f6888e
feat(transport): add read/write/batch timeouts
bevzzz Feb 22, 2026
d3adf35
chore(api): update contracts and generated models
bevzzz Mar 4, 2026
7b7c766
feat(backup): support backups in lower-level client
bevzzz Mar 4, 2026
75f3720
feat(internal/testkit): add tickingContext and ErrorIs utils
bevzzz Mar 4, 2026
a539cbc
feat(backup): add public backups API
bevzzz Mar 4, 2026
50094a9
chore: add better assert message
bevzzz Mar 4, 2026
dcf308f
fix(internal/testkit): do not require Map, Slice, and Chan to be poin…
bevzzz Mar 4, 2026
52f7fb1
test(internal/api): test BackupInfo unmarshaling
bevzzz Mar 4, 2026
bf7399c
chore(backup): make StartedAt and CompletedAt pointer variables
bevzzz Mar 4, 2026
21e7c3b
feat(backup): add Backup namespace to client
bevzzz Mar 4, 2026
ac928f5
feat(internal/api): support Aggregate request
bevzzz Mar 9, 2026
1665ea2
refactor(api/internal): declare our own aggregation types
bevzzz Mar 10, 2026
089ab39
feat(aggregate): add public API namespace
bevzzz Mar 12, 2026
e0f14d7
feat(aggregate): support GroupBy aggregation
bevzzz Mar 12, 2026
2889c2c
refactor(aggregate): simplify internal response data structures
bevzzz Mar 13, 2026
4b0fa56
feat(aggregate): expose aggregate.Client in collection handle
bevzzz Mar 13, 2026
b179ba0
chore(api): update contracts and generated models
bevzzz Mar 13, 2026
5b0c2cd
test(aggregate): check that NearVector argument is passed correctly
bevzzz Mar 13, 2026
53f7c38
chore(ci): appease linter
bevzzz Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ jobs:
go-version: stable
- uses: golangci/golangci-lint-action@v9
with:
version: v2.8.0
version: v2.9.0
212 changes: 212 additions & 0 deletions aggregate/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package aggregate

import (
"context"
"fmt"

"github.com/weaviate/weaviate-go-client/v6/internal"
"github.com/weaviate/weaviate-go-client/v6/internal/api"
"github.com/weaviate/weaviate-go-client/v6/internal/dev"
)

func NewClient(t internal.Transport, rd api.RequestDefaults) *Client {
dev.AssertNotNil(t, "transport")
return &Client{
transport: t,
defaults: rd,
OverAll: overAllFunc(t, rd),
NearVector: nearVectorFunc(t, rd),
}
}

type Client struct {
transport internal.Transport
defaults api.RequestDefaults

OverAll OverAllFunc
NearVector NearVectorFunc
}

// Request contains common aggregation parameters.
type Request[Query any] struct {
Query Query // Query-filter portion of the request.

Text []Text // Aggregations for text properties.
Integer []Integer // Aggregations for integer properties.
Number []Number // Aggregations for number properties.
Boolean []Boolean // Aggregations for boolean properties.
Date []Date // Aggregations for date properties.

TotalCount bool // Return total object count.
ObjectLimit int32

// groupBy can only be set by a GroupBy method, as it changes the shape of the response.
groupBy *GroupBy
}

type GroupBy struct {
Property string
Limit int
}

type (
Text api.AggregateTextRequest
Integer api.AggregateIntegerRequest
Number api.AggregateNumberRequest
Boolean api.AggregateBooleanRequest
Date api.AggregateDateRequest
)

type Result struct {
TookSeconds float32
Aggregations
}

type GroupByResult struct {
Groups []Group
}

type Group struct {
Property string
Value any
Aggregations
}

type (
Aggregations struct {
TotalCount *int64
Text map[string]TextResult
Integer map[string]IntegerResult
Number map[string]NumberResult
Boolean map[string]BooleanResult
Date map[string]DateResult
}
TextResult struct {
Property string
Count *int64
TopOccurrences []TopOccurrence
}
TopOccurrence api.TopOccurrence
IntegerResult api.AggregateIntegerResult
NumberResult api.AggregateNumberResult
BooleanResult api.AggregateBooleanResult
DateResult api.AggregateDateResult
)

func aggregate[Query any](ctx context.Context, t internal.Transport, rd api.RequestDefaults, r *Request[Query], search any, label string) (*Result, error) {
req := &api.AggregateRequest{
RequestDefaults: rd,
TotalCount: r.TotalCount,
ObjectLimit: r.ObjectLimit,
}
for _, txt := range r.Text {
req.Text = append(req.Text, api.AggregateTextRequest(txt))
}
for _, int := range r.Integer {
req.Integer = append(req.Integer, api.AggregateIntegerRequest(int))
}
for _, num := range r.Number {
req.Number = append(req.Number, api.AggregateNumberRequest(num))
}
for _, bool := range r.Boolean {
req.Boolean = append(req.Boolean, api.AggregateBooleanRequest(bool))
}
for _, date := range r.Date {
req.Date = append(req.Date, api.AggregateDateRequest(date))
}

if search != nil {
switch q := search.(type) {
case *api.NearVector:
req.NearVector = q
}
}

if r.groupBy != nil {
req.GroupBy = &api.GroupBy{
Property: r.groupBy.Property,
Limit: int32(r.groupBy.Limit),
}
}

var resp api.AggregateResponse
if err := t.Do(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("%s: %w", label, err)
}

// aggregate was called from a GroupBy() method.
// This means we should put GroupByResult in the context,
// as the first return value will be discarded.
if r.groupBy != nil {
groups := make([]Group, len(resp.GroupByResults))
for gi, group := range resp.GroupByResults {
groups[gi] = Group{
Property: group.Property,
Value: group.Value,
Aggregations: aggregationsFromAPI(group.Results),
}
}
setGroupByResult(ctx, &GroupByResult{Groups: groups})
return nil, nil
}

result := &Result{
TookSeconds: resp.TookSeconds,
Aggregations: aggregationsFromAPI(resp.Results),
}
return result, nil
}

func aggregationsFromAPI(aggregations api.Aggregations) Aggregations {
out := Aggregations{
TotalCount: aggregations.TotalCount,
Text: make(map[string]TextResult, len(aggregations.Text)),
Integer: make(map[string]IntegerResult, len(aggregations.Integer)),
Number: make(map[string]NumberResult, len(aggregations.Number)),
Boolean: make(map[string]BooleanResult, len(aggregations.Boolean)),
Date: make(map[string]DateResult, len(aggregations.Date)),
}
for _, txt := range aggregations.Text {
top := make([]TopOccurrence, len(txt.TopOccurrences))
for i, item := range txt.TopOccurrences {
top[i] = TopOccurrence(item)
}
out.Text[txt.Property] = TextResult{
Property: txt.Property,
Count: txt.Count,
TopOccurrences: top,
}
}
for _, int := range aggregations.Integer {
out.Integer[int.Property] = IntegerResult(int)
}
for _, num := range aggregations.Number {
out.Number[num.Property] = NumberResult(num)
}
for _, bool := range aggregations.Boolean {
out.Boolean[bool.Property] = BooleanResult(bool)
}
for _, date := range aggregations.Date {
out.Date[date.Property] = DateResult(date)
}
return out
}

// groupByResultKey is used to pass grouped query results to the GroupBy caller.
var groupByResultKey = internal.ContextKey{}

// contextWithGorupByResult creates a placeholder for *GroupByResult in the ctx.Values store.
func contextWithGroupByResult(ctx context.Context) context.Context {
return internal.ContextWithPlaceholder[GroupByResult](ctx, groupByResultKey)
}

// getGroupByResult extracts *GroupByResult from the context.
func getGroupByResult(ctx context.Context) *GroupByResult {
return internal.ValueFromContext[GroupByResult](ctx, groupByResultKey)
}

// setGroupByResult replaces *GroupByResult placeholder
// in the context with the value at r.
func setGroupByResult(ctx context.Context, r *GroupByResult) {
internal.SetContextValue(ctx, groupByResultKey, r)
}
Loading
Loading