Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)



Entities, Properties, and Keys

Data objects in the Datastore are known as entities . An entity has one or more named properties , each of which can have one or more values. Entities of the same kind need not have the same properties, and an entity's values for a given property need not all be of the same data type. (If necessary, an application can establish and enforce such restrictions in its own data model.)

The Datastore supports a variety of data types for property values . These include, among others:

  • Integers
  • Floating-point numbers
  • Strings
  • Dates
  • Binary data

Each entity in the Datastore has a key that uniquely identifies it. The key consists of the following components:

  • The namespace of the entity, which allows for multitenancy
  • The kind of the entity, which categorizes it for the purpose of Datastore queries
  • An identifier for the individual entity, which can be either
    • a key name string
    • an integer numeric ID
  • An optional ancestor path locating the entity within the Datastore hierarchy

An application has access only to entities it has created itself; it can't access data belonging to other applications. It can fetch an individual entity from the Datastore using the entity's key, or it can retrieve one or more entities by issuing a query based on the entities' keys or property values.

The Google Cloud Datastore API provides methods for creating, modifying and retrieving entities from the Datastore. Entities themselves are represented in API calls in JSON or protocol buffer messages , depending on whether you are using the JSON API or protocol buffer API .

The Datastore itself does not enforce any restrictions on the structure of entities, such as whether a given property has a value of a particular type; this task is left to the application.

Contents

  1. Kinds and identifiers
  2. Ancestor paths
  3. Transactions and entity groups
  4. Properties and value types
  5. Working with entities
    1. Creating an entity
    2. Retrieving an entity
    3. Updating an entity
    4. Deleting an entity
    5. Batch operations
  6. Understanding write costs

Kinds and identifiers

Each Datastore entity is of a particular kind, which categorizes the entity for the purpose of queries: for instance, a human resources application might represent each employee at a company with an entity of kind Employee . An entity's kind is the kind specified in the last element of its key's path array ( JSON ) or repeated pathElement field ( protocol buffers ). All kind names that begin with two underscores ( __ ) are reserved and may not be used.

The following example creates an entity of kind Employee , populates its property values, and saves it to the Datastore:

Node.js (JSON)

var entity = {
  key: { path: [{ kind: 'Employee' }] },
  properties: {
    firstName: { stringValue: 'Antonio' },
    lastName: { stringValue: 'Salieri' },
    hireDate: { dateTimeValue: '2013-05-14T13:01:00.234Z' },
    attendedHrTraining: { booleanValue: true }
  }
};
datastore.commit({
  mutation: { insertAutoId: [entity] },
  mode: 'NON_TRANSACTIONAL'
}).execute(callback);

Python (Protocol Buffers)

req = datastore.CommitRequest()
req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
employee = req.mutation.insert_auto_id.add()

path_element = employee.key.path_element.add()
path_element.kind = 'Employee'

first_name_property = employee.property.add()
first_name_property.name = 'first_name'
first_name_property.value.string_value = 'Antonio'

last_name_property = employee.property.add()
last_name_property.name = 'last_name'
last_name_property.value.string_value = 'Salieri'

hire_date_property = employee.property.add()
hire_date_property.name = 'hire_date'
hire_date_property.value.timestamp_microseconds_value = long(
    time.time() * 1e6)

attended_hr_training_property = employee.property.add()
attended_hr_training_property.name = 'attended_hr_training'
attended_hr_training_property.value.boolean_value = True

resp = self.datastore.commit(req)

Java (Protocol Buffers)

// import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;

Entity.Builder employee = Entity.newBuilder()
    .setKey(makeKey("Employee"))
    .addProperty(makeProperty("firstName", makeValue("Antonio")))
    .addProperty(makeProperty("lastName", makeValue("Salieri")))
    .addProperty(makeProperty("hireDate", makeValue(new Date())))
    .addProperty(makeProperty("attendedHrTraining", makeValue(true)));

CommitRequest commitRequest = CommitRequest.newBuilder()
    .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
    .setMutation(Mutation.newBuilder().addInsertAutoId(employee))
    .build();
CommitResponse response = datastore.commit(commitRequest);

In addition to a kind, each entity has an identifier , assigned when the entity is created. Because it is part of the entity's key, the identifier is associated permanently with the entity and cannot be changed. It can be assigned in either of two ways:

  • Your application can specify its own key name string for the entity.
  • You can have the Datastore automatically assign the entity an integer numeric ID .

To assign an entity a key name, specify it in the name field of the last element of its key's path array ( JSON ) or repeated pathElement field ( protocol buffers ):

Node.js (JSON)

var entity = {
  key: { path: [{ kind: 'Employee', name: 'asalieri' }] }
  // ... some properties ...
};

Python (Protocol Buffers)

employee = datastore.Entity()

path_element = employee.key.path_element.add()
path_element.kind = 'Employee'
path_element.name = 'asalieri'

Java (Protocol Buffers)

// import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;

Entity employee = Entity.newBuilder().setKey(makeKey("Employee", "asalieri")).build();

To have the Datastore assign a numeric ID automatically, omit both the name and id fields and place the entity in the insertAutoId (JSON) or insert_auto_id (protocol buffers) field of the mutation:

Node.js (JSON)

var entity = {
  key: { path: [{ kind: 'Employee' }] }
  // ... some properties ...
};

Python (Protocol Buffers)

employee = datastore.Entity()

path_element = employee.key.path_element.add()
path_element.kind = 'Employee'

Java (Protocol Buffers)

// import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;

Key key = makeKey("Employee").build();

Assigning identifiers

You can use Datastore's automatic ID generation to generate IDs for your entities, or you can create IDs manually.

Datastore generates a random sequence of IDs that are approximately uniformly distributed. Each ID can be up to 16 decimal digits long.

Ancestor paths

Entities in the Datastore form a hierarchically structured space similar to the directory structure of a file system. When you create an entity, you can optionally designate another entity as its parent; the new entity is a child of the parent entity (note that unlike in a file system, the parent entity need not actually exist). An entity without a parent is a root entity. The association between an entity and its parent is permanent, and cannot be changed once the entity is created. The Datastore will never assign the same numeric ID to two entities with the same parent, or to two root entities (those without a parent).

An entity's parent, parent's parent, and so on recursively, are its ancestors; its children, children's children, and so on, are its descendants. An entity and its descendants are said to belong to the same entity group. The sequence of entities beginning with a root entity and proceeding from parent to child, leading to a given entity, constitute that entity's ancestor path. The complete key identifying the entity consists of a sequence of kind-identifier pairs specifying its ancestor path and terminating with those of the entity itself:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

For a root entity, the ancestor path is empty and the key consists solely of the entity's own kind and identifier:

[Person:GreatGrandpa]

In the Datastore API, an entity's parent(s) are specified by simply prefixing elements to its path array ( JSON ) or repeated pathElement field ( protocol buffers ):

Node.js (JSON)

var greatGrandpa = { path: [{ kind: 'Person', name: 'GreatGrandpa' }] };
var grandpa = {
                     path: [{ kind: 'Person', name: 'GreatGrandpa' },
                            { kind: 'Person', name: 'Grandpa' }] };
var me = {
                     path: [{ kind: 'Person', name: 'GreatGrandpa' },
                            { kind: 'Person', name: 'Grandpa' },
                            { kind: 'Person', name: 'Dad' },
                            { kind: 'Person', name: 'Me' }] };

Python (Protocol Buffers)

great_grandpa = datastore.Entity()

path_element = great_grandpa.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'GreatGrandpa'

grandpa = datastore.Entity()

path_element = grandpa.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'GreatGrandpa'

path_element = grandpa.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'Grandpa'

me = datastore.Entity()

path_element = me.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'GreatGrandpa'

path_element = me.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'Grandpa'

path_element = me.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'Dad'

path_element = me.key.path_element.add()
path_element.kind = 'Person'
path_element.name = 'Me'

Java (Protocol Buffers)

// import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;

Key greatGrandpa = makeKey("Person", "GreatGrandpa").build();
Key grandpa = makeKey(greatGrandpa, "Person", "Grandpa").build();
Key me = makeKey(grandpa, "Person", "Dad", "Person", "Me").build();

Transactions and entity groups

Every attempt to create, update, or delete an entity takes place in the context of a transaction . A single transaction can include any number of such operations. To maintain the consistency of the data, the transaction ensures that all of the operations it contains are applied to the Datastore as a unit or, if any of the operations fails, that none of them are applied.

A single transaction can apply to multiple entities, so long as the entities belong to a limited number (5) of entity groups. You need to take this limitation into account when designing your data model: the simplest approach is to determine which entities you need to be able to process in the same transaction. Then, when you create those entities, place them in the same entity group by declaring them with a common ancestor. They will then all be in the same entity group and you will always be able to update and read them transactionally.

Properties and value types

The data values associated with an entity consist of one or more properties. Each property has a name and one or more values. A property can have values of more than one type, and two entities can have values of different types for the same property. Properties can be indexed or unindexed (queries that order or filter on a property P will ignore entities where P is unindexed).

Some example properties:

Node.js (JSON)

var properties = {
  singleValued: { stringValue: 'Antonio' },
  multiValued: { listValue: [{ integerValue: 42 }, { integerValue: 54 }] },
  unindexed: { stringValue: 'VeryVeryLong', indexed: false },
};

Python (Protocol Buffers)

# A plain vanilla property.
single_valued_property = datastore.Property()
single_valued_property.name = 'single_valued'
single_valued_property.value.string_value = 'Antonio'

# A list property with value [42, 54].
multi_valued_property = datastore.Property()
multi_valued_property.name = 'multi_valued'
multi_valued_property.value.list_value.add().integer_value = 42
multi_valued_property.value.list_value.add().integer_value = 54

# An unindexed string property.
unindexed_property = datastore.Property()
unindexed_property.name = 'unindexed'
unindexed_value = unindexed_property.value
unindexed_value.indexed = False
unindexed_value.string_value = 'VeryVeryLong'

Java (Protocol Buffers)

// import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;

Entity.Builder entity = Entity.newBuilder()
    .addProperty(makeProperty("singleValued", makeValue("Antonio")))
    .addProperty(makeProperty("multiValued", makeValue(makeValue(42), makeValue(52))))
    .addProperty(makeProperty("unindexed", makeValue("VeryVeryLong").setIndexed(false)));

In JSON, properties are named fields of an object, whose value is an object with a value type described in the table below. A property can be assigned multiple values by setting the listValue field to an array of values. For a property to be unindexed, the indexed field of the property's value object must be set to false .

In protocol buffers, properties are specified by the Property message , which specifies the property name and value. The Value message should have a field set according to the format described in the supported value types table below. A property can be assigned multiple values by populating the list_value field with multiple Value objects. For a property to be unindexed, the indexed field of Value must be set to false .

The following value types are supported:

Value type JSON field name and type(s) Protocol buffer field name and type Sort order Notes
Integer integerValue : number or string integer_value : int64 Numeric JSON: Use strings for integers that cannot be exactly represented as numbers
Floating-point number doubleValue : number double_value : double Numeric 64-bit double precision,
IEEE 754
Boolean booleanValue : true or false integer_value : bool false < true
Text string stringValue : string string_value : string Unicode Up to 500 Unicode characters if property is indexed, up to 1MB otherwise
Byte string blobValue : string blob_value : bytes Byte order Up to 500 bytes if property is indexed, up to 1MB otherwise
Date and time dateTimeValue : string (RFC 3339 formatted, with milliseconds, for instance 2013-05-14T00:01:00.234Z ) timestamp_microseconds_value : int64 (since Jan 1 1970) Chronological
Datastore key keyValue : a JSON Datastore key key_value : a Key message By path elements
(kind, identifier,
kind, identifier...)

Embedded entity entityValue : a JSON entity entity_value : an Entity message None Not indexed
List listValue : an array of JSON Value objects list_value : one or more Value messages None
Null None None None

When a query involves a property with values of mixed types, the Datastore uses a deterministic ordering based on the internal representations:

  1. Null values
  2. Fixed-point numbers
  3. Boolean values
  4. Byte strings (short)
  5. Unicode strings
  • Floating-point numbers
  • Datastore keys
  • Working with entities

    Applications can use the Datastore API to create, retrieve, update, and delete entities. If the application knows the complete key for an entity (or can derive it from its parent key, kind, and identifier), it can use the key to operate directly on the entity. An application can also obtain an entity's key as a result of a Datastore query; see the Datastore Queries page for more information.

    Creating an entity

    You create a new entity by creating an entity object (JSON) or Entity message (protocol buffers) and calling the commit() method ( JSON and protocol buffers ), placing the entity to create in the insert field of the mutation:

    Node.js (JSON)

    var entity = {
      key: { path: [{ kind: 'Employee', name: 'asalieri' }] }
      // ... some properties ...
    };
    datastore.commit({
      // Request insertion with complete key specified
      mutation: { insert: [entity] },
      mode: 'NON_TRANSACTIONAL'
    }).execute(callback);
    

    Python (Protocol Buffers)

    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    employee = req.mutation.insert.add()
    
    # Request insertion with complete key specified
    path_element = employee.key.path_element.add()
    path_element.kind = 'Employee'
    path_element.name = 'asalieri'
    
    # ... set properties ...
    
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;
    
    Entity employee = Entity.newBuilder().setKey(makeKey("Employee", "asalieri")).build();
    // ... set properties ...
    
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder().addInsert(employee))
        .build();
    datastore.commit(request);
    

    If you don't provide a key name, the Datastore will automatically generate a numeric ID for the entity's key, as long as you place the entity to create in the insertAutoId (JSON) or insert_auto_id (protocol buffers) field of the mutation:

    Node.js (JSON)

    var entity = {
      key: { path: [{ kind: 'Employee' }] }
      // ... some properties ...
    };
    datastore.commit({
      // Request insertion with automatic ID allocation
      mutation: { insertAutoId: [entity] },
      mode: 'NON_TRANSACTIONAL'
    }).execute(function(err, result) {
      if (!err) {
        // The result contains the key with the generated ID
        entity.key = result.mutationResult.insertAutoIdKeys[0];
        // ...
      }
    });
    

    Python (Protocol Buffers)

    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    employee = req.mutation.insert_auto_id.add()
    
    # Request insertion with automatic ID allocation
    path_element = employee.key.path_element.add()
    path_element.kind = 'Employee'
    
    # ... set properties ...
    
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;
    
    Entity employee = Entity.newBuilder().setKey(makeKey("Employee")).build();
    // ... set properties ...
    
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder().addInsertAutoId(employee))
        .build();
    CommitResponse response = datastore.commit(request);
    

    Retrieving an entity

    To retrieve an entity identified by a given key, pass the key to the lookup() method ( JSON and protocol buffers ):

    Node.js (JSON)

    var key = { path: [{ kind: 'Employee', name: 'asalieri' }] };
    datastore.lookup({
      keys: [key]
    }).execute(function(err, result) {
      if (!err) {
        var entity = null;
        if (result.found) {
          entity = result.found[0].entity;
        }
        // ... process entity ...
      }
    });
    

    Python (Protocol Buffers)

    # employee_key = ...
    req = datastore.LookupRequest()
    req.key.extend([employee_key])
    
    resp = self.datastore.lookup(req)
    if len(resp.missing) is 1:
      raise Exception('entity not found')
    employee = resp.found[0].entity
    

    Java (Protocol Buffers)

    // Key employeeKey = ...;
    LookupRequest request = LookupRequest.newBuilder().addKey(employeeKey).build();
    LookupResponse response = datastore.lookup(request);
    if (response.getMissingCount() == 1) {
      throw new RuntimeException("entity not found");
    }
    Entity employee = response.getFound(0).getEntity();
    

    Updating an entity

    To update an existing entity, modify the attributes of the object (JSON) or create a new entity message with the same key and appropriate properties (protocol buffers). Then call the commit() method, placing the entity to update in the update field of the mutation:

    Node.js (JSON)

    // var employeeKey = ...;
    // var newNickname = ...;
    datastore.lookup({
      keys: [employeeKey]
    }).execute(function(err, result) {
      if (!err && result.found) {
        var entity = result.found[0].entity;
        entity.nickname = { stringValue: newNickname };
        datastore.commit({
          mutation: { update: [entity] },
          mode: 'NON_TRANSACTIONAL'
        }).execute(callback);
      } else {
        callback('Lookup failed: ' + err, null);
      }
    });
    

    Python (Protocol Buffers)

    # employee_key = ...
    # new_nickname = ...
    req = datastore.LookupRequest()
    req.key.extend([employee_key])
    
    resp = self.datastore.lookup(req)
    employee = resp.found[0].entity
    
    nickname_property = datastore.Property()
    nickname_property.name = 'nickname'
    nickname_property.value.string_value = new_nickname
    
    found = False
    for prop in employee.property:
      if prop.name is 'nickname':
        del prop.value
        prop.value.string_value = nickname_property
        found = True
    if not found:
      employee.property.extend([nickname_property])
    
    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    req.mutation.update.extend([employee])
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;
    
    // Key employeeKey = ...;
    // String newNickname = ...;
    LookupRequest lookup = LookupRequest.newBuilder().addKey(employeeKey).build();
    LookupResponse response = datastore.lookup(lookup);
    // Error handling ...
    Entity employee = response.getFound(0).getEntity();
    Entity.Builder updatedEmployee = Entity.newBuilder(employee);
    updatedEmployee.clearProperty();
    for (Property prop : employee.getPropertyList()) {
      if (prop.getName().equals("nickname")) {
        updatedEmployee.addProperty(makeProperty("nickname", makeValue(newNickname)));
      } else {
        updatedEmployee.addProperty(prop);
      }
    }
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder().addUpdate(updatedEmployee))
        .build();
    datastore.commit(request);
    

    The object data overwrites the existing entity. The entire object is sent to the Datastore. If the entity does not exist, the update will fail. If you want to update-or-create an entity, use the upsert field of the mutation instead:

    Node.js (JSON)

    // var employeeKey = ...;
    // var firstName = ...;
    // var lastName = ...;
    var entity = {
      key: employeeKey,
      properties: {
        firstName: { stringValue: firstName },
        lastName: { stringValue: lastName }
      }
    };
    datastore.commit({
      mutation: { upsert: [entity] },
      mode: 'NON_TRANSACTIONAL'
    }).execute(callback);
    

    Python (Protocol Buffers)

    # employee_key = ...
    # first_name = ...
    # last_name = ...
    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    employee = req.mutation.upsert.add()
    employee.key.CopyFrom(employee_key)
    
    first_name_property = employee.property.add()
    first_name_property.name = 'first_name'
    first_name_property.value.string_value = first_name
    
    last_name_property = employee.property.add()
    last_name_property.name = 'last_name'
    last_name_property.value.string_value = last_name
    
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;
    
    // Key employeeKey = ...;
    // String firstName = ...;
    // String lastName = ...;
    Entity.Builder employee = Entity.newBuilder()
        .setKey(employeeKey)
        .addProperty(makeProperty("firstName", makeValue(firstName)))
        .addProperty(makeProperty("lastName", makeValue(lastName)));
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder().addUpsert(employee))
        .build();
    datastore.commit(request);
    

    Deleting an entity

    Given an entity's key, you can delete the entity by calling the commit method, placing the key of the entity to delete in the delete field of the mutation:

    Node.js (JSON)

    // var employeeKey = ...;
    datastore.commit({
      mutation: { delete: [employeeKey] },
      mode: 'NON_TRANSACTIONAL'
    }).execute(callback);
    

    Python (Protocol Buffers)

    # employee_key = ...
    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    req.mutation.delete.extend([employee_key])
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // Key employeeKey = ...;
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder().addDelete(employeeKey))
        .build();
    datastore.commit(request);
    

    Batch operations

    As you saw in the examples above, the fields of the mutation passed to commit() are actually lists. You can perform batch operations by simply placing multiple entities or keys in the fields of the mutation, and mix insertions, deletions, and modifications in a single batch operation:

    Node.js (JSON)

    var leftYesterday = { path: [{ kind: 'Employee', name: 'asalieri' }] };
    var newEmployee1 = { key: { path: [{ kind: 'Employee' }] } };
    var newEmployee2 = { key: { path: [{ kind: 'Employee' }] } };
    var newEmployee3 = { key: { path: [{ kind: 'Employee' }] } };
    // ...
    datastore.commit({
      mutation: {
        insertAutoId: [newEmployee1, newEmployee2, newEmployee3],
        delete: [leftYesterday]
      },
      mode: 'NON_TRANSACTIONAL'
    }).execute(callback);
    

    Python (Protocol Buffers)

    req = datastore.CommitRequest()
    req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
    
    left_yesterday = req.mutation.delete.add()
    path_element = left_yesterday.path_element.add()
    path_element.kind = 'Employee'
    path_element.name = 'asalieri'
    
    employee1 = req.mutation.insert_auto_id.add()
    employee1.key.path_element.add().kind = 'Employee'
    
    employee2 = req.mutation.insert_auto_id.add()
    employee2.key.path_element.add().kind = 'Employee'
    
    employee3 = req.mutation.insert_auto_id.add()
    employee3.key.path_element.add().kind = 'Employee'
    
    self.datastore.commit(req)
    

    Java (Protocol Buffers)

    // import static com.google.apphosting.client.datastoreservice.client.DatastoreHelper.*;
    // ...
    Entity employee1 = Entity.newBuilder().setKey(makeKey("Employee")).build();
    Entity employee2 = Entity.newBuilder().setKey(makeKey("Employee")).build();
    Entity employee3 = Entity.newBuilder().setKey(makeKey("Employee")).build();
    Key.Builder leftYesterday = makeKey("Employee", "asalieri");
    // ...
    
    CommitRequest request = CommitRequest.newBuilder()
        .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
        .setMutation(Mutation.newBuilder()
            .addInsertAutoId(employee1)
            .addInsertAutoId(employee2)
            .addInsertAutoId(employee3)
            .addDelete(leftYesterday))
        .build();
    datastore.commit(request);
    

    Performing operations in batches does not affect the cost, regardless of the entity's size. A batch operation for two keys costs two reads, even if one of the keys did not exist. For example, it is more economical to do a keys-only query that retrieves 1000 keys, and then do a fetch on 500 of them, than to do a regular (not keys-only) query for all 1000 directly.

    Understanding write costs

    When your application executes a Datastore commit() operation, the Datastore must perform a number of writes to store the entity. Your account is charged for each of these writes. You can see how many writes will be required to store an entity by looking at the data viewer in the SDK Development Console. This section explains how these write costs are calculated.

    Every entity requires a minimum of two writes to store: one for the entity itself and another for the built-in EntitiesByKind index, which is used by the query planner to service a variety of queries. In addition, the Datastore maintains two other built-in indexes, EntitiesByProperty and EntitiesByPropertyDesc , which provide efficient scans of entities by single property values in ascending and descending order, respectively. Each of an entity's indexed property values must be written to each of these indexes.

    As an example, consider an entity with properties A , B , and C :

    Key: 'Foo:1' (kind = 'Foo', id = 1, no parent)
    A: 1, 2
    B: null
    C: 'this', 'that', 'theOther'
    

    Assuming there are no composite indexes (see below) for entities of this kind, this entity requires 14 writes to store:

    Composite indexes (those referring to multiple properties) require additional writes to maintain. Suppose you define the following composite index:

    Kind: 'Foo'
    A ▲, B ▼
    

    where the triangles indicate the sort order for the specified properties: ascending for property A and descending for property B. Storing the entity defined above now takes an additional write to the composite index for every combination of A and B values:

    ( 1 , null ) ( 2 , null )

    This adds 2 writes for the composite index, for a total of 1 + 1 + 4 + 2 + 6 + 2 = 16. Now add property C to the index:

    Kind: 'Foo'
    A ▲, B ▼, C ▼
    

    Storing the same entity now requires a write to the composite index for each possible combination of A , B , and C values:

    ( 1 , null , 'this' ) ( 1 , null , 'that' ) ( 1 , null , 'theOther' )

    ( 2 , null , 'this' ) ( 2 , null , 'that' ) ( 2 , null , 'theOther' )

    This brings the total number of writes to 1 + 1 + 4 + 2 + 6 + 6 = 20.

    If a Datastore entity contains many multiple-valued properties, or if a single such property is referenced many times, the number of writes required to maintain the index can explode combinatorially. Such exploding indexes can be very expensive to maintain. For example, consider a composite index that includes ancestors:

    Kind: 'Foo'
    A ▲, B ▼, C ▼
    Ancestor: True
    

    Storing a simple entity with this index present takes the same number of writes as before. However, if the entity has ancestors, it requires a write for each possible combination of property values and ancestors , in addition to those for the entity itself. Thus an entity defined as

    Key: 'GreatGrandpa:1/Grandpa:1/Dad:1/Foo:1' (kind = 'Foo', id = 1, parent = 'GreatGrandpa:1/Grandpa:1/Dad:1')
    A: 1, 2
    B: null
    C: 'this', 'that', 'theOther'
    

    would require a write to the composite index for each of the following combinations of properties and ancestors:

    ( 1 , null , 'this' , 'GreatGrandpa' ) ( 1 , null , 'this' , 'Grandpa' ) ( 1 , null , 'this' , 'Dad' ) ( 1 , null , 'this' , 'Foo' )

    ( 1 , null , 'that' , 'GreatGrandpa' ) ( 1 , null , 'that' , 'Grandpa' ) ( 1 , null , 'that' , 'Dad' ) ( 1 , null , 'that' , 'Foo' )

    ( 1 , null , 'theOther' , 'GreatGrandpa' ) ( 1 , null , 'theOther' , 'Grandpa' ) ( 1 , null , 'theOther' , 'Dad' ) ( 1 , null , 'theOther' , 'Foo' )

    ( 2 , null , 'this' , 'GreatGrandpa' ) ( 2 , null , 'this' , 'Grandpa' ) ( 2 , null , 'this' , 'Dad' ) ( 2 , null , 'this' , 'Foo' )

    ( 2 , null , 'that' , 'GreatGrandpa' ) ( 2 , null , 'that' , 'Grandpa' ) ( 2 , null , 'that' , 'Dad' ) ( 2 , null , 'that' , 'Foo' )

    ( 2 , null , 'theOther' , 'GreatGrandpa' ) ( 2 , null , 'theOther' , 'Grandpa' ) ( 2 , null , 'theOther' , 'Dad' ) ( 2 , null , 'theOther' , 'Foo' )

    Storing this entity in the Datastore now requires 1 + 1 + 4 + 2 + 6 + 24 = 38 writes.

    Authentication required

    You need to be signed in with Google+ to do that.

    Signing you in...

    Google Developers needs your permission to do that.