/* * Copyright 2015 Google, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.examples.abelanav2.datastore; import static com.google.api.services.datastore.client.DatastoreHelper.makeFilter; import static com.google.api.services.datastore.client.DatastoreHelper.makeKey; import static com.google.api.services.datastore.client.DatastoreHelper.makeValue; import static com.google.common.base.Preconditions.checkNotNull; import com.google.api.services.datastore.DatastoreV1.BeginTransactionRequest; import com.google.api.services.datastore.DatastoreV1.BeginTransactionResponse; import com.google.api.services.datastore.DatastoreV1.CommitRequest; import com.google.api.services.datastore.DatastoreV1.CommitResponse; import com.google.api.services.datastore.DatastoreV1.Entity; import com.google.api.services.datastore.DatastoreV1.EntityResult; import com.google.api.services.datastore.DatastoreV1.Filter; import com.google.api.services.datastore.DatastoreV1.Key; import com.google.api.services.datastore.DatastoreV1.LookupRequest; import com.google.api.services.datastore.DatastoreV1.LookupResponse; import com.google.api.services.datastore.DatastoreV1.Mutation; import com.google.api.services.datastore.DatastoreV1.Property; import com.google.api.services.datastore.DatastoreV1.PropertyExpression; import com.google.api.services.datastore.DatastoreV1.PropertyFilter; import com.google.api.services.datastore.DatastoreV1.PropertyReference; import com.google.api.services.datastore.DatastoreV1.Query; import com.google.api.services.datastore.DatastoreV1.ReadOptions; import com.google.api.services.datastore.DatastoreV1.RollbackRequest; import com.google.api.services.datastore.DatastoreV1.RunQueryRequest; import com.google.api.services.datastore.client.Datastore; import com.google.api.services.datastore.client.DatastoreException; import java.util.ArrayList; import java.util.List; /** * Provides actual operations on the datastore database. */ public final class DbUtils { /** * Constructor. */ private DbUtils() { } /** * Gets an entity from the datastore. * @param datastore the datastore. * @param kind the entity kind. * @param id the entity id. * @return the entity. * @throws DatastoreException if there is a datastore error. */ public static Entity getEntity(final Datastore datastore, final String kind, final long id) throws DatastoreException { return getEntity(datastore, makeKey(kind, id).build()); } /** * Gets an entity from the datastore. * @param datastore the datastore. * @param key the entity key. * @return the entity. * @throws DatastoreException if there is a datastore error. */ public static Entity getEntity(final Datastore datastore, final Key key) throws DatastoreException { Entity entity; LookupRequest request = LookupRequest.newBuilder().addKey(key).build(); LookupResponse response = datastore.lookup(request); if (response.getMissingCount() == 1) { entity = null; } else { entity = response.getFound(0).getEntity(); } return entity; } /** * Inserts an entity in the datastore. * @param datastore the datastore. * @param kind the entity kind. * @param parent the entity parent. Can be null if not parent. * @param properties the entity properties list. * @return the entity inserted. * @throws DatastoreException if there is a datastore error. */ public static Entity insertEntity(final Datastore datastore, final String kind, final Key parent, final List<Property> properties) throws DatastoreException { Key key; if (parent != null) { key = makeKey(parent, kind).build(); } else { key = makeKey(kind).build(); } Entity.Builder entityBuilder = Entity.newBuilder().setKey(key).addAllProperty(properties); CommitRequest commitRequest = CommitRequest.newBuilder() .setMode(CommitRequest.Mode.NON_TRANSACTIONAL).setMutation(Mutation.newBuilder() .addInsertAutoId(entityBuilder)).build(); CommitResponse response = datastore.commit(commitRequest); return entityBuilder.setKey(response.getMutationResult().getInsertAutoIdKey(0)).build(); } /** * Updates an entity in the datastore. Fails and return null if entity * does not exist. * @param datastore the datastore. * @param key the entity key. * @param properties the entity properties list to update. * @return the entity update. * @throws DatastoreException if there is a datastore error. */ public static boolean updateEntity(final Datastore datastore, final Key key, final List<Property> properties) throws DatastoreException { Entity entity = getEntity(datastore, key); if (entity != null) { Entity.Builder updatedEntity = Entity.newBuilder(entity); updatedEntity.clearProperty(); for (Property prop : entity.getPropertyList()) { boolean found = false; for (Property updatedProp : properties) { if (updatedProp.getName().equals(prop.getName())) { updatedEntity.addProperty(updatedProp); found = true; } } if (!found) { updatedEntity.addProperty(prop); } } CommitRequest request = CommitRequest.newBuilder() .setMode(CommitRequest.Mode.NON_TRANSACTIONAL).setMutation(Mutation.newBuilder() .addUpdate(updatedEntity)).build(); CommitResponse response = datastore.commit(request); return response.getMutationResult().getIndexUpdates() > 0; } return false; } /** * Deletes an entity from the datastore. * @param datastore the datastore. * @param key the entity key. * @return boolean indicating the success. * @throws DatastoreException if there is a datastore error. */ public static boolean deleteEntity(final Datastore datastore, final Key key) throws DatastoreException { CommitRequest request = CommitRequest.newBuilder() .setMode(CommitRequest.Mode.NON_TRANSACTIONAL).setMutation(Mutation.newBuilder() .addDelete(key)).build(); CommitResponse response = datastore.commit(request); return response.getMutationResult().getIndexUpdates() > 0; } /** * Deletes an entity and its children from the datastore. * @param datastore the datastore. * @param key the entity key. * @return boolean indicating the success. * @throws DatastoreException if there is a datastore error. */ public static boolean deleteEntityAndChildren(final Datastore datastore, final Key key) throws DatastoreException { // Transaction. BeginTransactionRequest.Builder transactionRequest = BeginTransactionRequest.newBuilder(); BeginTransactionResponse transactionResponse = datastore.beginTransaction(transactionRequest.build()); boolean comitting = false; boolean res = false; try { ReadOptions.Builder readOptions = ReadOptions.newBuilder() .setTransaction(transactionResponse.getTransaction()); Query.Builder query = Query.newBuilder(); Filter ancestorFilter = makeFilter("__key__", PropertyFilter.Operator.HAS_ANCESTOR, makeValue(key)).build(); query.setFilter(makeFilter(ancestorFilter)); query.addProjection(PropertyExpression.newBuilder().setProperty(PropertyReference.newBuilder() .setName("__key__"))); // Note that this query include the ancestor itself in the results RunQueryRequest runQueryRequest = RunQueryRequest.newBuilder().setReadOptions(readOptions) .setQuery(query).build(); List<Key> resultKeys = new ArrayList<>(); for (EntityResult result : datastore.runQuery(runQueryRequest).getBatch() .getEntityResultList()) { resultKeys.add(result.getEntity().getKey()); } CommitRequest request = CommitRequest.newBuilder() .setTransaction(transactionResponse.getTransaction()) .setMutation(Mutation.newBuilder().addAllDelete(resultKeys)).build(); comitting = true; CommitResponse response = datastore.commit(request); res = response.getMutationResult().getIndexUpdates() > 0; } finally { if (!comitting) { RollbackRequest.Builder rollback = RollbackRequest.newBuilder() .setTransaction(transactionResponse.getTransaction()); datastore.rollback(rollback.build()); } } return res; } /** * Gets an entity children from the datastore. * @param datastore the datastore. * @param key the entity key. * @param kind the entity kind. Can be null to return all kinds found. * @param includeAncestor if we want to include the ancestor in the results. * @return the list of children. * @throws DatastoreException if there is a datastore error. */ public static List<Entity> getChildren(final Datastore datastore, final Key key, final String kind, final boolean includeAncestor) throws DatastoreException { Query.Builder query = Query.newBuilder(); Filter ancestorFilter = makeFilter("__key__", PropertyFilter.Operator.HAS_ANCESTOR, makeValue(key)).build(); if (kind != null) { query.addKindBuilder().setName(kind); } if (includeAncestor) { query.setFilter(makeFilter(ancestorFilter)); } else { Filter keyFilter = makeFilter("__key__", PropertyFilter.Operator.GREATER_THAN, makeValue(key)).build(); query.setFilter(makeFilter(ancestorFilter, keyFilter)); } RunQueryRequest runQueryRequest = RunQueryRequest.newBuilder().setQuery(query).build(); List<Entity> results = new ArrayList<>(); for (EntityResult result : datastore.runQuery(runQueryRequest).getBatch() .getEntityResultList()) { results.add(result.getEntity()); } return results; } /** * Returns the entity ID. * @param entity the entity. * @return the entity ID. */ public static long getEntityId(final Entity entity) { checkNotNull(entity, "Entity passed was null thus has no key"); return getEntityId(entity.getKey()); } /** * Returns the entity ID. * @param entityKey the entity key. * @return the entity ID. */ public static long getEntityId(final Key entityKey) { checkNotNull(entityKey, "EntityKey passed was null thus has no ID"); return entityKey.getPathElement(entityKey.getPathElementCount() - 1).getId(); } }