use of com.google.storage.onestore.v3.OnestoreEntity.Path in project appengine-java-standard by GoogleCloudPlatform.
the class LocalDatastoreService method runQuery.
// status
@SuppressWarnings("unused")
public QueryResult runQuery(Status status, Query query) {
// Construct a validated query right away so we can fail fast
// if something is wrong.
final LocalCompositeIndexManager.ValidatedQuery validatedQuery = new LocalCompositeIndexManager.ValidatedQuery(query);
query = validatedQuery.getV3Query();
// LocalCompositeIndexManager.ValidatedQuery. I don't know why.
try {
CursorModernizer.modernizeQueryCursors(query);
} catch (InvalidConversionException e) {
throw newError(ErrorCode.BAD_REQUEST, "Invalid cursor");
}
String app = query.getApp();
Profile profile = getOrCreateProfile(app);
synchronized (profile) {
// ancestor does not imply we have a transaction.
if (query.hasTransaction() || query.hasAncestor()) {
// Query can only have a txn if it is an ancestor query. Either way we
// know we've got an ancestor.
Path groupPath = getGroup(query.getAncestor());
Profile.EntityGroup eg = profile.getGroup(groupPath);
if (query.hasTransaction()) {
if (!app.equals(query.getTransaction().getApp())) {
throw newError(ErrorCode.INTERNAL_ERROR, "Can't query app " + app + "in a transaction on app " + query.getTransaction().getApp());
}
LiveTxn liveTxn = profile.getTxn(query.getTransaction().getHandle());
// this will throw an exception if we attempt to read from
// the wrong entity group
eg.addTransaction(liveTxn);
// Use snapshot profile.
profile = eg.getSnapshot(liveTxn);
}
if (query.hasAncestor()) {
if (query.hasTransaction() || !query.hasFailoverMs()) {
// Either we have a transaction or the user has requested strongly
// consistent results. Either way, we need to apply jobs.
eg.rollForwardUnappliedJobs();
}
}
}
if (query.hasSearchQuery()) {
throw newError(ErrorCode.BAD_REQUEST, "full-text search unsupported");
}
// Run as a PseudoKind query if necessary, otherwise check the actual local datastore
List<EntityProto> queryEntities = pseudoKinds.runQuery(query);
Map<Reference, Long> versions = null;
if (queryEntities == null) {
Collection<VersionedEntity> versionedEntities = null;
Map<String, Extent> extents = profile.getExtents();
Extent extent = extents.get(query.getKind());
if (extent != null) {
// Make a copy of the list of all the entities in the extent
versionedEntities = extent.getAllEntities();
} else if (!query.hasKind()) {
// Kind-less query, so we need a list containing all entities of
// all kinds.
versionedEntities = profile.getAllEntities();
if (query.orderSize() == 0) {
// add a sort by key asc to match the behavior of prod
query.addOrder(new Order().setDirection(Query.Order.Direction.ASCENDING).setProperty(Entity.KEY_RESERVED_PROPERTY));
}
} else {
// no extent - we're querying for a kind without any entities
}
if (versionedEntities != null) {
queryEntities = new ArrayList<>();
versions = new HashMap<>();
for (VersionedEntity entity : versionedEntities) {
queryEntities.add(entity.entityProto());
versions.put(entity.entityProto().getKey(), entity.version());
}
}
}
// Give all entity groups with unapplied jobs the opportunity to catch
// up. Note that this will not impact the result of the query we're
// currently fulfilling since we already have the (unfiltered) result
// set.
profile.groom();
if (queryEntities == null) {
// so we don't need to check for null anywhere else down below
queryEntities = Collections.emptyList();
}
// Building filter predicate
List<Predicate<EntityProto>> predicates = new ArrayList<>();
// apply ancestor restriction
if (query.hasAncestor()) {
final List<Element> ancestorPath = query.getAncestor().getPath().elements();
predicates.add(new Predicate<EntityProto>() {
@Override
public boolean apply(EntityProto entity) {
List<Element> path = entity.getKey().getPath().elements();
return path.size() >= ancestorPath.size() && path.subList(0, ancestorPath.size()).equals(ancestorPath);
}
});
}
if (query.isShallow()) {
final long keyPathLength = query.hasAncestor() ? query.getAncestor().getPath().elementSize() + 1 : 1;
predicates.add(new Predicate<EntityProto>() {
@Override
public boolean apply(EntityProto entity) {
return entity.getKey().getPath().elementSize() == keyPathLength;
}
});
}
// apply namespace restriction
final boolean hasNamespace = query.hasNameSpace();
final String namespace = query.getNameSpace();
predicates.add(new Predicate<EntityProto>() {
@Override
public boolean apply(EntityProto entity) {
Reference ref = entity.getKey();
// Filter all elements not in the query's namespace.
if (hasNamespace) {
if (!ref.hasNameSpace() || !namespace.equals(ref.getNameSpace())) {
return false;
}
} else {
if (ref.hasNameSpace()) {
return false;
}
}
return true;
}
});
// Get entityComparator with filter matching capability
final EntityProtoComparator entityComparator = new EntityProtoComparator(validatedQuery.getQuery().orders(), validatedQuery.getQuery().filters());
// applying filter restrictions
predicates.add(new Predicate<EntityProto>() {
@Override
public boolean apply(EntityProto entity) {
return entityComparator.matches(entity);
}
});
Predicate<EntityProto> queryPredicate = Predicates.<EntityProto>not(Predicates.<EntityProto>and(predicates));
// The ordering of the following operations is important to maintain correct
// query functionality.
// Filtering entities
Iterables.removeIf(queryEntities, queryPredicate);
// Expanding projections
if (query.propertyNameSize() > 0) {
queryEntities = createIndexOnlyQueryResults(queryEntities, entityComparator);
}
// Sorting entities
Collections.sort(queryEntities, entityComparator);
// Apply group by. This must happen after sorting to select the correct first entity.
queryEntities = applyGroupByProperties(queryEntities, query);
// store the query and return the results
LiveQuery liveQuery = new LiveQuery(queryEntities, versions, query, entityComparator, clock);
// CompositeIndexManager does some filesystem reads/writes, so needs to
// be privileged.
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
LocalCompositeIndexManager.getInstance().processQuery(validatedQuery.getV3Query());
return null;
}
});
// Using next function to prefetch results and return them from runQuery
QueryResult result = liveQuery.nextResult(query.hasOffset() ? query.getOffset() : null, query.hasCount() ? query.getCount() : null, query.isCompile());
if (query.isCompile()) {
result.setCompiledQuery(liveQuery.compileQuery());
}
if (result.isMoreResults()) {
long cursor = queryId.getAndIncrement();
profile.addQuery(cursor, liveQuery);
result.getMutableCursor().setApp(query.getApp()).setCursor(cursor);
}
// Copy the index list for the query into the result.
for (Index index : LocalCompositeIndexManager.getInstance().queryIndexList(query)) {
result.addIndex(wrapIndexInCompositeIndex(app, index));
}
// for
return result;
}
}
use of com.google.storage.onestore.v3.OnestoreEntity.Path in project appengine-java-standard by GoogleCloudPlatform.
the class LocalDatastoreService method deleteImpl.
// status
@SuppressWarnings("unused")
public DeleteResponse deleteImpl(Status status, DeleteRequest request) {
DeleteResponse response = new DeleteResponse();
if (request.keySize() == 0) {
return response;
}
Cost totalCost = response.getMutableCost();
// We don't support requests that span apps, so the app for the first key
// is the app for all keys.
String app = request.keys().get(0).getApp();
final Profile profile = getOrCreateProfile(app);
LiveTxn liveTxn = null;
// Maintain a mapping of keys by entity group so that we can apply one job
// per entity group.
Map<Path, List<Reference>> keysByEntityGroup = new LinkedHashMap<>();
Map<Reference, Long> writtenVersions = new HashMap<>();
synchronized (profile) {
for (final Reference key : request.keys()) {
validatePathComplete(key);
Path group = getGroup(key);
if (request.hasTransaction()) {
if (liveTxn == null) {
liveTxn = profile.getTxn(request.getTransaction().getHandle());
}
checkRequest(!liveTxn.isReadOnly(), "Cannot modify entities in a read-only transaction.");
Profile.EntityGroup eg = profile.getGroup(group);
// this will throw an exception if we attempt to modify
// the wrong entity group
eg.addTransaction(liveTxn).addDeletedEntity(key);
} else {
List<Reference> keysToDelete = keysByEntityGroup.get(group);
if (keysToDelete == null) {
keysToDelete = new ArrayList<>();
keysByEntityGroup.put(group, keysToDelete);
}
keysToDelete.add(key);
}
}
// does all the work for each entity group.
for (final Map.Entry<Path, List<Reference>> entry : keysByEntityGroup.entrySet()) {
Profile.EntityGroup eg = profile.getGroup(entry.getKey());
eg.incrementVersion();
LocalDatastoreJob job = new WriteJob(highRepJobPolicy, eg, profile, Collections.<EntityProto>emptyList(), entry.getValue());
addTo(totalCost, job.calculateJobCost());
eg.addJob(job);
for (Reference deletedKey : entry.getValue()) {
writtenVersions.put(deletedKey, job.getMutationTimestamp(deletedKey));
}
}
}
if (!request.hasTransaction()) {
for (Reference key : request.keys()) {
response.addVersion(writtenVersions.get(key));
}
}
return response;
}
use of com.google.storage.onestore.v3.OnestoreEntity.Path in project appengine-java-standard by GoogleCloudPlatform.
the class LocalDatastoreService method get.
public GetResponse get(@SuppressWarnings("unused") Status status, GetRequest request) {
GetResponse response = new GetResponse();
LiveTxn liveTxn = null;
for (Reference key : request.keys()) {
validatePathComplete(key);
String app = key.getApp();
Path groupPath = getGroup(key);
GetResponse.Entity responseEntity = response.addEntity();
Profile profile = getOrCreateProfile(app);
synchronized (profile) {
Profile.EntityGroup eg = profile.getGroup(groupPath);
if (request.hasTransaction()) {
if (liveTxn == null) {
liveTxn = profile.getTxn(request.getTransaction().getHandle());
}
// this will throw an exception if we attempt to read from
// the wrong entity group
eg.addTransaction(liveTxn);
}
boolean eventualConsistency = request.hasFailoverMs() && liveTxn == null;
EntityProto entity = pseudoKinds.get(liveTxn, eg, key, eventualConsistency);
if (entity == PseudoKinds.NOT_A_PSEUDO_KIND) {
VersionedEntity versionedEntity = eg.get(liveTxn, key, eventualConsistency);
if (versionedEntity == null) {
entity = null;
if (!eventualConsistency) {
responseEntity.setVersion(profile.getReadTimestamp());
}
} else {
entity = versionedEntity.entityProto();
responseEntity.setVersion(versionedEntity.version());
}
}
if (entity != null) {
responseEntity.getMutableEntity().copyFrom(entity);
postprocessEntity(responseEntity.getMutableEntity());
} else {
responseEntity.getMutableKey().copyFrom(key);
}
// Give all entity groups with unapplied jobs the opportunity to catch
// up. Note that this will not impact the result we're about to return.
profile.groom();
}
}
return response;
}
use of com.google.storage.onestore.v3.OnestoreEntity.Path in project appengine-java-standard by GoogleCloudPlatform.
the class KindPseudoKind method makeKindEntity.
/**
* Creates a __kind__ entity
*/
private static EntityProto makeKindEntity(String kind, String app, String namespace) {
EntityProto kindEntity = new EntityProto();
Path path = new Path();
path.addElement().setType(KIND_METADATA_KIND).setName(kind);
Reference key = new Reference().setApp(app).setPath(path);
if (namespace.length() > 0) {
key.setNameSpace(namespace);
}
kindEntity.setKey(key);
// EntityProto.entity_group is a required PB field.
kindEntity.getMutableEntityGroup().addElement(path.getElement(0));
return kindEntity;
}
use of com.google.storage.onestore.v3.OnestoreEntity.Path in project appengine-java-standard by GoogleCloudPlatform.
the class KeyTranslatorTest method testUpdateKey_Name.
@Test
public void testUpdateKey_Name() {
Key key = new Key("yam", "harold");
AppIdNamespace appIdNamespace = key.getAppIdNamespace();
Reference ref = new Reference();
ref.setApp("my app");
Path path = new Path();
ref.setPath(path);
KeyTranslator.updateKey(ref, key);
assertThat(key.getAppIdNamespace()).isEqualTo(appIdNamespace);
assertThat(key.getId()).isEqualTo(Key.NOT_ASSIGNED);
assertThat(key.getName()).isEqualTo("harold");
}
Aggregations