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 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 "math" | 10 "math" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
99 // a path in a struct (in the struct loading routine in helper). | 99 // a path in a struct (in the struct loading routine in helper). |
100 | 100 |
101 ToProperty() (Property, error) | 101 ToProperty() (Property, error) |
102 FromProperty(Property) error | 102 FromProperty(Property) error |
103 } | 103 } |
104 | 104 |
105 // PropertyType is a single-byte representation of the type of data contained | 105 // PropertyType is a single-byte representation of the type of data contained |
106 // in a Property. The specific values of this type information are chosen so | 106 // in a Property. The specific values of this type information are chosen so |
107 // that the types sort according to the order of types as sorted by the | 107 // that the types sort according to the order of types as sorted by the |
108 // datastore. | 108 // datastore. |
109 // | |
110 // Note that indexes may only contain values of the following types: | |
111 // PTNull | |
112 // PTInt | |
113 // PTBool | |
114 // PTFloat | |
115 // PTString | |
116 // PTGeoPoint | |
117 // PTKey | |
118 // | |
119 // The biggest impact of this is that if you do a Projection query, you'll only | |
120 // get back Properties with the above types (e.g. if you store a PTTime value, | |
121 // then Project on it, you'll get back a PTInt value). For convenience, Property | |
122 // has a Project(PropertyType) method which will side-cast to your intended | |
123 // type. If you project into a structure with the high-level Interface | |
124 // implementation, or use StructPLS, this conversion will be done for you | |
125 // automatically, using the type of the destination field to cast. | |
109 type PropertyType byte | 126 type PropertyType byte |
110 | 127 |
111 // These constants are in the order described by | 128 // These constants are in the order described by |
112 // https://cloud.google.com/appengine/docs/go/datastore/entities#Go_Value_type _ordering | 129 // https://cloud.google.com/appengine/docs/go/datastore/entities#Go_Value_type _ordering |
113 // with a slight divergence for the Int/Time split. | 130 // with a slight divergence for the Int/Time split. |
114 // | 131 // |
115 // NOTE: this enum can only occupy 7 bits, because we use the high bit to encode | 132 // NOTE: this enum can only occupy 7 bits, because we use the high bit to encode |
116 // indexed/non-indexed, and we additionally require that all valid values and | 133 // indexed/non-indexed, and we additionally require that all valid values and |
117 // all INVERTED valid values must never equal 0xFF or 0x00. The reason for this | 134 // all INVERTED valid values must never equal 0xFF or 0x00. The reason for this |
118 // constraint is that we must always be able to create a byte that sorts before | 135 // constraint is that we must always be able to create a byte that sorts before |
119 // and after it. | 136 // and after it. |
120 // | 137 // |
121 // See "./serialize".WriteProperty and "impl/memory".increment for more info. | 138 // See "./serialize".WriteProperty and "impl/memory".increment for more info. |
122 const ( | 139 const ( |
140 // PTNull represents the 'nil' value. This is only directly visible when | |
141 // reading/writing a PropertyMap. If a PTNull value is loaded into a str uct | |
142 // field, the field will be initialized with its zero value. If a struct with | |
143 // a zero value is saved from a struct, it will still retain the field's type, | |
144 // not the 'nil' type. This is in contrast to other GAE languages such a s | |
145 // python where 'None' is a distinct value than the 'zero' value (e.g. a | |
146 // StringProperty can have the value "" OR None). | |
147 // | |
148 // PTNull is a Projection-query type | |
123 PTNull PropertyType = iota | 149 PTNull PropertyType = iota |
150 | |
151 // PTInt is always an int64. | |
152 // | |
153 // This is a Projection-query type, and may be projected to PTTime. | |
124 PTInt | 154 PTInt |
125 | |
126 // PTTime is a slight divergence from the way that datastore natively st ores | |
127 // time. In datastore, times and integers actually sort together | |
128 // (apparently?). This is probably insane, and I don't want to add the | |
129 // complexity of field 'meaning' as a sparate concept from the field's ' type' | |
130 // (which is what datastore seems to do, judging from the protobufs). So if | |
131 // you're here because you implemented an app which relies on time.Time and | |
132 // int64 sorting together, then this is why your app acts differently in | |
133 // production. My advice is to NOT DO THAT. If you really want this (and you | |
134 // probably don't), you should take care of the time.Time <-> int64 conv ersion | |
135 // in your app and just use a property type of int64 (consider using | |
136 // PropertyConverter). | |
137 PTTime | 155 PTTime |
138 | 156 |
139 » // PTBoolFalse and True are also a slight divergence, but not a semantic | 157 » // PTBool represents true or false |
140 » // one. IIUC, in datastore 'bool' is actually the type and the value is either | 158 » // |
141 » // 0 or 1 (taking another byte to store). Since we have plenty of space in | 159 » // This is a Projection-query type. |
142 » // this type byte, I just merge the value into the type for booleans. If this | 160 » PTBool |
143 » // becomes problematic, consider changing this to just pvBool, and then | |
144 » // encoding a 0 or 1 as a byte in the relevant marshalling routines. | |
145 » PTBoolFalse | |
146 » PTBoolTrue | |
147 | 161 |
148 » PTBytes // []byte or datastore.ByteString | 162 » // PTBytes represents []byte |
149 » PTString // string or string noindex | 163 » PTBytes |
164 | |
165 » // PTString is used to represent all strings (text). | |
166 » // | |
167 » // PTString is a Projection-query type and may be projected to PTBytes o r | |
168 » // PTBlobKey. | |
169 » PTString | |
170 | |
171 » // PTFloat is always a float64. | |
172 » // | |
173 » // This is a Projection-query type. | |
150 PTFloat | 174 PTFloat |
175 | |
176 // PTGeoPoint is a Projection-query type. | |
151 PTGeoPoint | 177 PTGeoPoint |
178 | |
179 // PTKey represents a Key object. | |
180 // | |
181 // PTKey is a Projection-query type. | |
152 PTKey | 182 PTKey |
183 | |
184 // PTBlobKey represents a blobstore.Key | |
153 PTBlobKey | 185 PTBlobKey |
154 | 186 |
155 // NOTE: THIS MUST BE LAST VALUE FOR THE init() ASSERTION BELOW TO WORK. | 187 // NOTE: THIS MUST BE LAST VALUE FOR THE init() ASSERTION BELOW TO WORK. |
156 PTUnknown | 188 PTUnknown |
157 ) | 189 ) |
158 | 190 |
159 func init() { | 191 func init() { |
160 if PTUnknown > 0x7e { | 192 if PTUnknown > 0x7e { |
161 panic( | 193 panic( |
162 "PTUnknown (and therefore PropertyType) exceeds 0x7e. Th is conflicts " + | 194 "PTUnknown (and therefore PropertyType) exceeds 0x7e. Th is conflicts " + |
163 "with serialize.WriteProperty's use of the high bit to indicate " + | 195 "with serialize.WriteProperty's use of the high bit to indicate " + |
164 "NoIndex and/or \"impl/memory\".increment's abil ity to guarantee " + | 196 "NoIndex and/or \"impl/memory\".increment's abil ity to guarantee " + |
165 "incrementability.") | 197 "incrementability.") |
166 } | 198 } |
167 } | 199 } |
168 | 200 |
169 func (t PropertyType) String() string { | 201 func (t PropertyType) String() string { |
170 switch t { | 202 switch t { |
171 case PTNull: | 203 case PTNull: |
172 return "PTNull" | 204 return "PTNull" |
173 case PTInt: | 205 case PTInt: |
174 return "PTInt" | 206 return "PTInt" |
175 case PTTime: | 207 case PTTime: |
176 return "PTTime" | 208 return "PTTime" |
177 » case PTBoolFalse: | 209 » case PTBool: |
178 » » return "PTBoolFalse" | 210 » » return "PTBool" |
179 » case PTBoolTrue: | |
180 » » return "PTBoolTrue" | |
181 case PTBytes: | 211 case PTBytes: |
182 return "PTBytes" | 212 return "PTBytes" |
183 case PTString: | 213 case PTString: |
184 return "PTString" | 214 return "PTString" |
185 case PTFloat: | 215 case PTFloat: |
186 return "PTFloat" | 216 return "PTFloat" |
187 case PTGeoPoint: | 217 case PTGeoPoint: |
188 return "PTGeoPoint" | 218 return "PTGeoPoint" |
189 case PTKey: | 219 case PTKey: |
190 return "PTKey" | 220 return "PTKey" |
191 case PTBlobKey: | 221 case PTBlobKey: |
192 return "PTBlobKey" | 222 return "PTBlobKey" |
193 default: | 223 default: |
194 return fmt.Sprintf("PTUnknown(%02x)", byte(t)) | 224 return fmt.Sprintf("PTUnknown(%02x)", byte(t)) |
195 } | 225 } |
196 } | 226 } |
197 | 227 |
198 // PropertyTypeOf returns the PT* type of the given Property-compatible | 228 // PropertyTypeOf returns the PT* type of the given Property-compatible |
199 // value v. If checkValid is true, this method will also ensure that time.Time | 229 // value v. If checkValid is true, this method will also ensure that time.Time |
200 // and GeoPoint have valid values. | 230 // and GeoPoint have valid values. |
201 func PropertyTypeOf(v interface{}, checkValid bool) (PropertyType, error) { | 231 func PropertyTypeOf(v interface{}, checkValid bool) (PropertyType, error) { |
202 switch x := v.(type) { | 232 switch x := v.(type) { |
203 case nil: | 233 case nil: |
204 return PTNull, nil | 234 return PTNull, nil |
205 case int64: | 235 case int64: |
206 return PTInt, nil | 236 return PTInt, nil |
207 case float64: | 237 case float64: |
208 return PTFloat, nil | 238 return PTFloat, nil |
209 case bool: | 239 case bool: |
210 » » if x { | 240 » » return PTBool, nil |
211 » » » return PTBoolTrue, nil | 241 » case []byte: |
212 » » } | |
213 » » return PTBoolFalse, nil | |
214 » case []byte, ByteString: | |
215 return PTBytes, nil | 242 return PTBytes, nil |
216 case blobstore.Key: | 243 case blobstore.Key: |
217 return PTBlobKey, nil | 244 return PTBlobKey, nil |
218 case string: | 245 case string: |
219 return PTString, nil | 246 return PTString, nil |
220 case Key: | 247 case Key: |
221 // TODO(riannucci): Check key for validity in its own namespace? | 248 // TODO(riannucci): Check key for validity in its own namespace? |
222 return PTKey, nil | 249 return PTKey, nil |
223 case time.Time: | 250 case time.Time: |
224 err := error(nil) | 251 err := error(nil) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
265 o = v.Int() | 292 o = v.Int() |
266 case reflect.Bool: | 293 case reflect.Bool: |
267 o = v.Bool() | 294 o = v.Bool() |
268 case reflect.String: | 295 case reflect.String: |
269 if t != typeOfBSKey { | 296 if t != typeOfBSKey { |
270 o = v.String() | 297 o = v.String() |
271 } | 298 } |
272 case reflect.Float32, reflect.Float64: | 299 case reflect.Float32, reflect.Float64: |
273 o = v.Float() | 300 o = v.Float() |
274 case reflect.Slice: | 301 case reflect.Slice: |
275 » » if t != typeOfByteString && t.Elem().Kind() == reflect.Uint8 { | 302 » » if t.Elem().Kind() == reflect.Uint8 { |
276 o = v.Bytes() | 303 o = v.Bytes() |
277 } | 304 } |
278 case reflect.Struct: | 305 case reflect.Struct: |
279 if t == typeOfTime { | 306 if t == typeOfTime { |
280 // time in a Property can only hold microseconds | 307 // time in a Property can only hold microseconds |
281 o = v.Interface().(time.Time).Round(time.Microsecond) | 308 o = v.Interface().(time.Time).Round(time.Microsecond) |
282 } | 309 } |
283 } | 310 } |
284 return o | 311 return o |
285 } | 312 } |
286 | 313 |
287 // Value returns the current value held by this property. It's guaranteed to | 314 // Value returns the current value held by this property. It's guaranteed to |
288 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return | 315 // be a valid value type (i.e. `p.SetValue(p.Value(), true)` will never return |
289 // an error). | 316 // an error). |
290 func (p *Property) Value() interface{} { return p.value } | 317 func (p *Property) Value() interface{} { return p.value } |
291 | 318 |
292 // IndexSetting says weather or not the datastore should create indicies for | 319 // IndexSetting says weather or not the datastore should create indicies for |
293 // this value. | 320 // this value. |
294 func (p *Property) IndexSetting() IndexSetting { return p.indexSetting } | 321 func (p *Property) IndexSetting() IndexSetting { return p.indexSetting } |
295 | 322 |
296 // Type is the PT* type of the data contained in Value(). | 323 // Type is the PT* type of the data contained in Value(). |
297 func (p *Property) Type() PropertyType { return p.propType } | 324 func (p *Property) Type() PropertyType { return p.propType } |
298 | 325 |
299 // SetValue sets the Value field of a Property, and ensures that its value | 326 // SetValue sets the Value field of a Property, and ensures that its value |
300 // conforms to the permissible types. That way, you're guaranteed that if you | 327 // conforms to the permissible types. That way, you're guaranteed that if you |
301 // have a Property, its value is valid. | 328 // have a Property, its value is valid. |
302 // | 329 // |
303 // value is the property value. The valid types are: | 330 // value is the property value. The valid types are: |
304 // - int64 | 331 // - int64 |
332 // - time.Time | |
305 // - bool | 333 // - bool |
306 // - string | 334 // - string |
335 // (only the first 1500 bytes is indexable) | |
336 // - []byte | |
337 // (only the first 1500 bytes is indexable) | |
338 // - blobstore.Key | |
339 // (only the first 1500 bytes is indexable) | |
307 // - float64 | 340 // - float64 |
308 // - ByteString | |
309 // - Key | 341 // - Key |
310 // - time.Time | |
311 // - blobstore.Key | |
312 // - GeoPoint | 342 // - GeoPoint |
313 // - []byte (up to 1 megabyte in length) | |
314 // This set is smaller than the set of valid struct field types that the | 343 // This set is smaller than the set of valid struct field types that the |
315 // datastore can load and save. A Property Value cannot be a slice (apart | 344 // datastore can load and save. A Property Value cannot be a slice (apart |
316 // from []byte); use multiple Properties instead. Also, a Value's type | 345 // from []byte); use multiple Properties instead. Also, a Value's type |
317 // must be explicitly on the list above; it is not sufficient for the | 346 // must be explicitly on the list above; it is not sufficient for the |
318 // underlying type to be on that list. For example, a Value of "type | 347 // underlying type to be on that list. For example, a Value of "type |
319 // myInt64 int64" is invalid. Smaller-width integers and floats are also | 348 // myInt64 int64" is invalid. Smaller-width integers and floats are also |
320 // invalid. Again, this is more restrictive than the set of valid struct | 349 // invalid. Again, this is more restrictive than the set of valid struct |
321 // field types. | 350 // field types. |
322 // | 351 // |
323 // A value may also be the nil interface value; this is equivalent to | 352 // A value may also be the nil interface value; this is equivalent to |
324 // Python's None but not directly representable by a Go struct. Loading | 353 // Python's None but not directly representable by a Go struct. Loading |
325 // a nil-valued property into a struct will set that field to the zero | 354 // a nil-valued property into a struct will set that field to the zero |
326 // value. | 355 // value. |
327 func (p *Property) SetValue(value interface{}, is IndexSetting) (err error) { | 356 func (p *Property) SetValue(value interface{}, is IndexSetting) (err error) { |
328 pt := PTNull | 357 pt := PTNull |
329 if value != nil { | 358 if value != nil { |
330 value = UpconvertUnderlyingType(value) | 359 value = UpconvertUnderlyingType(value) |
331 if pt, err = PropertyTypeOf(value, true); err != nil { | 360 if pt, err = PropertyTypeOf(value, true); err != nil { |
332 return | 361 return |
333 } | 362 } |
334 } | 363 } |
335 p.propType = pt | 364 p.propType = pt |
336 p.value = value | 365 p.value = value |
337 p.indexSetting = is | 366 p.indexSetting = is |
338 » if _, ok := value.([]byte); ok { | 367 » return |
339 » » p.indexSetting = NoIndex | 368 } |
369 | |
370 // ForIndex gets a new Property with its value and type converted as if it were | |
371 // being stored in a datastore index. See the doc on PropertyType for more info. | |
372 func (p Property) ForIndex() Property { | |
373 » switch p.propType { | |
374 » case PTNull, PTInt, PTBool, PTString, PTFloat, PTGeoPoint, PTKey: | |
375 » » return p | |
376 | |
377 » case PTTime: | |
378 » » v, _ := p.Project(PTInt) | |
379 » » return Property{v, p.indexSetting, PTInt} | |
380 | |
381 » case PTBytes, PTBlobKey: | |
382 » » v, _ := p.Project(PTString) | |
383 » » return Property{v, p.indexSetting, PTString} | |
340 } | 384 } |
341 » return | 385 » panic(fmt.Errorf("unknown PropertyType: %s", p.propType)) |
386 } | |
387 | |
388 // Project can be used to project a Property retrieved from a Projection query | |
389 // into a different datatype. For example, if you have a PTInt property, you | |
390 // could Project(PTTime) to convert it to a time.Time. The following conversions | |
391 // are supported: | |
392 // PTInt <-> PTTime | |
dnj (Google)
2015/09/10 16:26:11
Note that you can project a type to itself (identi
iannucci
2015/09/10 17:29:21
Oh, right. Done.
| |
393 // PTString <-> PTBlobKey | |
394 // PTString <-> PTBytes | |
395 // PTNull <-> Anything | |
396 func (p *Property) Project(to PropertyType) (interface{}, error) { | |
397 » switch { | |
398 » case to == p.propType: | |
399 » » return p.value, nil | |
400 | |
401 » case to == PTInt && p.propType == PTTime: | |
402 » » t := p.value.(time.Time) | |
403 » » v := uint64(t.Unix())*1e6 + uint64(t.Nanosecond()/1e3) | |
404 » » return int64(v), nil | |
405 | |
406 » case to == PTTime && p.propType == PTInt: | |
407 » » v := p.value.(int64) | |
408 » » return time.Unix(int64(v/1e6), int64((v%1e6)*1e3)).UTC(), nil | |
409 | |
410 » case to == PTString && p.propType == PTBytes: | |
411 » » return string(p.value.([]byte)), nil | |
412 | |
413 » case to == PTString && p.propType == PTBlobKey: | |
414 » » return string(p.value.(blobstore.Key)), nil | |
415 | |
416 » case to == PTBytes && p.propType == PTString: | |
417 » » return []byte(p.value.(string)), nil | |
418 | |
419 » case to == PTBlobKey && p.propType == PTString: | |
420 » » return blobstore.Key(p.value.(string)), nil | |
421 | |
422 » case to == PTNull: | |
423 » » return nil, nil | |
424 | |
425 » case p.propType == PTNull: | |
426 » » switch to { | |
427 » » case PTInt: | |
428 » » » return int64(0), nil | |
429 » » case PTTime: | |
430 » » » return time.Time{}, nil | |
431 » » case PTBool: | |
432 » » » return false, nil | |
433 » » case PTBytes: | |
434 » » » return []byte(nil), nil | |
435 » » case PTString: | |
436 » » » return "", nil | |
437 » » case PTFloat: | |
438 » » » return float64(0), nil | |
439 » » case PTGeoPoint: | |
440 » » » return GeoPoint{}, nil | |
441 » » case PTKey: | |
442 » » » return nil, nil | |
443 » » case PTBlobKey: | |
444 » » » return blobstore.Key(""), nil | |
445 » » } | |
446 » » fallthrough | |
447 » default: | |
448 » » return nil, fmt.Errorf("unable to project %s to %s", p.propType, to) | |
449 » } | |
342 } | 450 } |
343 | 451 |
344 // MetaGetter is a subinterface of PropertyLoadSaver, but is also used to | 452 // MetaGetter is a subinterface of PropertyLoadSaver, but is also used to |
345 // abstract the meta argument for RawInterface.GetMulti. | 453 // abstract the meta argument for RawInterface.GetMulti. |
346 type MetaGetter interface { | 454 type MetaGetter interface { |
347 // GetMeta will get information about the field which has the struct tag in | 455 // GetMeta will get information about the field which has the struct tag in |
348 // the form of `gae:"$<key>[,<default>]?"`. | 456 // the form of `gae:"$<key>[,<default>]?"`. |
349 // | 457 // |
350 // Supported metadata types are: | 458 // Supported metadata types are: |
351 // int64 - may have default (ascii encoded base-10) | 459 // int64 - may have default (ascii encoded base-10) |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 dflt = UpconvertUnderlyingType(dflt) | 623 dflt = UpconvertUnderlyingType(dflt) |
516 cur, err := gm(key) | 624 cur, err := gm(key) |
517 if err != nil { | 625 if err != nil { |
518 return dflt | 626 return dflt |
519 } | 627 } |
520 if dflt != nil && reflect.TypeOf(cur) != reflect.TypeOf(dflt) { | 628 if dflt != nil && reflect.TypeOf(cur) != reflect.TypeOf(dflt) { |
521 return dflt | 629 return dflt |
522 } | 630 } |
523 return cur | 631 return cur |
524 } | 632 } |
OLD | NEW |