If I have three domain classes like this:
class Candidate {
static belongsto=[application:Application]
}
class Vote {
String name;
static belongsto=[application:Application]
}
class Application {
Date datedemand;
Candidate candidates;
Vote votes
static hasMany = [candidates:Candidate,votes:Vote]
}
I want to retrieve all candidates grouped by Vote's Name .
I started the following attempt, and I remain blocked :
def criteria = Application.createCriteria()
def results = criteria.list {
votes {
}
candidates{
}
}
In decent grails (2.x+) you could try something similar:
Application.withCriteria {
createAlias("votes", votes)
projections {
groupProperty("votes.name")
}
}
In this scenario I would go for a simple vanilla HQL instead of Criteria, something like this or more (use join the way you need):
String hql = "Select V.name, C.name " +
"from Candidate C, Vote V " +
"where C.application = V.application " +
"group by V.name, C.name"
Query query = session.createQuery(hql)
List results = query.list()
Related
Let's say I have this GORM objects
Class Person {
String country
int age
String Name
}
I would like to get the oldest person for each country
I can get the projection this way, but I would like to have the list of Person objects.
def results = Person.withCriteria {
projections {
groupProperty("country")
max('age')
}
}
def countries = Person.executeQuery(
"select distinct person.country from Person person")
def persons = countries.collect{ country ->
Person.executeQuery(
"from Person as person1 " +
"where person1.country='${country}' and person1.age=" +
"("+
"select max(age) "+
"from Person as person2 "+
"where person2.country='${country}' "+
"group by person2.country "+
")"
)
}
Hopefully this helps. This will return a list of lists as there is the possibility that for each country there is a list of people that have the maximum age.
I ran into some problems while trying to count items.
Imagine the following domain classes
class Book {
String name
}
class Author {
String name
static hasMany = [books:Book]
}
How do I get a list of Authors sorted by number of Books?
here's my try:
def c = Author.createCriteris()
c.list {
projections {
count 'books', 'numBooks'
groupProperty 'id'
}
order 'numBooks', 'desc'
}
but somehow I get only unusable results... and I don't know how to join the Author objects to the rsult list.... :-(
Havent tried it, but couldn't you do something like:
class Author {
String name
static hasMany = [books:Book]
static namedQueries = {
sortByMostBooks {
books {
order('size', 'desc')
}
}
}
}
And then get access by the cleaner named query
Author.sortByMostBooks.list();
In addition, you may want to include a belongsTo in you Book domain class:
static belongsTo = Author;
or:
static belongsTo = [authors:Author];
if a book is likely to have multiple authors
got something!
I still don't know how to do it with a criteria, but by switching to HQL, I succeeded.
So if someone comes up with a criteria solution, he will still get the bonus for the correct answer :-)
here is my query:
Author.executeQuery("""
select
a, size(a.books) as numBooks
from
Author a
group by
id
order by
numBooks DESC
""",[max:20])
This query isn't efficient, since it fetches all Authors in a loop, but that's ok for now.
Let say I have the following domain model :
class Book {
String title
Set authors
static hasMany = {authors: Author}
}
class Author {
String name
}
The HQL query to retrieve a collection of Author given a query on a title Book:
Author.executeQuery("select distinct author from Book as book join book.authors as author where book.name like ?", ["%groovy%"])
But I would to be able to have the same result a with DetachedCriteria or alike (but is it possible ... ?) and without adding a relationship from Author to Book (otherwise it would pretty obvious)
thanks
This can be done a few ways with criteria or detached Criteria, but a bit easier with regular GORM criteria since it implements the createAlias command that detachedCriteria doesn't as of Grails 2.2.2:
Create Alias In Detached Criteria
Here are both ways:
package bookauthor
import grails.gorm.DetachedCriteria
import grails.orm.HibernateCriteriaBuilder
class MyController {
def index() {
HibernateCriteriaBuilder ac2 = Author.createCriteria()
HibernateCriteriaBuilder criteria2 = Author.createCriteria()
HibernateCriteriaBuilder criteria = Book.createCriteria()
def bookResults = criteria {
projections {
property 'aut.id'
}
createAlias('authors', 'aut')
like('title', '%Groovy%')
}
def dc = new DetachedCriteria(Book).build {
authors {}
like('title', '%Groovy%')
}
def myList = dc.list().collect { it.authors.collect { author -> author.id} }.flatten()
def resultsDetached = criteria2 {
'in'('id', myList )
}
def results = ac2 {
'in'('id', bookResults )
}
log.info("RESULTS: " + results)
log.info("Detached RESULTS: " + resultsDetached)
}
}
You'll see in the logs:
bookauthor.MyController - RESULTS: [bookauthor.Author : 1, bookauthor.Author : 3]
bookauthor.MyController - Detached RESULTS: [bookauthor.Author : 1, bookauthor.Author : 3]
Unfortunately, AFAIK, this is not possible with this query. It's possible with the following ugly query, though:
select author from Author author
where author.id in (select author2.id from Book book
join book.authors as author2
where book.name like :bookName)
For such a simple, non-dynamically composed query, I would stick with your HQL query. If you really need to use Criteria, then this is the corresponding code:
Criteria c = session.createCriteria(Author.class, "author");
DetachedCriteria dc = DetachedCriteria.forClass(Book.class, "book");
dc.createAlias("book.authors", "author2");
dc.add(Restrictions.like("book.name", bookName));
dc.setProjection(Projections.property("author.id"));
c.add(Subqueries.propertyIn("author.id", dc);
List<Author> authors = (List<Author>) c.list();
I have a domain
class InvoiceLine {
String itemName
BigDecimal unitCost
Integer quantity
}
}
I would like to come up with a grails closure using .withCriteria that does an aggregation of the (unitCost * quantity) so that I end up with sql
select item_name, sum(unit_cost * quantity) from invoice_line group by item_name;
For now, the best I could come up with is
def result = InvoiceLine.withCriteria {
projections {
groupProperty('itemName')
sum ('quantity * unitCost')
}
}
Unfortunately, grails chokes up when I run the code above. Anyone have any idea how I could achieve my objective? Any help is highly appreciated.
Does it need to be a criteria query? HQL works great here:
def result = InvoiceLine.executeQuery(
"select itemName, sum(unitCost * quantity) " +
"from InvoiceLine " +
"group by itemName")
The results will be a List of Object[] where the 1st element is a String (the name) and the 2nd is a number (the sum), so for example you could iterate with something like
results.each { row ->
println "Total for ${row[0]} is ${row[1]}"
}
I have a many-to-many relationship between two objects with a join table. I need to be able to select 5 random children based on Parent and date, excluding some children records. I am stuck. Any ideas?
Parent {
static hasMany = [children: Child]
}
Child {
Date dob
static belongsTo = [Parent]
static hasMany = [parents: Parent]
static namedQueries {
randomFiveChildrenBornAfter { parentid, dob, excludeChildren->
qt 'dob', dob
parents {
eq 'id',parentid
}
// not in(excludeChildren) ?? order by rand() ??
}
}
}
Are these literally Parent/Child relationships (as in humans)? If so, the set of children is likely to be very small and I'd probably just do it in memory rather than through a sql query.
parent.children
.findAll { dob >= fiveYearsAgo }
.sort { Math.random() }
.with { it.size() >= 5 ? it.subList(0,5) : it }
The best workaround if you want to use the withCriteria method is this:
User.withCriteria{
eq 'name', 'joseph'
sqlRestriction " order by rand()"
}
It's important to say that sometime ( depends on the Criteria query created ) it's necessary to add a " 1=1 " in sqlRestriction cause it add an "and" condition in generated query.
So if you have a sqle exception use:
sqlRestriction " 1=1 order by rand()"