Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# Folders
_obj
_test
.idea

# Architecture specific extensions/prefixes
*.[568vq]
Expand Down
51 changes: 42 additions & 9 deletions datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -207,27 +208,31 @@ 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
}

// 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)
Expand All @@ -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 {
Expand All @@ -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
}
39 changes: 25 additions & 14 deletions lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}) {
Expand All @@ -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
}
Expand Down