0
votes

I am migrating from google datastore api to objectify(i have used datastore at compute engine and migrating to 6.0a1 objectify). In previous version i used this code to query users:

KeyFactory userKeyFactory = datastore.newKeyFactory()
    .addAncestors(PathElement.of("UserList", "default"))
    .setKind(USER_KIND);

//...save entity

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind(USER_KIND)
    .setFilter(PropertyFilter.hasAncestor(datastore.newKeyFactory().setKind("UserList").newKey("default")))
    .build();
QueryResults<Entity> queryResults = datastore.run(query);
List<User> result = new ArrayList<>();
queryResults.forEachRemaining(entity -> result.add(transformUser(entity)));
return result; 

Now i am trying to make same query with ancestor. However objectify don't work with PathElement. So following code:

ofy().load().type(User.class).ancestor(PathElement.of("UserList", "default")).list()

fails with

java.lang.IllegalArgumentException: No class 'com.google.cloud.datastore.PathElement' was registered
    at com.googlecode.objectify.impl.Registrar.getMetadataSafe(Registrar.java:115)
    at com.googlecode.objectify.impl.Keys.getMetadataSafe(Keys.java:56)
    at com.googlecode.objectify.impl.Keys.getMetadataSafe(Keys.java:65)
    at com.googlecode.objectify.impl.Keys.rawKeyOf(Keys.java:47)
    at com.googlecode.objectify.impl.Keys.anythingToRawKey(Keys.java:117)
    at com.googlecode.objectify.impl.QueryImpl.setAncestor(QueryImpl.java:203)
    at com.googlecode.objectify.impl.SimpleQueryImpl.ancestor(SimpleQueryImpl.java:69)
    at com.googlecode.objectify.impl.LoadTypeImpl.ancestor(LoadTypeImpl.java:23)

What is proper way to use PathElement with objectify? I see i can create com.googlecode.objectify.Key and pass it as ancestor but it require class, but i don't have UserList class (it is small application, all entities related to single group).

I tried to use this code: ofy().load().type(User.class).ancestor(datastore.newKeyFactory().setKind("UserList").newKey("default")).list() Now it fails that User don't have field with @Parent annotation. Here is stacktrace:

com.googlecode.objectify.LoadException: Error loading Key{projectId=projectId, namespace=, path=[PathElement{kind=UserList, id=null, name=default}, PathElement{kind=User, id=1, name=null}]}: Loaded Entity has parent but com.package.model.User has no @Parent
    at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:84)
    at com.googlecode.objectify.impl.LoadEngine.load(LoadEngine.java:187)
    at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:145)
    at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:131)
    at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
    at com.googlecode.objectify.impl.Round$1.nowUncached(Round.java:66)
    at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
    at com.googlecode.objectify.impl.HybridQueryResults.lambda$load$1(HybridQueryResults.java:87)
    at com.google.common.collect.Iterators$5.transform(Iterators.java:757)
    at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:48)
    at com.google.common.collect.MultitransformedIterator.next(MultitransformedIterator.java:66)
    at com.google.common.collect.Iterators$4.computeNext(Iterators.java:623)
    at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:145)
    at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:140)
    at com.googlecode.objectify.impl.HybridQueryResults.hasNext(HybridQueryResults.java:92)
    at com.googlecode.objectify.util.IteratorFirstResult.nowUncached(IteratorFirstResult.java:31)
    at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
    at com.googlecode.objectify.LoadResult.now(LoadResult.java:25)
    at com.package.UserObjectifyRepository.getUserByEmail(UserObjectifyRepository.java:43)
    ...
Caused by: java.lang.IllegalStateException: Loaded Entity has parent but com.package.model.User has no @Parent
    at com.googlecode.objectify.impl.KeyMetadata.setKey(KeyMetadata.java:142)
    at com.googlecode.objectify.impl.KeyMetadata.setKey(KeyMetadata.java:122)
    at com.googlecode.objectify.impl.KeyPopulator.load(KeyPopulator.java:24)
    at com.googlecode.objectify.impl.translate.ClassPopulator.load(ClassPopulator.java:118)
    at com.googlecode.objectify.impl.translate.ClassTranslator.loadSafe(ClassTranslator.java:109)
    at com.googlecode.objectify.impl.translate.NullSafeTranslator.load(NullSafeTranslator.java:21)
    at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:80)
    .. 125 more

I suppose correct way to fix this is to have parent field in my User entity, something like this:

@Parent
private UserList userList;

But i haven't such entity "UserList", i need ancestor just to make query with strong consistency.

UPD: Error is gone if i adding this code:

import com.google.cloud.datastore.Key;
@Parent
private Key userList;

Is it proper way to make consistent query?

1
What do you mean by "Now it fails that User don't have field with @Parent annotation"? The right answer is to provide a low-level Key as you describe. - stickfigure
Thanks for response @stickfigure, i will add stacktrace tomorrow, when will be near code. - Zufar Muhamadeev
@stickfigure i updated question - Zufar Muhamadeev

1 Answers

0
votes

The @Parent annotation requires a key.

You just have to create a key, so it can understand that Parent/Child relationship.