Grails returning one element from each object - grails

I am trying to just return a single string from each object.
Given the following:
class Book {
String title
Date releaseDate
String author
Boolean paperback
}
for every instance of Book I want to get an array of authors then make them unique.
I thought you could do something like:
def authors = Book.findAllByAuthor()
This just gives me an array off book objects.
I know i can do a
a =[]
authors.each{a.add(it.author)}
a.unique()
I am almost certain there is a way just to grab all authors in one line.
any ideas?

This gives you distinct authors of any book:
Book.executeQuery("select distinct author from Book")

You can use projections to get a distinct list of authors across all books. Take a look at the createCriteria documentation for more examples.
def c = Book.createCriteria()
def authors = c.list() {
projections {
distinct('author')
}
}

Related

How do I check if a given list is a subset of a field value in GORM?

I'm working on some sort of advanced search feature with variable fields. Some of the search fields are lists of some primitive(ish) objects (String, enums, etc.). I want to be able to retrieve records whose values are a subset of some given list.
To illustrate, say I have a Book class (assume the model is appropriate this way):
class Book {
...
List authors = []
...
}
In addition say we have the following book records:
Book(title: 'Great Expectations of Tom Sawyer', authors: ['cdickens', 'mtwain'])
Book(title: 'Huckleberry Potter in Bleak House', authors: ['cdickens', 'mtwain', 'jrowling'])
Book(title: 'A Christmas Carol', authors: ['cdickens'])
Then, I'm given a list of author (names) authorFilter = ['cdickens', 'mtwain'] to search for any collaborative works of cdickens and mtwain. How do I express this using GORM's where construct? Is it even possible to cover this using it?
Basically, what I want to do is:
Book.where {
authorFilter.every { it in authors }
}
This question has come up before. Unfortunately, where nor criteria queries have an every() equivalent. But there's a hack that may work for you. But first, I'll expand on your domain model.
Domain model
class Book {
String title
static hasMany = [authors: Author]
static belongsTo = Author
}
class Author {
String name
static hasMany = [books: Book]
}
HQL query
Using the domain model described above, you can use the following HQL query.
def hql = """
SELECT b FROM Book AS b
INNER JOIN b.authors AS a
WHERE a.name in :authors
GROUP BY b
HAVING COUNT(b) = :count
"""
def books = Book.executeQuery(hql, [authors: authorFilter, count: authorFilter.size()])
How it works.
You can read about how this query works in the other question I mentioned.
I don't think this is any better than #EmmanuelRosa's answer but I have another approach using HQL and the executeQuery method.
Using the same domain model he's given in his answer, I use the MEMBER OF expression to restrict the results.
def authorFilter = [Author.get(1)]
def authorMemberOfRestriction = ""
def namedParameters = [:]
authorFilter.eachWithIndex{ aut, ind ->
authorMemberOfRestriction += ":author${ind} MEMBER OF b.authors AND "
namedParameters.put("author" + ind, aut)
}
namedParameters.put('count', authorFilter.size())
def hql = """
FROM Book b
WHERE
(
${authorMemberOfRestriction}
size(b.authors) = :count
)
"""
def books = Book.executeQuery(hql, namedParameters)
Mine is somewhat different in that the authorFilter is a collection of Author domain class instances; I found it to work much easier for the MEMBER OF expression and truthfully is more of a depiction of how the real data would be modeled.
You can see that I build the multiple MEMBER OF expressions with the eachWithIndex, using the index on both sides of the named parameters. It's not exactly pretty but I don't believe there is a way around this and still use this approach.
I think #EmmanuelRosa's approach is probably the 'cleaner' option but the MEMBER OF approach makes more sense in my head as far as the logic is concerned.
There doesn't seem to be a simpler way of doing this other than doing HQL query. Taking hints from this answer to a very similar question, I figured out a solution to my problem.
To be clear, the Book should already have relation to many String using the hasMany construct:
class Book {
...
static hasMany = [authors: String]
...
}
To fetch results:
def results = Product.executeQuery("select p from Products p join p.tags t where t in :tags", [tags: givenTags])

Grails createCrieteria on super class with conditions on subclasses

I have been trying for this. But don't know it is possible or not. I need to list records by writing a crieria query on super class with conditions on subclass.
Say, Cats, Dogs extends Animal class. I need to list all animals but dogs with black dots and cats with white color. Here the key problem is, dog's properties are not in cat domain class.
But i have to write createCriteria on Animal class so that i can paginate with all animals or am i missing something?.
given that you use tablePerHierarchy = true you should be able to write a native SQL query to select the records from the table by where-clauses pointing to partially null-ed columns:
Anumal.withCriteria{
or{
sqlRestriction "dots is not null and dots != 'black'" // dogs
sqlRestriction "color = 'white'" // cats
}
}

GORM: Find by collection contents

I Have the following two classes:
class Person{
String name
static hasMany = [
items: Item
]
}
class Item{
String name
}
Multiple Persons can also have the same Item. I'm trying to get a list of all Persons that have a specific item in their collection. I.E. Both Person A and B have Item A in their list so return them both. Unfortunately their is no findAllByCollectionContains() the closest is is findAllByCollection() which requires an exact set.
I've been trying executeQuery to give me a but more control and haven't come up with anything yet.
Example of what I have tried:
Person.executeQuery("select name from Person p where ? in p.items",[item])
Any suggestions?
You have to join the items collection, then you can query on it easily
Person.executeQuery("select name from Person p join p.items as i where i = ?",[item])

Grails projections group by and count

Following are my two classes
class Users {
String emailAddress
String password
// String filename
String firstName
String lastName
Date dateCreated
Date lastUpdated
}
and
class SharedDocuments {
Users author
Users receiver
Documents file
static constraints = {
}
}
I want to run a query similar to this one, essentially i want to get list of all the users along with count of documents they have authored
SELECT author_id, COUNT(SharedDocuments.id )FROM SharedDocuments
INNER JOIN users ON author_id = users.id
GROUP BY author_id
This is what I have so far
def sharedDocumentsInstanceList = SharedDocuments.createCriteria().list(params){
createAlias("author","a")
eq("receiver.id",session.uid)
projections{
groupProperty "author"
count "id",'mycount'
}
order('mycount','desc')
maxResults(params.max)
}
I have this 90% working, if i get rid of count or countDistinct I get list of distinct authors but what i want is authors along with the counts of documents. So when i add the count or countDistinct clause to this criteria I just get array of long!!
like [2,3,4] what I want is [[author1,2],[author2,3] ...]
How can i achive this I have already seen Grails: Projection on many tables?,
Grails criteria projections - get rows count,
Grails groupProperty and order. How it works?,
but none of the answers seem to be solving my issue!
Projections with pagination has a bug where it only return the last field in the projections block. The current grails 2.1.5 has this bug fixed. Here is the reported bug http://jira.grails.org/browse/GRAILS-9644
Couldn't you just use countBy* - like
SharedDocuments.countByAuthorAndReceiver(user, receiver)
If you want the result as as you described in your query then you need the id of the author or any other specific property like email.
def sharedDocumentsInstanceList = SharedDocuments.createCriteria().list(params){
eq("receiver.id",session.uid)
projections{
groupProperty "author"
count "id",'mycount'
}
order('mycount','desc')
maxResults(params.max)
}

Grails criteria query retruing duplicate instances

I have a domain class called Order and that class has hasMany relation with Item class.
When I am querying for the list of orders with certain restrictions I am getting as many instances of Order as there are items.
So for example Order instance has say references to 3 instances of Item then , criteria call on Order is returning 3 duplicate instances of Order. I am not sure but if it's worth mentioning that the domain class Order has fetchMode set to "eager".
I am really puzzled with what's going on there. Any help in this regard will be greatly appreciated. Snippet of code is attached:
def clazz = "cust.Order"
def criteria = clazz.createCriteria()
println("clazz == "+Order.list())// returning correct data i.e unique instance of order
def filter = {
// trimmed down all filtering criteria for debugging
}//close filter
List results = criteria.list(max:params?.max,offset:params?.offset,filter)
results.each{Object data->
println(data.getClass())
}
println("results == "+results)
Thanks again
One solution is to use this inside your query:
resultTransformer org.hibernate.Criteria.DISTINCT_ROOT_ENTITY
If you call criteria.listDistinct instead of criteria.list duplicates will be eliminated
Criteria API is just a wrapper for constructing a SQL query. In your case, the query in question has JOINs in it (because of the eager fetching), and returns a cartesian product of Orders and their matching Items. Each row returned is included in results as a separate Order instance.
The easiest way to remove duplicates is to put all the results in a Set, like this:
def resultSet = new HashSet()
resultSet.addAll(results)
println("results == " + resultSet)
You could also use dynamic finders, as in Order.findAllBy* .Depending on how complicated your filter is, this could be easy or tough :)

Resources