diff --git a/.gitignore b/.gitignore index daf913b..0c9e329 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Folders _obj _test +.idea # Architecture specific extensions/prefixes *.[568vq] diff --git a/datastore.go b/datastore.go index e84c047..6901e23 100644 --- a/datastore.go +++ b/datastore.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/rs/rest-layer/schema/query" "cloud.google.com/go/datastore" "github.com/rs/rest-layer/resource" "google.golang.org/api/iterator" @@ -207,13 +208,17 @@ func (d *Handler) Delete(ctx context.Context, item *resource.Item) error { } // Clear clears all entities matching the lookup from the Datastore -func (d *Handler) Clear(ctx context.Context, lookup *resource.Lookup) (int, error) { - q, err := getQuery(d.entity, d.namespace, lookup) +func (d *Handler) Clear(ctx context.Context, q *query.Query) (int, error) { + qry, err := getQuery(d.entity, d.namespace, q) if err != nil { return 0, err } - c, err := d.client.Count(ctx, q) + if q.Window != nil { + qry = applyWindow(qry, *q.Window) + } + + c, err := d.client.Count(ctx, qry) if err != nil { return 0, err } @@ -221,13 +226,13 @@ func (d *Handler) Clear(ctx context.Context, lookup *resource.Lookup) (int, erro // TODO: Check wheter if DeleteMulti is better here than delete on every // iteration here or not. mKeys := make([]*datastore.Key, c) - for t, i := d.client.Run(ctx, q), 0; ; i++ { + for t, i := d.client.Run(ctx, qry), 0; ; i++ { var e Entity key, err := t.Next(&e) - mKeys[i] = key if err == iterator.Done { break } + mKeys[i] = key } err = d.client.DeleteMulti(ctx, mKeys) @@ -238,15 +243,33 @@ func (d *Handler) Clear(ctx context.Context, lookup *resource.Lookup) (int, erro } // Find entities matching the provided lookup from the Datastore -func (d *Handler) Find(ctx context.Context, lookup *resource.Lookup, offset, limit int) (*resource.ItemList, error) { - q, err := getQuery(d.entity, d.namespace, lookup) +func (d *Handler) Find(ctx context.Context, q *query.Query) (*resource.ItemList, error) { + qry, err := getQuery(d.entity, d.namespace, q) if err != nil { return nil, err } + offset := 0; limit := -1 + + if q.Window != nil && q.Window.Offset > 0 { + offset = q.Window.Offset + } + + if q.Window != nil && q.Window.Limit > -1 { + limit = q.Window.Limit + } // TODO: Apply context deadline if any. - list := &resource.ItemList{Total: -1, Offset: offset, Limit: limit, Items: []*resource.Item{}} - for t := d.client.Run(ctx, q); ; { + list := &resource.ItemList{ + Total: -1, + Offset: offset, + Limit: limit, + Items: []*resource.Item{}, + } + if q.Window != nil { + qry = applyWindow(qry, *q.Window) + } + + for t := d.client.Run(ctx, qry); ; { var e Entity _, terr := t.Next(&e) if terr == iterator.Done { @@ -262,3 +285,13 @@ func (d *Handler) Find(ctx context.Context, lookup *resource.Lookup, offset, lim } return list, nil } + +func applyWindow(qry *datastore.Query, w query.Window) *datastore.Query { + if w.Offset > 0 { + qry = qry.Offset(w.Offset) + } + if w.Limit > -1 { + qry = qry.Limit(w.Limit) + } + return qry +} diff --git a/lookup.go b/lookup.go index 6687860..3ec5137 100644 --- a/lookup.go +++ b/lookup.go @@ -28,23 +28,34 @@ func getField(f string) string { } // getQuery transform a resource.Lookup into a Google Datastore query -func getQuery(e string, ns string, l *resource.Lookup) (*datastore.Query, error) { - query, err := translateQuery(datastore.NewQuery(e), l.Filter()) +func getQuery(e string, ns string, q *query.Query) (*datastore.Query, error) { + query, err := translateQuery(datastore.NewQuery(e), q.Predicate) + if err != nil { + return nil, err + } // if lookup specifies sorting add this to our query - if len(l.Sort()) > 0 { - query = getSort(query, l.Sort()) + if len(q.Sort) > 0 { + s := make([]string, len(q.Sort)) + for i, sort := range q.Sort { + if sort.Reversed { + s[i] = "-" + getField(sort.Name) + } else { + s[i] = getField(sort.Name) + } + } + query = getSort(query, s) } // Set namespace for this query query = query.Namespace(ns) return query, err } -func translateQuery(dsQuery *datastore.Query, q query.Query) (*datastore.Query, error) { +func translateQuery(dsQuery *datastore.Query, q query.Predicate) (*datastore.Query, error) { var err error // process each schema.Expression into a datastore filter for _, exp := range q { switch t := exp.(type) { - case query.Equal: + case *query.Equal: // If our Query contains a slice, add each as an additional filter if reflect.TypeOf(t.Value).Kind() == reflect.Slice { for _, v := range t.Value.([]interface{}) { @@ -53,19 +64,19 @@ func translateQuery(dsQuery *datastore.Query, q query.Query) (*datastore.Query, } else { dsQuery = dsQuery.Filter(fmt.Sprintf("%s =", getField(t.Field)), t.Value) } - case query.NotEqual: + case *query.NotEqual: dsQuery = dsQuery.Filter(fmt.Sprintf("%s !=", getField(t.Field)), t.Value) - case query.GreaterThan: + case *query.GreaterThan: dsQuery = dsQuery.Filter(fmt.Sprintf("%s >", getField(t.Field)), t.Value) - case query.GreaterOrEqual: + case *query.GreaterOrEqual: dsQuery = dsQuery.Filter(fmt.Sprintf("%s >=", getField(t.Field)), t.Value) - case query.LowerThan: + case *query.LowerThan: dsQuery = dsQuery.Filter(fmt.Sprintf("%s <", getField(t.Field)), t.Value) - case query.LowerOrEqual: + case *query.LowerOrEqual: dsQuery = dsQuery.Filter(fmt.Sprintf("%s <=", getField(t.Field)), t.Value) - case query.And: - for _, subExp := range t { - dsQuery, err = translateQuery(dsQuery, query.Query{subExp}) + case *query.And: + for _, subExp := range *t { + dsQuery, err = translateQuery(dsQuery, query.Predicate{subExp}) if err != nil { return nil, err }