Documentation
¶
Index ¶
- Constants
- Variables
- func MarshalIndexYAML(indexes []Index) ([]byte, error)
- func UnmarshalEntity(e Entity, out any) error
- type Batch
- type Client
- func (c *Client) AllocateIDs(ctx context.Context, keys []Key) ([]Key, error)
- func (c *Client) Avg(ctx context.Context, q *Query, property string, opts ...ReadOption) (Value, error)
- func (c *Client) Close() error
- func (c *Client) CommitOverheadBytes(n int) int
- func (c *Client) Count(ctx context.Context, q *Query, opts ...ReadOption) (int64, error)
- func (c *Client) Delete(ctx context.Context, k Key, opts ...WriteOption) error
- func (c *Client) Endpoint() string
- func (c *Client) Get(ctx context.Context, key Key, opts ...ReadOption) (*Entity, error)
- func (c *Client) GetMulti(ctx context.Context, keys []Key, opts ...ReadOption) (*LookupResult, error)
- func (c *Client) Insert(ctx context.Context, e Entity, opts ...WriteOption) (Key, error)
- func (c *Client) Mutate(ctx context.Context, ms []Mutation) (*CommitResult, error)
- func (c *Client) MutationSize(m Mutation) (int, error)
- func (c *Client) Namespace() string
- func (c *Client) ProjectID() string
- func (c *Client) Put(ctx context.Context, e Entity, opts ...WriteOption) (Key, error)
- func (c *Client) Run(ctx context.Context, q *Query, opts ...ReadOption) (*Batch, error)
- func (c *Client) RunInTransaction(ctx context.Context, fn func(*Tx) error, opts ...TxOption) error
- func (c *Client) RunReadOnly(ctx context.Context, fn func(*Tx) error, opts ...TxOption) error
- func (c *Client) Sum(ctx context.Context, q *Query, property string, opts ...ReadOption) (Value, error)
- func (c *Client) Update(ctx context.Context, e Entity, opts ...WriteOption) error
- type CommitResult
- type Condition
- type Cursor
- type Direction
- type Entity
- type Error
- type Index
- type IndexProperty
- type Integer
- type Key
- func (k Key) Child(e PathElement) Key
- func (k Key) Equal(other Key) bool
- func (k Key) Incomplete() bool
- func (k Key) Kind() string
- func (k Key) MarshalJSON() ([]byte, error)
- func (k Key) String() string
- func (k *Key) UnmarshalJSON(b []byte) error
- func (k Key) Valid() error
- func (k Key) WithNamespace(namespace string) Key
- type Kind
- type LatLng
- type LookupResult
- type MoreResults
- type Mutation
- type Operator
- type Option
- func WithCredentials(creds google.Credentials) Option
- func WithDatabase(database string) Option
- func WithEndpoint(endpoint string) Option
- func WithHTTPClient(client *http.Client) Option
- func WithMaxIdleConns(n int) Option
- func WithNamespace(namespace string) Option
- func WithRetry(attempts int, base time.Duration) Option
- func WithTimeout(d time.Duration) Option
- func WithTokenSource(ts google.TokenSource) Option
- type PathElement
- type Query
- func (q *Query) Ancestor(key Key) *Query
- func (q *Query) DistinctOn(properties ...string) *Query
- func (q *Query) End(cursor Cursor) *Query
- func (q *Query) Filter(property string, op Operator, value Value) *Query
- func (q *Query) KeysOnly() *Query
- func (q *Query) Limit(n int32) *Query
- func (q *Query) Offset(n int32) *Query
- func (q *Query) Order(property string) *Query
- func (q *Query) OrderDesc(property string) *Query
- func (q *Query) Project(properties ...string) *Query
- func (q *Query) Start(cursor Cursor) *Query
- func (q *Query) Where(c Condition) *Query
- type ReadOption
- type Tx
- func (t *Tx) Avg(ctx context.Context, q *Query, property string) (Value, error)
- func (t *Tx) CommitOverheadBytes(n int) int
- func (t *Tx) Count(ctx context.Context, q *Query) (int64, error)
- func (t *Tx) Delete(k Key, opts ...WriteOption)
- func (t *Tx) Get(ctx context.Context, key Key) (*Entity, error)
- func (t *Tx) GetMulti(ctx context.Context, keys []Key) (*LookupResult, error)
- func (t *Tx) Insert(e Entity, opts ...WriteOption)
- func (t *Tx) Mutate(ms ...Mutation)
- func (t *Tx) Put(e Entity, opts ...WriteOption)
- func (t *Tx) Run(ctx context.Context, q *Query) (*Batch, error)
- func (t *Tx) Sum(ctx context.Context, q *Query, property string) (Value, error)
- func (t *Tx) Update(e Entity, opts ...WriteOption)
- type TxOption
- type Value
- func Array(vs ...Value) Value
- func Blob(v []byte) Value
- func Bool(v bool) Value
- func Float(v float64) Value
- func GeoPoint(lat, lng float64) Value
- func Int[T Integer](v T) Value
- func IntString(v string) Value
- func KeyValue(k Key) Value
- func Nested(e Entity) Value
- func Null() Value
- func String(v string) Value
- func Time(v time.Time) Value
- func Unindexed(v Value) Value
- func (v Value) AsArray() ([]Value, bool)
- func (v Value) AsBool() (bool, bool)
- func (v Value) AsBytes() ([]byte, bool)
- func (v Value) AsEntity() (Entity, bool)
- func (v Value) AsFloat() (float64, bool)
- func (v Value) AsGeoPoint() (LatLng, bool)
- func (v Value) AsInt() (int64, bool)
- func (v Value) AsKey() (Key, bool)
- func (v Value) AsNumber() (string, bool)
- func (v Value) AsString() (string, bool)
- func (v Value) AsTime() (time.Time, bool)
- func (v Value) IsNull() bool
- func (v Value) Kind() Kind
- func (v Value) MarshalJSON() ([]byte, error)
- func (v *Value) UnmarshalJSON(b []byte) error
- type WriteOption
Constants ¶
const ( // MaxLookupKeys is the most keys one lookup accepts. GetMulti checks this // before sending. MaxLookupKeys = 1000 // MaxRequestBytes bounds one API request. MaxRequestBytes = 10 << 20 // MaxTransactionBytes bounds everything one transaction writes. MaxTransactionBytes = 10 << 20 // MaxEntityBytes is the largest a single entity may be. MaxEntityBytes = 1<<20 - 4 // MaxKeyBytes is the largest a single key may be. MaxKeyBytes = 6 << 10 // MaxIndexedStringBytes is where a string property stops being indexed. // A longer value is still stored; it just cannot be filtered or ordered on. MaxIndexedStringBytes = 1500 // MaxNestingDepth is how deep entity values may nest. MaxNestingDepth = 20 )
Service limits, from the published quotas. They are exported because a caller batching work has to chunk against them, and a number copied out of the documentation into every consumer drifts silently when the service changes it.
There is deliberately no maximum-mutations-per-commit constant: Google documents no count limit on a commit. A commit is bounded in bytes, by MaxRequestBytes and, inside a transaction, MaxTransactionBytes. The only documented count of 500 is property transformations per entity, which requirement:datastore-client-scope excludes. So chunk a batch write by size, not by count.
const MaxDisjunctions = 30
MaxDisjunctions is how many disjunctions a query may expand to once its filter is put in disjunctive normal form. A query past it is rejected by the service, not here: the expansion rule is the service's and a client-side count that disagreed would refuse a query that works.
Variables ¶
var ( ErrNoProject = errors.New("datastore: no project configured") ErrNoCredentials = errors.New("datastore: no credentials configured") )
Configuration errors.
var ( ErrNoSuchEntity = errors.New("datastore: no such entity") ErrAlreadyExists = errors.New("datastore: entity already exists") ErrAborted = errors.New("datastore: transaction aborted by contention") ErrFailedPrecondition = errors.New("datastore: failed precondition") ErrInvalidArgument = errors.New("datastore: invalid argument") ErrPermissionDenied = errors.New("datastore: permission denied") ErrUnauthenticated = errors.New("datastore: unauthenticated") ErrDeadlineExceeded = errors.New("datastore: deadline exceeded") ErrResourceExhausted = errors.New("datastore: resource exhausted") ErrInternal = errors.New("datastore: internal server error") )
Sentinels for the canonical error codes Datastore returns. Match on these with errors.Is rather than on the HTTP status: ABORTED and ALREADY_EXISTS are both 409 and mean opposite things, one retryable and one terminal.
var ( ErrEmptyKeyPath = errors.New("datastore: key has no path elements") ErrAmbiguousID = errors.New("datastore: path element has both an id and a name") ErrIncompleteKey = errors.New("datastore: key is incomplete") )
Key errors.
var ( ErrNoKind = errors.New("datastore: query has no kind") ErrBadOperator = errors.New("datastore: unknown filter operator") )
Query errors.
var ( ErrEmptyValue = errors.New("datastore: value has no member set") ErrAmbiguousValue = errors.New("datastore: value has more than one member set") ErrBadValue = errors.New("datastore: malformed value on the wire") )
Codec failures. A Value carries exactly one member on the wire, because it is a proto3 oneof, so neither zero nor two is something this package can encode on the caller's behalf.
var ErrEmptyCondition = errors.New("datastore: And or Or with no conditions")
ErrEmptyCondition is returned for And or Or with no operands, which has no meaning on the wire.
var (
ErrEmptyIndex = errors.New("datastore: index has no kind or no properties")
)
Index errors.
var ErrTooManyKeys = errors.New("datastore: more than 1000 keys in one lookup")
ErrTooManyKeys is returned before a request the server would reject.
var ErrTxClosed = errors.New("datastore: transaction is no longer usable")
ErrTxClosed is returned by a Tx used after its closure returned.
Functions ¶
func MarshalIndexYAML ¶ added in v1.1.5
MarshalIndexYAML renders indexes in the index.yaml form that `gcloud datastore indexes create` consumes.
The output is sorted by kind and then by property list, so a tool that regenerates it produces a stable diff rather than a reordering.
It is written by hand rather than through a YAML library. The shape is four keys deep and closed, and this module has no external dependencies — acquiring one for this would cost more than the feature.
func UnmarshalEntity ¶ added in v1.1.5
UnmarshalEntity fills a struct from an Entity.
Properties with no matching field are ignored, and fields with no matching property are left alone: an entity of one kind need not carry the same properties as the next, so neither is an error.
A field tagged "__key__" receives the entity's key.
Types ¶
type Batch ¶
type Batch struct {
Entities []Entity
EndCursor Cursor
More MoreResults
// SkippedResults counts entities the offset stepped over. They were read
// and billed.
SkippedResults int32
}
Batch is one page of query results.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to one Datastore endpoint.
It is safe for concurrent use. Close releases pooled connections and the cached token; a client dropped without Close leaves native TLS handles alive until they idle out, which is the whole reason this repository exists.
func New ¶
New builds a client for one project.
Credentials resolve in this order: an explicit token source, explicit credentials, the emulator (which needs none), then GOOGLE_APPLICATION_CREDENTIALS.
func (*Client) AllocateIDs ¶
AllocateIDs completes incomplete keys, for referring to an entity before writing it.
func (*Client) Avg ¶ added in v1.1.6
func (c *Client) Avg(ctx context.Context, q *Query, property string, opts ...ReadOption) (Value, error)
Avg averages one property across the entities the query matches.
The result is a double, or null when nothing matched — which is why this returns a Value too: zero would be a different claim from "no data".
func (*Client) Close ¶
Close releases pooled connections and the signing key.
A client built with WithHTTPClient leaves that client alone, since its owner may still be using it.
func (*Client) CommitOverheadBytes ¶ added in v1.1.9
CommitOverheadBytes reports how many bytes a commit of n mutations spends on everything that is not the mutations themselves: the mode, the array that holds them, the comma between each pair, and the databaseId when this client has one.
MutationSize measures a mutation; this measures the request built around them, which is the part a caller summing MutationSize cannot see. Together they account for the whole body:
c.CommitOverheadBytes(len(ms)) + Σ c.MutationSize(m) == bytes sent to :commit
It takes a count rather than the mutations so that chunking stays a running total. A caller asks whether one more fits by adding that one's MutationSize and re-reading the overhead for n+1, instead of re-measuring the batch on every step.
A named database is counted twice over, and both are real: once inside each mutation, because every key carries the partition, and once here for the request-level databaseId.
This is the non-transactional envelope. A commit inside a transaction also carries a handle or a singleUseTransaction block, and only the transaction knows which; use Tx.CommitOverheadBytes there.
func (*Client) Count ¶
Count returns how many entities the query matches.
It exists because counting by paging through keys costs a read per entity, so leaving it out would push callers toward the expensive thing.
func (*Client) GetMulti ¶
func (c *Client) GetMulti(ctx context.Context, keys []Key, opts ...ReadOption) (*LookupResult, error)
GetMulti reads several entities.
It returns found, missing and deferred rather than failing, because the server answers a lookup by partitioning the keys. Deferred keys are handed back, not retried inside the call.
func (*Client) Insert ¶
Insert writes an entity, failing with ErrAlreadyExists if the key is taken.
An incomplete key is allowed here and only here; the allocated key comes back in the result.
func (*Client) Mutate ¶
Mutate applies several mutations in one commit.
Without a transaction this is NON_TRANSACTIONAL, where the server requires that no two mutations touch the same entity and does not promise all-or-none. Inside RunInTransaction it is atomic.
func (*Client) MutationSize ¶ added in v1.1.6
MutationSize reports how many bytes m contributes to a commit request.
It exists so a caller chunking a batch write against MaxRequestBytes does not have to marshal each entity itself and then hand it to this package to be marshalled again. Datastore publishes no per-commit mutation count, so size is the only bound there is to chunk against.
This is the encoded mutation, including the key with its project, database and namespace attached — which is why it is a method on Client rather than on Entity: only the client knows the partition, and an Entity-level figure would understate every mutation by exactly the part the caller cannot see.
func (*Client) Put ¶
Put writes an entity, creating or replacing it. It sends an upsert.
Update replaces the whole entity: there is no partial update on this wire and no server-side arithmetic, which is also why a replayed Put cannot double anything.
func (*Client) Run ¶
Run executes a query and returns one batch.
There is no iterator hiding round trips: feed EndCursor back through Query.Start to continue, the same shape the S3 and DynamoDB clients use.
func (*Client) RunInTransaction ¶
RunInTransaction runs fn inside a read-write transaction and commits what it queued.
The closure can run more than once. Datastore reports contention as ABORTED, and the right response is to re-run the whole closure, not to resend the commit: the reads it decided on are stale. So fn must have no side effects outside the transaction — that cannot be enforced, only stated.
This is also the only way to express a conditional write richer than the insert and update preconditions. The predicate runs in Go, between a read and a commit that share a snapshot, which is what makes it safe.
func (*Client) RunReadOnly ¶
RunReadOnly runs fn inside a read-only transaction, which gives several reads one consistent snapshot without taking write locks.
func (*Client) Sum ¶ added in v1.1.6
func (c *Client) Sum(ctx context.Context, q *Query, property string, opts ...ReadOption) (Value, error)
Sum totals one property across the entities the query matches.
The result is an integer when every summed value is an integer and a double otherwise, which is why this returns a Value rather than one Go type: flattening it would erase the same integer-versus-double distinction the rest of this package keeps. Values of other types are ignored by the service rather than failing the query.
It is here for the reason Count is, applied consistently. Counting by paging can be done keys-only; summing by paging cannot, because every entity has to come back in full for the caller to add one property up. So paging to sum is strictly more expensive than paging to count, and the argument that put Count in scope applies harder here. requirement:datastore-client-scope excluded these as "conveniences over data the caller can page" until a downstream reader pointed that out on 2026-08-04.
type CommitResult ¶
type CommitResult struct {
// Keys are the keys of the written entities, in mutation order. An insert
// with an incomplete key comes back completed here.
Keys []Key
// Versions are the post-commit versions, in the same order.
Versions []int64
// IndexUpdates counts the index entries the commit touched, which is what
// a write is billed on.
IndexUpdates int
CommitTime string
}
CommitResult reports what a commit did.
type Condition ¶ added in v1.1.6
type Condition interface {
// contains filtered or unexported methods
}
Condition is a filter tree. Build one with Prop, And and Or, and attach it with Query.Where.
Datastore composes with AND and OR; the OR arm arrived with disjunctive queries and this package believed otherwise until 2026-08-04, when a downstream reader asked whether the AND-only comment was still true. It was not.
func AncestorOf ¶ added in v1.1.6
Ancestor restricts results to descendants of key. It is a filter on the key path rather than on a property, which is why it reads as a condition rather than as a query option.
type Cursor ¶
type Cursor string
Cursor is an opaque position in a result set. It is fed back through Start, the same shape the S3 and DynamoDB clients use, rather than hidden inside an iterator that makes round trips the caller cannot see.
type Direction ¶ added in v1.1.5
type Direction string
Direction is the sort order of one indexed property.
type Entity ¶
type Entity struct {
Key *Key
Properties map[string]Value
Version int64
UpdateTime string
CreateTime string
}
Entity is a key and its properties. Datastore is schemaless, so two entities of one kind need not carry the same properties.
Version and UpdateTime come back from a read and feed the write preconditions in WithBaseVersion and WithUpdateTime. They are ignored on the way out.
func MarshalEntity ¶ added in v1.1.5
MarshalEntity converts a struct into an Entity.
Fields are named by their datastore tag, or by the field name when there is none. A tag of "-" skips the field, ",omitempty" skips it when it holds the zero value, and ",noindex" sets ExcludeFromIndexes.
A field tagged "__key__" carries the entity's key and must be a Key or *Key. Without one the returned Entity has no key, and the caller supplies it.
The supported types are string, the integer and float kinds, bool, []byte, time.Time, Key, Value, slices, structs, and pointers to any of those. A nil pointer becomes the null value, which is distinct from an absent property; use ",omitempty" when absent is what you meant.
Maps are deliberately unsupported. Datastore has no map type — a map would have to become an embedded entity, whose property names would then come from runtime data rather than from the struct, which is the one thing this mapping exists to avoid.
func (Entity) Get ¶
Get returns a property. The second result distinguishes an absent property from one explicitly set to null, which are different things to a filter.
func (Entity) MarshalJSON ¶
MarshalJSON emits the entity without a partition on its key; a Client fills that in on the way out.
func (Entity) Set ¶
Set stores a property, allocating the map on first use, and returns e so calls chain.
func (*Entity) UnmarshalJSON ¶
UnmarshalJSON reads an entity.
type Error ¶
Error is a failure reported by the service.
Status is the canonical code name, which is the only reliable discriminator: the HTTP code is ambiguous by itself.
type Index ¶ added in v1.1.5
type Index struct {
Kind string
// Ancestor reports whether the index serves ancestor queries.
Ancestor bool
Properties []IndexProperty
}
Index describes a composite index.
Single-property indexes are automatic and need none of this. A composite index is required when a query combines an equality filter with an inequality on a different property, orders on a property it also filters on inequality, or otherwise needs more than one property considered together. Without one, the query fails at runtime with FAILED_PRECONDITION on code that compiled cleanly, which is the failure this type exists to move earlier.
This is a description, not a request. Applying an index is an admin-API operation and requirement:datastore-client-scope excludes the admin API on purpose; the shape of an index, though, is a property of this service rather than of any one tool, so it belongs here instead of being reinvented by every generator and migration script.
idx := datastore.Index{
Kind: "Task",
Properties: []datastore.IndexProperty{
{Name: "done"},
{Name: "priority", Direction: datastore.Descending},
},
}
yaml, _ := datastore.MarshalIndexYAML([]datastore.Index{idx})
func (Index) Equal ¶ added in v1.1.5
Equal reports whether two indexes describe the same thing. Property order is significant; an empty direction equals Ascending.
type IndexProperty ¶ added in v1.1.5
type IndexProperty struct {
Name string
// Direction defaults to Ascending when empty.
Direction Direction
}
IndexProperty is one property of a composite index. Order matters: an index serves a query only if its properties appear in the order the query needs.
type Integer ¶
Integer is the set of Go integer types Int accepts.
Every member fits int64 on every platform, which is what makes Int total: it cannot be handed a value Datastore has no representation for.
~uint was removed on 2026-08-04. On a 64-bit platform uint holds values int64 does not, and Int converted through int64, so Int(uint(math.MaxUint64)) stored "-1" and reported no error — the one silent wrong write in a package that refuses out-of-range integer text, refuses widening a double to an integer, and refuses a uint64 in the struct mapper. Int returns no error, so the constraint is the only place this could be fixed without changing the signature.
A uint caller writes Int(int64(n)) when the value is known to fit, or IntString(strconv.FormatUint(n, 10)) when it is not — and the latter fails loudly at encode time if it really is too wide. 32-bit callers, where uint always fits, pay a conversion for that.
type Key ¶
type Key struct {
Namespace string
Path []PathElement
}
Key identifies an entity. The last path element is the entity itself; the ones before it are its ancestors, which is why ancestry is part of identity here rather than a property.
Namespace is a tenancy dimension with no DynamoDB equivalent. It is empty for the default namespace.
The project and database are not carried here. A Client adds them at encode time, so a Key stays portable inside a program.
func IncompleteKey ¶
IncompleteKey builds a one-element key for the server to complete.
func (Key) Child ¶
func (k Key) Child(e PathElement) Key
Child returns k with one more path element appended, making k its ancestor.
func (Key) Incomplete ¶
Incomplete reports whether the last element still needs an identifier.
func (Key) MarshalJSON ¶
MarshalJSON emits the key without a project. A Client fills the partition in on the way out, because only it knows which project and database the request is for; see (*Client).encodeKey.
func (Key) String ¶
String renders the key for logs and error messages. It is not a wire format and nothing parses it back.
func (*Key) UnmarshalJSON ¶
UnmarshalJSON reads a key, keeping only the namespace from the partition: the project and database are the client's, not the key's.
func (Key) Valid ¶
Valid reports whether k can be sent. An incomplete key is valid; only insert and AllocateIDs accept one, which is checked where it matters.
func (Key) WithNamespace ¶
WithNamespace returns k in the given namespace.
type Kind ¶
type Kind int
Kind identifies which member of a Value is set.
type LookupResult ¶
type LookupResult struct {
Found []Entity
// Missing are keys with no entity.
Missing []Key
// Deferred are keys the server did not read this time. They are handed back
// rather than retried inside the call, because that is the caller's
// decision about which reads still matter.
Deferred []Key
}
LookupResult is what a batch read returns. All three lists matter: the server answers a lookup by partitioning the keys rather than by failing, and a caller batching a thousand keys needs to know which came back.
func (*LookupResult) HasDeferred ¶
func (r *LookupResult) HasDeferred() bool
HasDeferred reports whether the server left keys unread.
type MoreResults ¶
type MoreResults string
MoreResults says why a batch ended.
const ( NotFinished MoreResults = "NOT_FINISHED" MoreResultsAfterLimit MoreResults = "MORE_RESULTS_AFTER_LIMIT" MoreResultsAfterCursor MoreResults = "MORE_RESULTS_AFTER_CURSOR" NoMoreResults MoreResults = "NO_MORE_RESULTS" )
The batch termination reasons.
type Mutation ¶
type Mutation struct {
// contains filtered or unexported fields
}
Mutation is one change in a commit. Datastore carries the verb in the request rather than in the endpoint, so several verbs fit in one round trip.
func DeleteOp ¶
DeleteOp removes an entity. Deleting an absent key succeeds, which is what makes it replay-safe.
func InsertOp ¶
InsertOp fails if the key already exists. This is put-if-absent, and it is the closest thing on this wire to a condition expression.
func (Mutation) With ¶
func (m Mutation) With(opts ...WriteOption) Mutation
With applies write options, so a Mutation inside Mutate can carry the same preconditions a single-entity call takes as arguments.
type Operator ¶
type Operator string
Operator is a property filter comparison.
const ( LessThan Operator = "LESS_THAN" LessThanOrEqual Operator = "LESS_THAN_OR_EQUAL" GreaterThan Operator = "GREATER_THAN" GreaterThanEqual Operator = "GREATER_THAN_OR_EQUAL" Equal Operator = "EQUAL" NotEqual Operator = "NOT_EQUAL" HasAncestor Operator = "HAS_ANCESTOR" In Operator = "IN" NotIn Operator = "NOT_IN" )
The property filter operators. Composition is in condition.go: Datastore supports AND and OR.
type Option ¶
type Option func(*config)
Option configures a Client.
func WithCredentials ¶
func WithCredentials(creds google.Credentials) Option
WithCredentials supplies a service account key directly.
func WithDatabase ¶
WithDatabase selects a named database. Empty means the project's default.
func WithEndpoint ¶
WithEndpoint overrides the service endpoint, which is how a client is pointed at the emulator. A value with no scheme is taken as http, matching what DATASTORE_EMULATOR_HOST carries.
func WithHTTPClient ¶
WithHTTPClient supplies the HTTP client. Close leaves such a client alone, since its owner may still be using it.
func WithMaxIdleConns ¶
WithMaxIdleConns sets how many idle connections are kept.
Every request goes to one host, so the per-host cap is the whole pool for this client. Set it to the concurrency the application runs.
func WithNamespace ¶
WithNamespace sets the default namespace for keys that carry none.
func WithRetry ¶
WithRetry configures the retry budget. Zero attempts disables retrying.
Retries multiply: the TinyGo transport replays a request once below this client when a pooled connection dies before any response byte arrives, so the worst case is attempts x 2 deliveries. The mutation verbs are idempotent by construction, so that is harmless for them; a commit carrying a transaction handle fails on the second delivery rather than writing twice.
func WithTimeout ¶
WithTimeout bounds one request including reading the response body. The default is 10s, matching the DynamoDB client: these are small round trips.
func WithTokenSource ¶
func WithTokenSource(ts google.TokenSource) Option
WithTokenSource supplies bearer tokens from somewhere else: a metadata server, a companion process, or a test.
A client built this way never signs anything, so on a TinyGo build it links no RSA code at all.
type PathElement ¶
PathElement is one step of a key path: a kind, plus either a numeric id or a string name. Neither set means an incomplete key, which the server completes on insert.
func (PathElement) Incomplete ¶
func (p PathElement) Incomplete() bool
Incomplete reports whether the server still has to allocate an identifier.
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query selects entities of one kind. It is a value worth building once and running repeatedly, which is why it is a type rather than a pile of options.
Every method returns a new Query, so a partially built query can be shared without one caller's additions reaching another.
func (*Query) Ancestor ¶
Ancestor restricts the query to descendants of key, which is a filter on the key path rather than on a property.
func (*Query) DistinctOn ¶
DistinctOn collapses results that share the named properties.
func (*Query) Filter ¶
Filter adds a property comparison. Repeated calls combine with AND.
It is sugar over Where(Prop(...)) and is kept because most filters are one comparison ANDed with the rest; reach for Where when a query needs OR.
func (*Query) KeysOnly ¶
KeysOnly returns keys without properties, which is the cheapest read there is. It is a projection on the special __key__ property.
func (*Query) Offset ¶
Offset skips results. The skipped entities are still read and still billed, so a cursor is the cheaper way to resume.
func (*Query) Project ¶
Project returns only the named properties. A projection query reads from an index, so every projected property must be indexed.
func (*Query) Where ¶ added in v1.1.6
Where adds a condition tree. Repeated calls, and a Where alongside a Filter, combine with AND — so Or belongs inside one call, not across two.
q.Where(datastore.Or(
datastore.Prop("state", datastore.Equal, datastore.String("new")),
datastore.Prop("priority", datastore.GreaterThanEqual, datastore.Int(8)),
))
type ReadOption ¶
type ReadOption interface {
// contains filtered or unexported methods
}
ReadOption configures a read.
func WithEventualConsistency ¶
func WithEventualConsistency() ReadOption
WithEventualConsistency asks for a possibly stale read.
The default is strong, which on the Firestore backend applies to non-ancestor queries too. This exists for the cases where a stale answer is cheaper and good enough, not because staleness is ever the safer default.
func WithReadTime ¶
func WithReadTime(at time.Time) ReadOption
WithReadTime reads the database as of a past instant.
Two windows, and which one you are in changes what a legal instant is:
- Within the past hour, any microsecond-granularity instant, whether or not point-in-time recovery is enabled on the database.
- From one hour to seven days back, whole-minute timestamps only, and only with point-in-time recovery enabled.
Neither window reaches before the database's earliestVersionTime, which on a young database can be later than both.
A read older than an hour must therefore be truncated by the caller:
at := time.Now().Add(-2 * time.Hour).Truncate(time.Minute)
Without that, the service refuses the read as "read_time is too old", which names the age when the precision is what was wrong. This does not truncate for you, because truncating would change the instant you asked for, and the boundary between the two windows moves while the request is in flight.
None of this is checked here. The client cannot see whether PITR is enabled or what earliestVersionTime is, so a local range check would refuse reads that work.
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
Tx accumulates reads and mutations inside one transaction.
Mutations are queued and sent with the commit, so a closure that returns an error writes nothing and needs no rollback on the ordinary path.
The transaction starts lazily. handle is empty until a read or the commit starts it, which is what removes the separate beginTransaction round trip: the first read asks to start one and the reply carries the handle, and a closure that never reads folds its transaction into the commit instead. A Tx is used by one goroutine, the closure's, so this needs no lock.
func (*Tx) CommitOverheadBytes ¶ added in v1.1.9
CommitOverheadBytes reports what this transaction's commit will spend on everything that is not the mutations, the same figure as Client.CommitOverheadBytes but for the request this Tx actually sends. Chunk against MaxTransactionBytes with it.
A transaction that has read carries the handle its first read brought back; a transaction that has not carries a singleUseTransaction block instead, and the two are different sizes. So the answer describes the transaction as it stands: asked before the first read it describes the single-use shape, and reading changes it, because reading is what changes the commit. Asked where a caller decides whether one more mutation fits, which is after whatever reads the closure does, it is exact.
type TxOption ¶
type TxOption func(*txConfig)
TxOption configures a transaction.
func WithTxRetries ¶
WithTxRetries caps how many times the closure re-runs on ABORTED. The default is 3.
type Value ¶
type Value struct {
Null bool
Bool *bool
Integer *string
Double *float64
Timestamp *time.Time
Key *Key
String *string
Blob []byte
GeoPoint *LatLng
Entity *Entity
Array []Value
// ExcludeFromIndexes is not part of the union. It rides alongside whichever
// member is set.
ExcludeFromIndexes bool
}
Value is one property in its wire form. Exactly one member is set; a slice member counts as set when it is non-nil, so an empty array and an absent one are different values.
Integer is text because proto3 JSON encodes int64 as a string, which is also what keeps a 64-bit id from passing through float64 on the way in. Double is a real JSON number: Datastore stores the two as different types, and collapsing them would change sort order and equality filters.
func Array ¶
Array builds a list value. An array of zero values is legal and is distinct from an absent property.
func IntString ¶
IntString builds an integer value from its decimal text, for a uint64 beyond int64 or to avoid a conversion the caller does not want.
func Nested ¶
Nested builds a value holding an embedded entity.
An embedded entity has no key. One that carries a key is rejected at encode time rather than silently stripped.
func Null ¶
func Null() Value
Null builds the null value, which is distinct from an absent property.
func Time ¶
Time builds a timestamp value.
Datastore stores microseconds, so a value with finer resolution loses it on the round trip. That truncation happens on the server and is not hidden here.
func Unindexed ¶
Unindexed returns v with ExcludeFromIndexes set. It composes with every constructor, because indexing is not one of the union members.
func (Value) AsFloat ¶
AsFloat returns the double value.
It does not accept an integer value. The two are distinct types to Datastore, and quietly widening one to the other is how a filter stops matching.
func (Value) AsGeoPoint ¶
AsGeoPoint returns the geographical point value.
func (Value) IsNull ¶
IsNull reports whether this is the null value, which is not the same as an absent property.
func (Value) MarshalJSON ¶
MarshalJSON emits exactly one union member, plus excludeFromIndexes when set.
func (*Value) UnmarshalJSON ¶
UnmarshalJSON reads exactly one union member.
It decodes to a member map first rather than to a struct of pointers. A struct cannot see nullValue at all: encoding/json resolves a JSON null by setting the pointer field to nil, so the one member whose value is literally null becomes indistinguishable from an absent one. Counting keys also makes an unknown member an error instead of silently nothing.
type WriteOption ¶
type WriteOption interface {
// contains filtered or unexported methods
}
WriteOption configures a write.
func WithBaseVersion ¶
func WithBaseVersion(version int64) WriteOption
WithBaseVersion applies the write only if the stored entity is still at this version, which comes from a previous read.
This is optimistic concurrency, and that is the honest name for it. It is strictly stronger than a client-side check, because it also catches a concurrent write the caller never read.
func WithUpdateTime ¶
func WithUpdateTime(at string) WriteOption
WithUpdateTime is WithBaseVersion keyed on a timestamp instead, taken from Entity.UpdateTime.