| 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 |