google-app-enginegoogle-cloud-datastorelow-level-api

Datastore Low level API - query multiple value property - FilterOperator.IN not working


Why does this filter return no entities? I can't work it out. Other filters work just fine.

I create an entity with a property set to a List of Strings. List is also a Collection, so the Datastore should accept it (and I think it does *).

Key aliceKey = KeyFactory.createKey("User", user.getEmail());
Entity alice = new Entity(aliceKey);
List<String> roles = new ArrayList<String>();
roles.add("admin");
roles.add("user");
alice.setProperty("role", roles);

I save it to the datastore:

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(alice);

Now I make the query: FilterOperator.IN requires an Iterable in the third argument of the FilterPredicate constructor, which the rolesList is.

List<String> rolesList = new ArrayList<String>();
rolesList.add("admin");

Filter propertyFilter =  new FilterPredicate("role", FilterOperator.IN, rolesList);
Query q = new Query("User").setFilter(propertyFilter);
List<Entity> c = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults());

if(c.size()==0)
    response.getWriter().println("nothing");
for (int i = 0; i < c.size(); i++) {
    response.getWriter().println(c.get(i).toString());
}

Do the values in the property appear in the right format here? This is the output of alice.toString()

Alice: <Entity [User("test@example.com")]:
    role = [admin, zone admin]
>

Solution

  • Replace FilterOperator.IN with FilterOperator.EQUALS, and pass "admin" as a value.

    FilterOperator.IN is the opposite of what you are trying to do - it looks if a property value (a single value) equals any of the values that you pass in the filter. Under the hoods, FilterOperator.IN is executed as a series of FilterOperator.EQUALS queries - one for each value on the filter list.