Gaelyk: How to perform datastore queries on collection attributes

1.5k views Asked by At

The Gaelyk tutorial provides some nice low level wrappers to the datastore and this Gaelyk google groups article describes an easy way to model relationships by simply storing the keys in a collection on the entity.

My question is how can I perform queries on the values within the collections? Here is some example code to clarify...

def b1 = new Entity("Book")
b1.title = "Book1"
b1.save()

def b2 = new Entity("Book")
b2.title = "Book2"
b2.save()

def author1 = new Entity("Author")  
author1.books = [b1.key, b2.key]
author1.name = "Chris"
author1.save()

// It is easy to simply query the Author entity for a standard string property
def query = new Query("Author")
query.addFilter("name", Query.FilterOperator.EQUAL, "Chris")
PreparedQuery preparedQuery = datastore.prepare(query)
def authors = preparedQuery.asList(withLimit(1))
assert authors[0].name == "Chris"

// But I can't find out how to query a collection property eg books on the Author entity
def query2 = new Query("Author")
// I would like to do something to return the entity if the value is in the collection property
// eg if there could be something like 'CONTAINS' criteria ...
// Unfortunately Query.FilterOperator.CONTAINS doesn't exist...
query2.addFilter("books", Query.FilterOperator.CONTAINS, b2.key)
PreparedQuery preparedQuery2 = datastore.prepare(query2)
def authors2 = preparedQuery2.asList(withLimit(1))
assert authors2[0].name == "Chris"

How can I create a query that that searches for matches within a collection property of an Entity? ie how to recreate the functionality of the mythical 'FilterOperator.CONTAINS' query above?

3

There are 3 answers

0
kdabir On BEST ANSWER

Answering just for the sake of users landing to this page in future:

Query.FilterOperator.EQUAL will find inside the list of keys as well (it works as CONTAINS, in case of lists). So now your second case looks like:

def query2 = new Query("Author")
query2.addFilter("books", Query.FilterOperator.EQUAL, b2.key)
PreparedQuery preparedQuery2 = datastore.prepare(query2)
def authors2 = preparedQuery2.asList(withLimit(1))
assert authors2[0].name == "Chris"

and the assertion passes :)

This may look strange at first sight but it is indeed a great feature of DataStore.

2
Benjamin Muschko On

One way to do this would be to have the author key as one of the fields in your entity "Book". Therefore, you can query the book first and then get the authors for it.

def author1 = new Entity("Author")  
author1.name = "Chris"
author1.save()

def b1 = new Entity("Book")
b1.title = "Book1"
b1.authors = [author1.key]
b1.save()

author1.books = [ b1.key ]
author1.save()

def book = datastore.get(b1.key)    
def chrisAuthors = book.authors.findAll { authorKey -> datastore.get(authorKey).name == 'Chris' }
assert chrisAuthors.size() == 1
0
glaforge On

By the way, a little update on this topic: you might want to have a look at the new Query DSL feature of Gaelyk 1.0, which was released recently.

http://gaelyk.appspot.com/tutorial/app-engine-shortcuts#query

This should simplify a lot of the queries one can make against the datastore.