Search code examples
javaobjectifydatastore

Filter entities with null parent using `Objectify` on datastore


In my model I have

@Entity
public class Ent implements Serializable {
  @Id
  private long key;

  @Parent
  private Key<Ent> parentKey;
}

entity which creates a simple hierarchy (actually just 2-levels in my case). I would like to filter for just the roots of the hierarchy, which is defined by having no parent (parentKey = null).

I tried using filter() in Objectify, but got an exception

java.lang.IllegalArgumentException: @Parent fields cannot be filtered on. Perhaps you wish to use filterKey() or ancestor() instead?

Then I tried using ancestor(), but it seems to not support null values:

javax.servlet.ServletException: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "pojo" is null
    at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:117)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.handler.SizeLimitHandler.handle(SizeLimitHandler.java:96)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.Server.handle(Server.java:516)
    at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
    at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
    at com.google.apphosting.runtime.jetty9.RpcConnection.handle(RpcConnection.java:269)
    at com.google.apphosting.runtime.jetty9.RpcConnector.serviceRequest(RpcConnector.java:100)
    at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:184)
    at com.google.apphosting.runtime.RequestRunner.dispatchServletRequest(RequestRunner.java:262)
    at com.google.apphosting.runtime.RequestRunner.dispatchRequest(RequestRunner.java:227)
    at com.google.apphosting.runtime.RequestRunner.run(RequestRunner.java:193)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:273)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "pojo" is null
    at com.googlecode.objectify.impl.Keys.getMetadataSafe(Keys.java:70)
    at com.googlecode.objectify.impl.Keys.rawKeyOf(Keys.java:52)
    at com.googlecode.objectify.impl.Keys.anythingToRawKey(Keys.java:122)
    at com.googlecode.objectify.impl.QueryImpl.setAncestor(QueryImpl.java:221)
    at com.googlecode.objectify.impl.SimpleQueryImpl.ancestor(SimpleQueryImpl.java:69)
    at 

I suppose I should use filterKey(), the question is how do I create a "filtering key" that will accept any id, but only null parent...

Edit

It's probably not possible, my data actually doesn't encode parentKey inside the entity's key, which is necessary for @Parent feature, removing that annotation solved the issue.


Solution

  • I hate to give you bad news, but I don't think you can. There's no index for "entities which don't have an ancestor". It's just not a datastore operation.

    I would add an indexed field to your entities to enable the query you want. You may have to map/reduce across all of your entities, resaving them to add the field.