Index: impl/memory/datastore_query.go |
diff --git a/impl/memory/datastore_query.go b/impl/memory/datastore_query.go |
index bf912f718b2aee98cb0edc88ad7226df5cfa698e..91e08750e81a978c7469ce588c443a8858d7aad0 100644 |
--- a/impl/memory/datastore_query.go |
+++ b/impl/memory/datastore_query.go |
@@ -5,7 +5,6 @@ |
package memory |
import ( |
- "bytes" |
"errors" |
"fmt" |
"math" |
@@ -13,148 +12,8 @@ import ( |
ds "github.com/luci/gae/service/datastore" |
"github.com/luci/gkvlite" |
- "github.com/luci/luci-go/common/cmpbin" |
) |
-type qDirection bool |
- |
-const ( |
- qASC qDirection = true |
- qDEC = false |
-) |
- |
-var builtinQueryPrefix = []byte{0} |
-var complexQueryPrefix = []byte{1} |
- |
-type qSortBy struct { |
- prop string |
- dir qDirection |
-} |
- |
-func (q qSortBy) WriteBinary(buf *bytes.Buffer) { |
- if q.dir == qASC { |
- buf.WriteByte(0) |
- } else { |
- buf.WriteByte(1) |
- } |
- cmpbin.WriteString(buf, q.prop) |
-} |
- |
-func (q *qSortBy) ReadBinary(buf *bytes.Buffer) error { |
- dir, err := buf.ReadByte() |
- if err != nil { |
- return err |
- } |
- q.dir = dir == 0 |
- q.prop, _, err = cmpbin.ReadString(buf) |
- return err |
-} |
- |
-type qIndex struct { |
- kind string |
- ancestor bool |
- sortby []qSortBy |
-} |
- |
-func (i *qIndex) Builtin() bool { |
- return !i.ancestor && len(i.sortby) <= 1 |
-} |
- |
-func (i *qIndex) Less(o *qIndex) bool { |
- ibuf, obuf := &bytes.Buffer{}, &bytes.Buffer{} |
- i.WriteBinary(ibuf) |
- o.WriteBinary(obuf) |
- return i.String() < o.String() |
-} |
- |
-// Valid verifies that this qIndex doesn't have duplicate sortBy fields. |
-func (i *qIndex) Valid() bool { |
- names := map[string]bool{} |
- for _, sb := range i.sortby { |
- if names[sb.prop] { |
- return false |
- } |
- names[sb.prop] = true |
- } |
- return true |
-} |
- |
-func (i *qIndex) WriteBinary(buf *bytes.Buffer) { |
- // TODO(riannucci): do a Grow call here? |
- if i.Builtin() { |
- buf.Write(builtinQueryPrefix) |
- } else { |
- buf.Write(complexQueryPrefix) |
- } |
- cmpbin.WriteString(buf, i.kind) |
- if i.ancestor { |
- buf.WriteByte(0) |
- } else { |
- buf.WriteByte(1) |
- } |
- cmpbin.WriteUint(buf, uint64(len(i.sortby))) |
- for _, sb := range i.sortby { |
- sb.WriteBinary(buf) |
- } |
-} |
- |
-func (i *qIndex) String() string { |
- ret := &bytes.Buffer{} |
- if i.Builtin() { |
- ret.WriteRune('B') |
- } else { |
- ret.WriteRune('C') |
- } |
- ret.WriteRune(':') |
- ret.WriteString(i.kind) |
- if i.ancestor { |
- ret.WriteString("|A") |
- } |
- for _, sb := range i.sortby { |
- ret.WriteRune('/') |
- if sb.dir == qDEC { |
- ret.WriteRune('-') |
- } |
- ret.WriteString(sb.prop) |
- } |
- return ret.String() |
-} |
- |
-func (i *qIndex) ReadBinary(buf *bytes.Buffer) error { |
- // discard builtin/complex byte |
- _, err := buf.ReadByte() |
- if err != nil { |
- return err |
- } |
- |
- i.kind, _, err = cmpbin.ReadString(buf) |
- if err != nil { |
- return err |
- } |
- anc, err := buf.ReadByte() |
- if err != nil { |
- return err |
- } |
- i.ancestor = anc == 1 |
- |
- numSorts, _, err := cmpbin.ReadUint(buf) |
- if err != nil { |
- return err |
- } |
- if numSorts > 64 { |
- return fmt.Errorf("qIndex.ReadBinary: Got over 64 sort orders: %d", numSorts) |
- } |
- i.sortby = make([]qSortBy, numSorts) |
- for idx := range i.sortby { |
- err = (&i.sortby[idx]).ReadBinary(buf) |
- if err != nil { |
- return err |
- } |
- } |
- |
- return nil |
-} |
- |
type queryOp int |
const ( |
@@ -183,7 +42,7 @@ var queryOpMap = map[string]queryOp{ |
} |
type queryFilter struct { |
- field string |
+ prop string |
op queryOp |
value interface{} |
} |
@@ -197,7 +56,7 @@ func parseFilter(f string, v interface{}) (ret queryFilter, err error) { |
if op == qInvalid { |
err = fmt.Errorf("datastore: invalid operator %q in filter %q", toks[1], f) |
} else { |
- ret.field = toks[0] |
+ ret.prop = toks[0] |
ret.op = op |
ret.value = v |
} |
@@ -205,11 +64,6 @@ func parseFilter(f string, v interface{}) (ret queryFilter, err error) { |
return |
} |
-type queryOrder struct { |
- field string |
- direction qDirection |
-} |
- |
type queryCursor string |
func (q queryCursor) String() string { return string(q) } |
@@ -221,7 +75,7 @@ type queryImpl struct { |
kind string |
ancestor ds.Key |
filter []queryFilter |
- order []queryOrder |
+ order []ds.IndexColumn |
project []string |
distinct bool |
@@ -252,9 +106,9 @@ func (q *queryImpl) normalize() (ret *queryImpl) { |
// if we supported the IN operator, we would check to see if there were |
// multiple value operands here, but the go SDK doesn't support this. |
if f.op.isEQOp() { |
- eqProperties.Set([]byte(f.field), []byte{}) |
+ eqProperties.Set([]byte(f.prop), []byte{}) |
} else if f.op.isINEQOp() { |
- ineqProperties.Set([]byte(f.field), []byte{}) |
+ ineqProperties.Set([]byte(f.prop), []byte{}) |
} |
} |
@@ -269,10 +123,10 @@ func (q *queryImpl) normalize() (ret *queryImpl) { |
return true |
}) |
- newOrders := []queryOrder{} |
+ newOrders := []ds.IndexColumn{} |
for _, o := range ret.order { |
- if removeSet.Get([]byte(o.field)) == nil { |
- removeSet.Set([]byte(o.field), []byte{}) |
+ if removeSet.Get([]byte(o.Property)) == nil { |
+ removeSet.Set([]byte(o.Property), []byte{}) |
newOrders = append(newOrders, o) |
} |
} |
@@ -285,8 +139,8 @@ func (q *queryImpl) normalize() (ret *queryImpl) { |
// for f in ret.filters: |
// if f.op != qExists: |
// newFilters = append(newFilters, f) |
- // if !removeSet.Has(f.field): |
- // removeSet.InsertNoReplace(f.field) |
+ // if !removeSet.Has(f.prop): |
+ // removeSet.InsertNoReplace(f.prop) |
// newFilters = append(newFilters, f) |
// |
// so ret.filters == newFilters becuase none of ret.filters has op == qExists |
@@ -302,12 +156,12 @@ func (q *queryImpl) normalize() (ret *queryImpl) { |
// However, since we don't support projection queries, this is moot. |
if eqProperties.Get([]byte("__key__")) != nil { |
- ret.order = []queryOrder{} |
+ ret.order = []ds.IndexColumn{} |
} |
- newOrders = []queryOrder{} |
+ newOrders = []ds.IndexColumn{} |
for _, o := range ret.order { |
- if o.field == "__key__" { |
+ if o.Property == "__key__" { |
newOrders = append(newOrders, o) |
break |
} |
@@ -365,7 +219,7 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { |
ineqPropName := "" |
for _, f := range ret.filter { |
- if f.field == "__key__" { |
+ if f.prop == "__key__" { |
k, ok := f.value.(ds.Key) |
if !ok { |
ret.err = errors.New( |
@@ -386,16 +240,16 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { |
// __key__ filter app is X but query app is X |
// __key__ filter namespace is X but query namespace is X |
} |
- // if f.op == qEqual and f.field in ret.project_fields |
+ // if f.op == qEqual and f.prop in ret.project_fields |
// "cannot use projection on a proprety with an equality filter" |
if f.op.isINEQOp() { |
if ineqPropName == "" { |
- ineqPropName = f.field |
- } else if f.field != ineqPropName { |
+ ineqPropName = f.prop |
+ } else if f.prop != ineqPropName { |
ret.err = fmt.Errorf( |
"gae/memory: Only one inequality filter per query is supported. "+ |
- "Encountered both %s and %s", ineqPropName, f.field) |
+ "Encountered both %s and %s", ineqPropName, f.prop) |
return |
} |
} |
@@ -406,26 +260,26 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { |
// "when group by properties are set." |
if ineqPropName != "" && len(ret.order) != 0 { |
- if ret.order[0].field != ineqPropName { |
+ if ret.order[0].Property != ineqPropName { |
ret.err = fmt.Errorf( |
"gae/memory: The first sort property must be the same as the property "+ |
"to which the inequality filter is applied. In your query "+ |
"the first sort property is %s but the inequality filter "+ |
- "is on %s", ret.order[0].field, ineqPropName) |
+ "is on %s", ret.order[0].Property, ineqPropName) |
return |
} |
} |
if ret.kind == "" { |
for _, f := range ret.filter { |
- if f.field != "__key__" { |
+ if f.prop != "__key__" { |
ret.err = errors.New( |
"gae/memory: kind is required for non-__key__ filters") |
return |
} |
} |
for _, o := range ret.order { |
- if o.field != "__key__" || o.direction != qASC { |
+ if o.Property != "__key__" || o.Direction != ds.ASCENDING { |
ret.err = errors.New( |
"gae/memory: kind is required for all orders except __key__ ascending") |
return |
@@ -435,7 +289,7 @@ func (q *queryImpl) checkCorrectness(ns string, isTxn bool) (ret *queryImpl) { |
return |
} |
-func (q *queryImpl) calculateIndex() *qIndex { |
+func (q *queryImpl) calculateIndex() *ds.IndexDefinition { |
// as a nod to simplicity in this code, we'll require that a single index |
// is able to service the entire query. E.g. no zigzag merge joins or |
// multiqueries. This will mean that the user will need to rely on |
@@ -453,7 +307,7 @@ func (q *queryImpl) calculateIndex() *qIndex { |
func (q *queryImpl) clone() *queryImpl { |
ret := *q |
ret.filter = append([]queryFilter(nil), q.filter...) |
- ret.order = append([]queryOrder(nil), q.order...) |
+ ret.order = append([]ds.IndexColumn(nil), q.order...) |
ret.project = append([]string(nil), q.project...) |
return &ret |
} |
@@ -494,18 +348,18 @@ func (q *queryImpl) Filter(fStr string, val interface{}) ds.Query { |
return q |
} |
-func (q *queryImpl) Order(field string) ds.Query { |
+func (q *queryImpl) Order(prop string) ds.Query { |
q = q.clone() |
- field = strings.TrimSpace(field) |
- o := queryOrder{field, qASC} |
- if strings.HasPrefix(field, "-") { |
- o.direction = qDEC |
- o.field = strings.TrimSpace(field[1:]) |
- } else if strings.HasPrefix(field, "+") { |
- q.err = fmt.Errorf("datastore: invalid order: %q", field) |
+ prop = strings.TrimSpace(prop) |
+ o := ds.IndexColumn{Property: prop} |
+ if strings.HasPrefix(prop, "-") { |
+ o.Direction = ds.DESCENDING |
+ o.Property = strings.TrimSpace(prop[1:]) |
+ } else if strings.HasPrefix(prop, "+") { |
+ q.err = fmt.Errorf("datastore: invalid order: %q", prop) |
return q |
} |
- if len(o.field) == 0 { |
+ if len(o.Property) == 0 { |
q.err = errors.New("datastore: empty order") |
return q |
} |