quarks
quarks

Reputation: 35276

Filtering by List - Objectify

This query works, the fullPath field is a List<String>:

    KeyLookup lookup 
        = ofy().load().type(KeyLookup.class).filter("fullPath IN", key.getFullPath()).first().get();

The query above also fetch the entities having the same String elements in the List<String> as with key.getFullPath(), however it also fetches those entities having the same String in the list with additional more String in the list.

How can I filter only those Entity having the exact same elements in the list, like "no more, no less"

Update:

For example

One entity (say Object1) field fullPath contains:

Another entity (say Object2) field fullPath contains:

And key.getFullPath contains:

Then the query above will return Object1 and Object2, however what I need is that it only returns Object2

Upvotes: 0

Views: 1740

Answers (2)

stickfigure
stickfigure

Reputation: 13556

Assuming you want an exact match (no more values, no fewer values), there is no "native datastore" way to implement this. IN gets you an or operation, multiple calls to filter() gives you an and operation - neither of which are exact matches. However, there are ways.

Option 1: Join the list into a single indexed property

Concatenate all the items in the list into a single value and store that as a synthetic indexed property. Maybe you need to normalize it depending on the significance of order or case. Query on that property, not the list property.

This will only work if the concatenated value is guaranteed to fit under 500 chars. Seems unlikely. Instead you probably want...

Option 2: Index and query on a hash of the list

  1. Create a synthetic property which holds a hash of all the list values, something like fullPathHash. It doesn't need to be cryptographically secure; MD5 is fine.
  2. Whenever you update the list property, update the hash.
  3. Index the fullPathHash; you don't need to index the list property.
  4. When you query for a list of items, query on the hash. Since there might be collisions, do a post-query check to make sure the lists are equal and skip any false positives.

Upvotes: 3

Thanos Makris
Thanos Makris

Reputation: 3115

This is expected since when comparing two lists in a query, what actually happens under the hood is that the datastore fires multiple queries for each of the values in the filter list. Thus, when you query for Object2, four queries are executed for each of the values in Object2. Since, Object1 is a "subset" of Object2, it also matches the queries. This is the reason you get both Object1 and Object2.

You can modify your queries to use the AND operator in order to force exact matching. So, you can have the following:

Query<KeyLookup> q = ofy().load().type(KeyLookup.class);
for (String f : key.getFullPath()) {
    q = q.filter("fullPath", f);
}
KeyLookup lookup = q.first().get();

Hope this helps.

Upvotes: 1

Related Questions