• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2011 Google Inc. All rights reserved.
2// Use of this source code is governed by the Apache 2.0
3// license that can be found in the LICENSE file.
4
5/*
6Package datastore provides a client for App Engine's datastore service.
7
8
9Basic Operations
10
11Entities are the unit of storage and are associated with a key. A key
12consists of an optional parent key, a string application ID, a string kind
13(also known as an entity type), and either a StringID or an IntID. A
14StringID is also known as an entity name or key name.
15
16It is valid to create a key with a zero StringID and a zero IntID; this is
17called an incomplete key, and does not refer to any saved entity. Putting an
18entity into the datastore under an incomplete key will cause a unique key
19to be generated for that entity, with a non-zero IntID.
20
21An entity's contents are a mapping from case-sensitive field names to values.
22Valid value types are:
23  - signed integers (int, int8, int16, int32 and int64),
24  - bool,
25  - string,
26  - float32 and float64,
27  - []byte (up to 1 megabyte in length),
28  - any type whose underlying type is one of the above predeclared types,
29  - ByteString,
30  - *Key,
31  - time.Time (stored with microsecond precision),
32  - appengine.BlobKey,
33  - appengine.GeoPoint,
34  - structs whose fields are all valid value types,
35  - slices of any of the above.
36
37Slices of structs are valid, as are structs that contain slices. However, if
38one struct contains another, then at most one of those can be repeated. This
39disqualifies recursively defined struct types: any struct T that (directly or
40indirectly) contains a []T.
41
42The Get and Put functions load and save an entity's contents. An entity's
43contents are typically represented by a struct pointer.
44
45Example code:
46
47	type Entity struct {
48		Value string
49	}
50
51	func handle(w http.ResponseWriter, r *http.Request) {
52		ctx := appengine.NewContext(r)
53
54		k := datastore.NewKey(ctx, "Entity", "stringID", 0, nil)
55		e := new(Entity)
56		if err := datastore.Get(ctx, k, e); err != nil {
57			http.Error(w, err.Error(), 500)
58			return
59		}
60
61		old := e.Value
62		e.Value = r.URL.Path
63
64		if _, err := datastore.Put(ctx, k, e); err != nil {
65			http.Error(w, err.Error(), 500)
66			return
67		}
68
69		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
70		fmt.Fprintf(w, "old=%q\nnew=%q\n", old, e.Value)
71	}
72
73GetMulti, PutMulti and DeleteMulti are batch versions of the Get, Put and
74Delete functions. They take a []*Key instead of a *Key, and may return an
75appengine.MultiError when encountering partial failure.
76
77
78Properties
79
80An entity's contents can be represented by a variety of types. These are
81typically struct pointers, but can also be any type that implements the
82PropertyLoadSaver interface. If using a struct pointer, you do not have to
83explicitly implement the PropertyLoadSaver interface; the datastore will
84automatically convert via reflection. If a struct pointer does implement that
85interface then those methods will be used in preference to the default
86behavior for struct pointers. Struct pointers are more strongly typed and are
87easier to use; PropertyLoadSavers are more flexible.
88
89The actual types passed do not have to match between Get and Put calls or even
90across different calls to datastore. It is valid to put a *PropertyList and
91get that same entity as a *myStruct, or put a *myStruct0 and get a *myStruct1.
92Conceptually, any entity is saved as a sequence of properties, and is loaded
93into the destination value on a property-by-property basis. When loading into
94a struct pointer, an entity that cannot be completely represented (such as a
95missing field) will result in an ErrFieldMismatch error but it is up to the
96caller whether this error is fatal, recoverable or ignorable.
97
98By default, for struct pointers, all properties are potentially indexed, and
99the property name is the same as the field name (and hence must start with an
100upper case letter).
101
102Fields may have a `datastore:"name,options"` tag. The tag name is the
103property name, which must be one or more valid Go identifiers joined by ".",
104but may start with a lower case letter. An empty tag name means to just use the
105field name. A "-" tag name means that the datastore will ignore that field.
106
107The only valid options are "omitempty" and "noindex".
108
109If the options include "omitempty" and the value of the field is empty, then the field will be omitted on Save.
110The empty values are false, 0, any nil interface value, and any array, slice, map, or string of length zero.
111Struct field values will never be empty.
112
113If options include "noindex" then the field will not be indexed. All fields are indexed
114by default. Strings or byte slices longer than 1500 bytes cannot be indexed;
115fields used to store long strings and byte slices must be tagged with "noindex"
116or they will cause Put operations to fail.
117
118To use multiple options together, separate them by a comma.
119The order does not matter.
120
121If the options is "" then the comma may be omitted.
122
123Example code:
124
125	// A and B are renamed to a and b.
126	// A, C and J are not indexed.
127	// D's tag is equivalent to having no tag at all (E).
128	// I is ignored entirely by the datastore.
129	// J has tag information for both the datastore and json packages.
130	type TaggedStruct struct {
131		A int `datastore:"a,noindex"`
132		B int `datastore:"b"`
133		C int `datastore:",noindex"`
134		D int `datastore:""`
135		E int
136		I int `datastore:"-"`
137		J int `datastore:",noindex" json:"j"`
138	}
139
140
141Structured Properties
142
143If the struct pointed to contains other structs, then the nested or embedded
144structs are flattened. For example, given these definitions:
145
146	type Inner1 struct {
147		W int32
148		X string
149	}
150
151	type Inner2 struct {
152		Y float64
153	}
154
155	type Inner3 struct {
156		Z bool
157	}
158
159	type Outer struct {
160		A int16
161		I []Inner1
162		J Inner2
163		Inner3
164	}
165
166then an Outer's properties would be equivalent to those of:
167
168	type OuterEquivalent struct {
169		A     int16
170		IDotW []int32  `datastore:"I.W"`
171		IDotX []string `datastore:"I.X"`
172		JDotY float64  `datastore:"J.Y"`
173		Z     bool
174	}
175
176If Outer's embedded Inner3 field was tagged as `datastore:"Foo"` then the
177equivalent field would instead be: FooDotZ bool `datastore:"Foo.Z"`.
178
179If an outer struct is tagged "noindex" then all of its implicit flattened
180fields are effectively "noindex".
181
182
183The PropertyLoadSaver Interface
184
185An entity's contents can also be represented by any type that implements the
186PropertyLoadSaver interface. This type may be a struct pointer, but it does
187not have to be. The datastore package will call Load when getting the entity's
188contents, and Save when putting the entity's contents.
189Possible uses include deriving non-stored fields, verifying fields, or indexing
190a field only if its value is positive.
191
192Example code:
193
194	type CustomPropsExample struct {
195		I, J int
196		// Sum is not stored, but should always be equal to I + J.
197		Sum int `datastore:"-"`
198	}
199
200	func (x *CustomPropsExample) Load(ps []datastore.Property) error {
201		// Load I and J as usual.
202		if err := datastore.LoadStruct(x, ps); err != nil {
203			return err
204		}
205		// Derive the Sum field.
206		x.Sum = x.I + x.J
207		return nil
208	}
209
210	func (x *CustomPropsExample) Save() ([]datastore.Property, error) {
211		// Validate the Sum field.
212		if x.Sum != x.I + x.J {
213			return nil, errors.New("CustomPropsExample has inconsistent sum")
214		}
215		// Save I and J as usual. The code below is equivalent to calling
216		// "return datastore.SaveStruct(x)", but is done manually for
217		// demonstration purposes.
218		return []datastore.Property{
219			{
220				Name:  "I",
221				Value: int64(x.I),
222			},
223			{
224				Name:  "J",
225				Value: int64(x.J),
226			},
227		}, nil
228	}
229
230The *PropertyList type implements PropertyLoadSaver, and can therefore hold an
231arbitrary entity's contents.
232
233
234Queries
235
236Queries retrieve entities based on their properties or key's ancestry. Running
237a query yields an iterator of results: either keys or (key, entity) pairs.
238Queries are re-usable and it is safe to call Query.Run from concurrent
239goroutines. Iterators are not safe for concurrent use.
240
241Queries are immutable, and are either created by calling NewQuery, or derived
242from an existing query by calling a method like Filter or Order that returns a
243new query value. A query is typically constructed by calling NewQuery followed
244by a chain of zero or more such methods. These methods are:
245  - Ancestor and Filter constrain the entities returned by running a query.
246  - Order affects the order in which they are returned.
247  - Project constrains the fields returned.
248  - Distinct de-duplicates projected entities.
249  - KeysOnly makes the iterator return only keys, not (key, entity) pairs.
250  - Start, End, Offset and Limit define which sub-sequence of matching entities
251    to return. Start and End take cursors, Offset and Limit take integers. Start
252    and Offset affect the first result, End and Limit affect the last result.
253    If both Start and Offset are set, then the offset is relative to Start.
254    If both End and Limit are set, then the earliest constraint wins. Limit is
255    relative to Start+Offset, not relative to End. As a special case, a
256    negative limit means unlimited.
257
258Example code:
259
260	type Widget struct {
261		Description string
262		Price       int
263	}
264
265	func handle(w http.ResponseWriter, r *http.Request) {
266		ctx := appengine.NewContext(r)
267		q := datastore.NewQuery("Widget").
268			Filter("Price <", 1000).
269			Order("-Price")
270		b := new(bytes.Buffer)
271		for t := q.Run(ctx); ; {
272			var x Widget
273			key, err := t.Next(&x)
274			if err == datastore.Done {
275				break
276			}
277			if err != nil {
278				serveError(ctx, w, err)
279				return
280			}
281			fmt.Fprintf(b, "Key=%v\nWidget=%#v\n\n", key, x)
282		}
283		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
284		io.Copy(w, b)
285	}
286
287
288Transactions
289
290RunInTransaction runs a function in a transaction.
291
292Example code:
293
294	type Counter struct {
295		Count int
296	}
297
298	func inc(ctx context.Context, key *datastore.Key) (int, error) {
299		var x Counter
300		if err := datastore.Get(ctx, key, &x); err != nil && err != datastore.ErrNoSuchEntity {
301			return 0, err
302		}
303		x.Count++
304		if _, err := datastore.Put(ctx, key, &x); err != nil {
305			return 0, err
306		}
307		return x.Count, nil
308	}
309
310	func handle(w http.ResponseWriter, r *http.Request) {
311		ctx := appengine.NewContext(r)
312		var count int
313		err := datastore.RunInTransaction(ctx, func(ctx context.Context) error {
314			var err1 error
315			count, err1 = inc(ctx, datastore.NewKey(ctx, "Counter", "singleton", 0, nil))
316			return err1
317		}, nil)
318		if err != nil {
319			serveError(ctx, w, err)
320			return
321		}
322		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
323		fmt.Fprintf(w, "Count=%d", count)
324	}
325
326
327Metadata
328
329The datastore package provides access to some of App Engine's datastore
330metadata. This metadata includes information about the entity groups,
331namespaces, entity kinds, and properties in the datastore, as well as the
332property representations for each property.
333
334Example code:
335
336	func handle(w http.ResponseWriter, r *http.Request) {
337		// Print all the kinds in the datastore, with all the indexed
338		// properties (and their representations) for each.
339		ctx := appengine.NewContext(r)
340
341		kinds, err := datastore.Kinds(ctx)
342		if err != nil {
343			serveError(ctx, w, err)
344			return
345		}
346
347		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
348		for _, kind := range kinds {
349			fmt.Fprintf(w, "%s:\n", kind)
350			props, err := datastore.KindProperties(ctx, kind)
351			if err != nil {
352				fmt.Fprintln(w, "\t(unable to retrieve properties)")
353				continue
354			}
355			for p, rep := range props {
356				fmt.Fprintf(w, "\t-%s (%s)\n", p, strings.Join(rep, ", "))
357			}
358		}
359	}
360*/
361package datastore
362