Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: service/datastore/index.go

Issue 1355783002: Refactor keys and queries in datastore service and implementation. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: appease errcheck Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « service/datastore/finalized_query.go ('k') | service/datastore/index_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package datastore 5 package datastore
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "fmt"
10 "strings"
9 ) 11 )
10 12
11 const MaxIndexColumns = 64 13 // IndexColumn represents a sort order for a single entity field.
12 14 type IndexColumn struct {
13 type IndexDirection bool 15 » Property string
14 16 » Descending bool
15 const (
16 » // ASCENDING is false so that it's the default (zero) value.
17 » ASCENDING IndexDirection = false
18 » DESCENDING = true
19 )
20
21 func (i IndexDirection) String() string {
22 » if i == ASCENDING {
23 » » return "ASCENDING"
24 » }
25 » return "DESCENDING"
26 } 17 }
27 18
28 type IndexColumn struct { 19 // ParseIndexColumn takes a spec in the form of /\s*-?\s*.+\s*/, and
29 » Property string 20 // returns an IndexColumn. Examples are:
30 » Direction IndexDirection 21 // `- Field `: IndexColumn{Property: "Field", Descending: true}
22 // `Something`: IndexColumn{Property: "Something", Descending: false}
23 //
24 // `+Field` is invalid. `` is invalid.
25 func ParseIndexColumn(spec string) (IndexColumn, error) {
26 » col := IndexColumn{}
27 » if strings.HasPrefix(spec, "-") {
28 » » col.Descending = true
29 » » col.Property = strings.TrimSpace(spec[1:])
30 » } else if strings.HasPrefix(spec, "+") {
31 » » return col, fmt.Errorf("datastore: invalid order: %q", spec)
32 » } else {
33 » » col.Property = strings.TrimSpace(spec)
34 » }
35 » if col.Property == "" {
36 » » return col, fmt.Errorf("datastore: empty order")
37 » }
38 » return col, nil
31 } 39 }
32 40
33 func (i IndexColumn) cmp(o IndexColumn) int { 41 func (i IndexColumn) cmp(o IndexColumn) int {
34 // sort ascending first 42 // sort ascending first
35 » if i.Direction == ASCENDING && o.Direction == DESCENDING { 43 » if !i.Descending && o.Descending {
36 return -1 44 return -1
37 » } else if i.Direction == DESCENDING && o.Direction == ASCENDING { 45 » } else if i.Descending && !o.Descending {
38 return 1 46 return 1
39 } 47 }
40 return cmpString(i.Property, o.Property)() 48 return cmpString(i.Property, o.Property)()
41 } 49 }
42 50
51 // String returns a human-readable version of this IndexColumn which is
52 // compatible with ParseIndexColumn.
53 func (i IndexColumn) String() string {
54 ret := ""
55 if i.Descending {
56 ret = "-"
57 }
58 return ret + i.Property
59 }
60
61 // GQL returns a correctly formatted Cloud Datastore GQL literal which
62 // is valid for the `ORDER BY` clause.
63 //
64 // The flavor of GQL that this emits is defined here:
65 // https://cloud.google.com/datastore/docs/apis/gql/gql_reference
66 func (i IndexColumn) GQL() string {
67 if i.Descending {
68 return gqlQuoteName(i.Property) + " DESC"
69 }
70 return gqlQuoteName(i.Property)
71 }
72
73 // IndexDefinition holds the parsed definition of a datastore index definition.
43 type IndexDefinition struct { 74 type IndexDefinition struct {
44 Kind string 75 Kind string
45 Ancestor bool 76 Ancestor bool
46 SortBy []IndexColumn 77 SortBy []IndexColumn
47 } 78 }
48 79
80 // Equal returns true if the two IndexDefinitions are equivalent.
49 func (id *IndexDefinition) Equal(o *IndexDefinition) bool { 81 func (id *IndexDefinition) Equal(o *IndexDefinition) bool {
50 if id.Kind != o.Kind || id.Ancestor != o.Ancestor || len(id.SortBy) != l en(o.SortBy) { 82 if id.Kind != o.Kind || id.Ancestor != o.Ancestor || len(id.SortBy) != l en(o.SortBy) {
51 return false 83 return false
52 } 84 }
53 for i, col := range id.SortBy { 85 for i, col := range id.SortBy {
54 if col != o.SortBy[i] { 86 if col != o.SortBy[i] {
55 return false 87 return false
56 } 88 }
57 } 89 }
58 return true 90 return true
59 } 91 }
60 92
61 // NormalizeOrder returns the normalized SortBy value for this IndexDefinition. 93 // Normalize returns an IndexDefinition which has a normalized SortBy field.
94 //
62 // This is just appending __key__ if it's not explicitly the last field in this 95 // This is just appending __key__ if it's not explicitly the last field in this
63 // IndexDefinition. 96 // IndexDefinition.
64 func (id *IndexDefinition) Normalize() *IndexDefinition { 97 func (id *IndexDefinition) Normalize() *IndexDefinition {
65 if len(id.SortBy) > 0 && id.SortBy[len(id.SortBy)-1].Property == "__key_ _" { 98 if len(id.SortBy) > 0 && id.SortBy[len(id.SortBy)-1].Property == "__key_ _" {
66 return id 99 return id
67 } 100 }
68 ret := *id 101 ret := *id
69 ret.SortBy = make([]IndexColumn, len(id.SortBy), len(id.SortBy)+1) 102 ret.SortBy = make([]IndexColumn, len(id.SortBy), len(id.SortBy)+1)
70 copy(ret.SortBy, id.SortBy) 103 copy(ret.SortBy, id.SortBy)
71 ret.SortBy = append(ret.SortBy, IndexColumn{Property: "__key__"}) 104 ret.SortBy = append(ret.SortBy, IndexColumn{Property: "__key__"})
72 return &ret 105 return &ret
73 } 106 }
74 107
108 // GetFullSortOrder gets the full sort order for this IndexDefinition,
109 // including an extra "__ancestor__" column at the front if this index has
110 // Ancestor set to true.
75 func (id *IndexDefinition) GetFullSortOrder() []IndexColumn { 111 func (id *IndexDefinition) GetFullSortOrder() []IndexColumn {
76 id = id.Normalize() 112 id = id.Normalize()
77 if !id.Ancestor { 113 if !id.Ancestor {
78 return id.SortBy 114 return id.SortBy
79 } 115 }
80 ret := make([]IndexColumn, 0, len(id.SortBy)+1) 116 ret := make([]IndexColumn, 0, len(id.SortBy)+1)
81 ret = append(ret, IndexColumn{Property: "__ancestor__"}) 117 ret = append(ret, IndexColumn{Property: "__ancestor__"})
82 return append(ret, id.SortBy...) 118 return append(ret, id.SortBy...)
83 } 119 }
84 120
121 // PrepForIdxTable normalize and then flips the IndexDefinition.
85 func (id *IndexDefinition) PrepForIdxTable() *IndexDefinition { 122 func (id *IndexDefinition) PrepForIdxTable() *IndexDefinition {
86 return id.Normalize().Flip() 123 return id.Normalize().Flip()
87 } 124 }
88 125
126 // Flip returns an IndexDefinition with its SortBy field in reverse order.
89 func (id *IndexDefinition) Flip() *IndexDefinition { 127 func (id *IndexDefinition) Flip() *IndexDefinition {
90 ret := *id 128 ret := *id
91 ret.SortBy = make([]IndexColumn, 0, len(id.SortBy)) 129 ret.SortBy = make([]IndexColumn, 0, len(id.SortBy))
92 for i := len(id.SortBy) - 1; i >= 0; i-- { 130 for i := len(id.SortBy) - 1; i >= 0; i-- {
93 ret.SortBy = append(ret.SortBy, id.SortBy[i]) 131 ret.SortBy = append(ret.SortBy, id.SortBy[i])
94 } 132 }
95 return &ret 133 return &ret
96 } 134 }
97 135
98 // Yeah who needs templates, right? 136 // Yeah who needs templates, right?
(...skipping 28 matching lines...) Expand all
127 if a == b { 165 if a == b {
128 return 0 166 return 0
129 } 167 }
130 if a > b { 168 if a > b {
131 return 1 169 return 1
132 } 170 }
133 return -1 171 return -1
134 } 172 }
135 } 173 }
136 174
137 func (i *IndexDefinition) Less(o *IndexDefinition) bool { 175 // Less returns true iff id is ordered before o.
176 func (id *IndexDefinition) Less(o *IndexDefinition) bool {
138 decide := func(v int) (ret, keepGoing bool) { 177 decide := func(v int) (ret, keepGoing bool) {
139 if v > 0 { 178 if v > 0 {
140 return false, false 179 return false, false
141 } 180 }
142 if v < 0 { 181 if v < 0 {
143 return true, false 182 return true, false
144 } 183 }
145 return false, true 184 return false, true
146 } 185 }
147 186
148 factors := []func() int{ 187 factors := []func() int{
149 » » cmpBool(i.Builtin(), o.Builtin()), 188 » » cmpBool(id.Builtin(), o.Builtin()),
150 » » cmpString(i.Kind, o.Kind), 189 » » cmpString(id.Kind, o.Kind),
151 » » cmpBool(i.Ancestor, o.Ancestor), 190 » » cmpBool(id.Ancestor, o.Ancestor),
152 » » cmpInt(len(i.SortBy), len(o.SortBy)), 191 » » cmpInt(len(id.SortBy), len(o.SortBy)),
153 } 192 }
154 for _, f := range factors { 193 for _, f := range factors {
155 ret, keepGoing := decide(f()) 194 ret, keepGoing := decide(f())
156 if !keepGoing { 195 if !keepGoing {
157 return ret 196 return ret
158 } 197 }
159 } 198 }
160 » for idx := range i.SortBy { 199 » for idx := range id.SortBy {
161 » » ret, keepGoing := decide(i.SortBy[idx].cmp(o.SortBy[idx])) 200 » » ret, keepGoing := decide(id.SortBy[idx].cmp(o.SortBy[idx]))
162 if !keepGoing { 201 if !keepGoing {
163 return ret 202 return ret
164 } 203 }
165 } 204 }
166 return false 205 return false
167 } 206 }
168 207
169 func (i *IndexDefinition) Builtin() bool { 208 // Builtin returns true iff the IndexDefinition is one of the automatic built-in
170 » return !i.Ancestor && len(i.SortBy) <= 1 209 // indexes.
210 func (id *IndexDefinition) Builtin() bool {
211 » return !id.Ancestor && len(id.SortBy) <= 1
171 } 212 }
172 213
173 func (i *IndexDefinition) Compound() bool { 214 // Compound returns true iff this IndexDefinition is a valid compound index
174 » if i.Kind == "" || len(i.SortBy) <= 1 { 215 // definition.
216 //
217 // NOTE: !Builtin() does not imply Compound().
218 func (id *IndexDefinition) Compound() bool {
219 » if id.Kind == "" || len(id.SortBy) <= 1 {
175 return false 220 return false
176 } 221 }
177 » for _, sb := range i.SortBy { 222 » for _, sb := range id.SortBy {
178 if sb.Property == "" || sb.Property == "__ancestor__" { 223 if sb.Property == "" || sb.Property == "__ancestor__" {
179 return false 224 return false
180 } 225 }
181 } 226 }
182 return true 227 return true
183 } 228 }
184 229
185 func (i *IndexDefinition) String() string { 230 func (id *IndexDefinition) String() string {
186 ret := &bytes.Buffer{} 231 ret := &bytes.Buffer{}
187 » if i.Builtin() { 232 » wr := func(r rune) {
188 » » ret.WriteRune('B') 233 » » _, err := ret.WriteRune(r)
234 » » if err != nil {
235 » » » panic(err)
236 » » }
237 » }
238
239 » ws := func(s string) {
240 » » _, err := ret.WriteString(s)
241 » » if err != nil {
242 » » » panic(err)
243 » » }
244 » }
245
246 » if id.Builtin() {
247 » » wr('B')
189 } else { 248 } else {
190 » » ret.WriteRune('C') 249 » » wr('C')
191 } 250 }
192 » ret.WriteRune(':') 251 » wr(':')
193 » ret.WriteString(i.Kind) 252 » ws(id.Kind)
194 » if i.Ancestor { 253 » if id.Ancestor {
195 » » ret.WriteString("|A") 254 » » ws("|A")
196 } 255 }
197 » for _, sb := range i.SortBy { 256 » for _, sb := range id.SortBy {
198 » » ret.WriteRune('/') 257 » » wr('/')
199 » » if sb.Direction == DESCENDING { 258 » » if sb.Descending {
200 » » » ret.WriteRune('-') 259 » » » wr('-')
201 } 260 }
202 » » ret.WriteString(sb.Property) 261 » » ws(sb.Property)
203 } 262 }
204 return ret.String() 263 return ret.String()
205 } 264 }
OLDNEW
« no previous file with comments | « service/datastore/finalized_query.go ('k') | service/datastore/index_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698