Skip to content

Datastore

floci-gcp emulates Google Cloud Datastore over gRPC using the real google.datastore.v1 protocol.

Configuration

Variable Default Description
FLOCI_GCP_SERVICES_DATASTORE_ENABLED true Enable/disable Datastore

Emulator Variable

export DATASTORE_EMULATOR_HOST=localhost:4588

The GCP Datastore SDK uses this variable to route requests to floci-gcp instead of datastore.googleapis.com.

Quick Start

DatastoreOptions options = DatastoreOptions.newBuilder()
    .setHost("http://localhost:4588")
    .setProjectId("floci-local")
    .setCredentials(NoCredentials.getInstance())
    .build();

Datastore datastore = options.getService();

// Create entity
KeyFactory keyFactory = datastore.newKeyFactory().setKind("Task");
Key taskKey = datastore.allocateId(keyFactory.newKey());

Entity task = Entity.newBuilder(taskKey)
    .set("description", "Buy groceries")
    .set("done", false)
    .set("priority", 4L)
    .build();

datastore.put(task);

// Query entities
Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.eq("done", false))
    .build();

QueryResults<Entity> results = datastore.run(query);
while (results.hasNext()) {
    Entity e = results.next();
    System.out.println(e.getString("description"));
}
import os
os.environ["DATASTORE_EMULATOR_HOST"] = "localhost:4588"

from google.cloud import datastore

client = datastore.Client(project="floci-local")

# Create entity
key = client.key("Task")
task = datastore.Entity(key=key)
task.update({
    "description": "Buy groceries",
    "done": False,
    "priority": 4,
})
client.put(task)

# Query entities
query = client.query(kind="Task")
query.add_filter("done", "=", False)
results = list(query.fetch())
for task in results:
    print(task["description"])
process.env.DATASTORE_EMULATOR_HOST = "localhost:4588";

import { Datastore } from "@google-cloud/datastore";

const datastore = new Datastore({ projectId: "floci-local" });

// Create entity
const key = datastore.key("Task");
const data = [
    { name: "description", value: "Buy groceries" },
    { name: "done", value: false },
    { name: "priority", value: 4 },
];

await datastore.save({ key, data });

// Query entities
const query = datastore.createQuery("Task").filter("done", "=", false);
const [entities] = await datastore.runQuery(query);
entities.forEach(e => console.log(e.description));
export DATASTORE_EMULATOR_HOST=localhost:4588
gcloud config set project floci-local

# Use gcloud datastore operations
gcloud datastore operations list

Transactions

TransactionCallable<Void> callable = transaction -> {
    Key taskKey = datastore.newKeyFactory().setKind("Task").newKey(1);
    Entity task = transaction.get(taskKey);

    if (task != null) {
        Entity updated = Entity.newBuilder(task)
            .set("done", true)
            .build();
        transaction.put(updated);
    }
    return null;
};

datastore.runInTransaction(callable);

Indexes

Datastore requires indexes for composite queries (queries with multiple inequality filters or ORDER BY on a field that's not the sort field). In the emulator, basic indexes are created automatically; complex composite indexes can be defined in datastore.indexes.yaml.

GQL Queries

Datastore supports GQL (Google Query Language) syntax:

Query<Entity> query = Query.newGqlQueryBuilder(Query.ResultType.ENTITY,
    "SELECT * FROM Task WHERE done = @done LIMIT @limit")
    .setBinding("done", false)
    .setBinding("limit", 10)
    .build();

QueryResults<Entity> results = datastore.run(query);

Supported GQL syntax: - SELECT * FROM Kind - WHERE prop = value (and !=, <, <=, >, >=) - WHERE cond1 AND cond2 - LIMIT n / OFFSET n - Named bindings (@name) and positional bindings (@1)

Supported Operations

  • Lookup
  • RunQuery (structured query and GQL)
  • RunAggregationQuery (COUNT)
  • BeginTransaction
  • Commit
  • Rollback
  • AllocateIds
  • ReserveIds