/** * Gets a key composed of the relevant id and parent fields in the object. * * @param pojo must be of the entityClass type for this metadata. * @return either a Key or IncompleteKey depending on whether the id is null or not */ public IncompleteKey getIncompleteKey(final P pojo) { log.trace("Getting key from {}", pojo); if (!clazz.isAssignableFrom(pojo.getClass())) throw new IllegalArgumentException("Trying to use metadata for " + clazz.getName() + " to get key of " + pojo.getClass().getName()); final Object id = getId(pojo); final com.google.cloud.datastore.Key parent = getParentRaw(pojo); if (id == null) return factory.keys().createRawIncomplete(parent, kind); else return factory.keys().createRawAny(parent, kind, id); }
/** Create a key from a registered POJO entity. */ public static <T> Key<T> create(final T pojo) { return ObjectifyService.factory().keys().keyOf(pojo); }
@Override public Result<Void> entities(final Iterable<?> entities) { final List<com.google.cloud.datastore.Key> keys = new ArrayList<>(); for (final Object obj: entities) keys.add(ofy.factory().keys().anythingToRawKey(obj)); return ofy.createWriteEngine().delete(keys); }
/** * Eliminate any deferred operations against the entity. Used when an explicit save (or delete) was * executed against the key, so we no longer need the deferred operation. * * @param keyOrEntity can be a Key, Key<?>, Entity, or entity pojo */ public void undefer(final Object keyOrEntity) { if (keyOrEntity instanceof Key<?>) { operations.remove((Key<?>)keyOrEntity); } else if (keyOrEntity instanceof com.google.cloud.datastore.Key) { operations.remove(Key.create((com.google.cloud.datastore.Key)keyOrEntity)); } else if (factory().keys().requiresAutogeneratedId(keyOrEntity)) { // note might be FullEntity without complete key autogeneratedIdSaves.remove(keyOrEntity); } else { final Key<?> key = factory().keys().keyOf(keyOrEntity); operations.remove(key); } }
@Override public LoadIds<T> parent(final Object keyOrEntity) { final Key<T> parentKey = factory().keys().anythingToKey(keyOrEntity); return new LoadTypeImpl<>(loader, kind, type, parentKey); }
@Override @SuppressWarnings("unchecked") public <E> LoadResult<E> value(final Object key) { return (LoadResult<E>)key(ofy.factory().keys().anythingToKey(key)); }
@Override public DeleteIds parent(final Object keyOrEntity) { final Key<?> parentKey = factory().keys().anythingToKey(keyOrEntity); return new DeleteTypeImpl(deleter, type, parentKey); }
@Override public DeferredDeleteIds parent(final Object keyOrEntity) { final Key<?> parentKey = factory().keys().anythingToKey(keyOrEntity); return new DeferredDeleteTypeImpl(deleter, type, parentKey); }
/** Modifies the instance */ void addOrder(String condition) { condition = condition.trim(); boolean descending = false; if (condition.startsWith("-")) { descending = true; condition = condition.substring(1).trim(); } // Prevent ordering by @Id or @Parent fields, which are really part of the key if (this.classRestriction != null) { final KeyMetadata<?> meta = loader.ofy.factory().keys().getMetadataSafe(this.classRestriction); if (condition.equals(meta.getParentFieldName())) throw new IllegalArgumentException("You cannot order by @Parent field. Perhaps you wish to order by __key__ instead?"); if (condition.equals(meta.getIdFieldName())) { throw new IllegalArgumentException("You cannot order by @Id field. Perhaps you wish to order by __key__ instead?"); } } this.actual = actual.orderBy(descending ? OrderBy.desc(condition) : OrderBy.asc(condition)); }
@Override public void entity(final Object entity) { key(ofy.factory().keys().anythingToKey(entity)); }
@Override public <S> Result<Void> ids(final Iterable<S> ids) { return this.deleter.keys(factory().keys().createKeys(parent, type, ids)); }
@Override public void ids(final Iterable<?> ids) { this.deleter.keys(factory().keys().createKeys(parent, type, ids)); }
@Override public <E> LoadResult<E> entity(final E entity) { return key(ofy.factory().keys().keyOf(entity)); }
@Override protected Map<Key<E>, E> wrap(List<com.google.cloud.datastore.Key> base) { Map<Key<E>, E> result = new LinkedHashMap<>(base.size() * 2); // One pass through the translated pojos to patch up any generated ids in the original objects // Iterator order should be exactly the same for keys and values Iterator<com.google.cloud.datastore.Key> keysIt = base.iterator(); for (E obj: original) { com.google.cloud.datastore.Key k = keysIt.next(); if (!(obj instanceof FullEntity<?>)) { KeyMetadata<E> metadata = factory().keys().getMetadataSafe(obj); if (metadata.isIdGeneratable()) metadata.setLongId(obj, k.getId()); } Key<E> key = Key.create(k); result.put(key, obj); // Also stuff this in the session session.addValue(key, obj); } log.trace("Saved {}", base); return result; } };
/** Modifies the instance */ void addFilter(final String condition, final Object value) { final String[] parts = condition.trim().split(" "); if (parts.length < 1 || parts.length > 2) throw new IllegalArgumentException("'" + condition + "' is not a legal filter condition"); final String prop = parts[0].trim(); final FilterOperator op = (parts.length == 2) ? this.translate(parts[1]) : FilterOperator.EQUAL; // If we have a class restriction, check to see if the property is the @Parent or @Id. We used to try to convert // filtering on the id field to a __key__ query, but that tended to confuse users about the real capabilities // of GAE and Objectify. So let's force users to use filterKey() instead. if (this.classRestriction != null) { final KeyMetadata<?> meta = loader.ofy.factory().keys().getMetadataSafe(this.classRestriction); if (prop.equals(meta.getParentFieldName())) { throw new IllegalArgumentException("@Parent fields cannot be filtered on. Perhaps you wish to use filterKey() or ancestor() instead?"); } else if (prop.equals(meta.getIdFieldName())) { throw new IllegalArgumentException("@Id fields cannot be filtered on. Perhaps you wish to use filterKey() instead?"); } } // Convert to something filterable, possibly extracting/converting keys final Value<?> translated = loader.getObjectifyImpl().makeFilterable(value); addFilter(op.of(prop, translated)); }
@Override @SuppressWarnings("unchecked") public <E> Map<Key<E>, E> values(final Iterable<?> values) { // Do this in a separate pass so any errors converting keys will show up before we try loading something final List<Key<E>> keys = new ArrayList<>(); for (final Object keyish: values) keys.add(ofy.factory().keys().anythingToKey(keyish)); final LoadEngine engine = createLoadEngine(); final Map<Key<E>, Result<E>> results = new LinkedHashMap<>(); for (final Key<E> key: keys) results.put(key, engine.load(key)); engine.execute(); // Now asynchronously translate into a normal-looking map. We must be careful to exclude results with // null (missing) values because that is the contract established by DatastoreService.get(). // We use the ResultProxy and create a new map because the performance of filtered views is questionable. return ResultProxy.create(Map.class, new ResultCache<Map<Key<E>, E>>() { @Override public Map<Key<E>, E> nowUncached() { return Maps.newLinkedHashMap( Maps.filterValues( Maps.transformValues(results, ResultNowFunction.instance()), Predicates.notNull())); } }); }
/** * Make a key for the given id, which could be either string or long */ private <T> Key<T> makeKey(final Object id) { final com.google.cloud.datastore.Key key = factory().keys().createRawAny(Keys.raw(this.parent), kind, id); return Key.create(key); }
/** Modifies the instance */ void setAncestor(final Object keyOrEntity) { final com.google.cloud.datastore.Key key = loader.ofy.factory().keys().anythingToRawKey(keyOrEntity); this.actual = this.actual.andFilter(PropertyFilter.hasAncestor(key)); }
/** * Preallocate a contiguous range of unique ids within the namespace of the * specified entity class and the parent key. These ids can be used in concert with the normal * automatic allocation of ids when put()ing entities with null Long id fields. * * @param parentKeyOrEntity must be a legitimate parent for the class type. It need not * point to an existent entity, but it must be the correct type for clazz. * @param clazz must be a registered entity class with a Long or long id field, and * a parent key of the correct type. * @param num must be >= 1 and <= 1 billion */ public <T> KeyRange<T> allocateIds(final Object parentKeyOrEntity, final Class<T> clazz, final int num) { final Key<?> parent = keys().anythingToKey(parentKeyOrEntity); final String kind = Key.getKind(clazz); final IncompleteKey incompleteKey = com.google.cloud.datastore.Key.newBuilder(parent.getRaw(), kind).build(); return allocate(incompleteKey, num); }