OLD | NEW |
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 Loading... |
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 } |
OLD | NEW |