How do I perform Deep query in Grails using createCriteria - grails

I have some grails Domain classes that have relationships between them like so...
Domain A {
...
Domain B
}
Domain B {
...
Domain C
}
Domain C {
...
String name
String attr1
String attr2
}
How can I use withCriteria to perform eager fetching on A such that instances of B and C that are related to A are included in my results
like so...
List<A> aList = [..., B: [..., C: [... name: 'name', attr1: 'attr1', attr2: 'attr2']]]

The way you describe the expected results is a peculiar and I can't tell exactly what you want there...
List<A> aList = [..., B: [..., C: [... name: 'name', attr1: 'attr1', attr2: 'attr2']]]
The declared type of aList suggest you want a List<A> but the value you show looks like a Map with some nested Maps.
If you execute a query like this...
def aList = A.withCriteria {
// your criteria goes here
}
What is returned from that will be a List of A and each A will reference a B and each B will reference a C.
def aList = A.withCriteria {
// your criteria goes here
}
for(A obj : aList) {
B b = obj.b
C c = b.c
// ...
}
I hope that helps.
EDIT BASED ON COMMENT:
It is unclear if you want them to always be eagerly fetched or if you want to express that when you express the query. It sounds like maybe you want to express it when you express the query, which can be done like this...
import import org.hibernate.FetchMode
// ...
A.withCriteria {
// criteria goes here
fetchMode "b", FetchMode.JOIN
fetchMode "b.c", FetchMode.JOIN
}

Related

How to save addTo in order by given date/id?

I need some help on my API, when I'm on web, the order is saving correct, but when its on API, it goes all wrong:
def test = parseJSON.sort { a, b -> a.ID <=> b.ID } //or dateTime, will print the same
//order when I print each of them
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:1, ID:1, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:0, ID:2, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:0, ID:3, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:4, ID:4, ph:0, redoxPotential:0, temperature:0]
test.each{
def sample = new SampleWater()
sample.levelWater = it.levelWater
sample.conductivity = it.conductivity
sample.dissolvedOxygen = it.dissolvedOxygen
sample.redoxPotential = it.redoxPotential
sample.ph = it.ph
sample.temperature = it.temperature
water.addToSamples(sample)
}
return water
My problem is that addTo is not saving in order. How can I solve this?
Make sure you have defined the type of samples as a List in your Water domain class so that we can maintain the insertion order:
class Water {
static hasMany = [samples: Sample]
List<Sample> samples = []
}
class Sample {
def levelWater
}
By default implementation of hasMany is of type Set which does not maintain the insertion order but is responsible for uniqueness.
Since, now you samples will be saved in the same order as they are inserted.
You have to specify with order you want to apply to the list of SampleWater in the "water" domain class.
i.e:
class BlogCategory {
static hasMany = [
entries : BlogEntry
]
static mapping = {
entries: sort:'dateCreated', order:'desc'
}
}
In this example BlogEntry will be ordered respect dateCreated.

Grails search owned objects by properties

I am trying to find the proper syntax for a query that I know has got to be very common but couldn't find a code example for.
class ObjA {
...
static hasMany = [b:ObjB]
}
if a is an instance of ObjA, I want to perform a query like:
a.b.findAllBsSuchThat(b.someproperty = somevalue)
In order to avoid (N+1) queries for lazy associations per a, you can use a criteria as:
ObjA.withCriteria {
b {
eq 'someProperty', someValue
}
}
or where queries:
ObjA.where { b.someProperty == somevalue }.list()
If you use something like a.b.findAllBsSuchThat(b.someproperty = somevalue) then you would be getting all b's for a and then filtering on the result. This will affect the performance and will unnecessary.

GORM Query on Multiple Parent and Child Objects With Parent's Property

FYI, feel free to suggest a better title for this. I have the following domain model (we have no control over this):
class Foo {
int id
String baz
Date someDate
static hasMany = [bars:Bar]
static mapping = {
id composite: [ "id", "baz" ]
}
}
class Bar {
int id
String baz
Date someDate
Foo foo
static mapping = {
id composite: ["id", "baz", "someDate"]
columns {
foo([:]) {
column name: "id"
column name: "baz"
}
}
}
}
My problem is: I have a List of Foos and I need to find all Bars where Foo.someDate == Bar.someDate, but only in one query instead of a query for each Foo, with no lazy loading involved. Also setting Bars to eager fetching is not an option.
For example if this is my data (pseudocode, I'm combining id and baz into simply "id" for simplicity):
[
Foo (someDate:4/1/2013)
|___ bars: [{someDate:12/4/2012, id:1}, {someDate:4/1/2013, id:2}]
Foo (someDate:5/10/2012)
|___ bars: [{someDate:{4/1/2013, id:3}
Foo (someDate:3/3/2013)
|___ bars: [{someDate:3/3/2013, id:4}, {someDate:9/5/2013, id:5}]
]
I need to return the Bars with id 2 and 4 in one query. I'm really not sure how to approach this.
It could be HQL if necessary but it's preferred to be a GORM query.
Try this:
Bar.executeQuery("select b from Bar b join b.foo as f where b.someDate = f.someDate and f in :fooList", [fooList: fooList])
Not sure if it is the best way to do so, but I think it will give you the result
:)
Bar.executeQuery("select b.id from Bar b, Foo f where b.someDate = f.someDate")
although I have removed your composite mapping constraints and then tried it, worked for me :)
or using where query:
def km = Bar.where {
foo.someDate==someDate
}
println(km.list())
If you already have foo list then can be filtered through findAll closure:
fooList.findAll{it.someDate in it.bars.someDate}
:)

Criteria on a one-to-many relation

My domain contains a one-to-many relationship like this:
class A {
B b
}
class B {
String name
}
I want to create a criteria query on A which will look for A to have the B object with the given name. It may return multiple entries. So... compare a given string with the "name" field from B and return the list of entries of type A for which B matches the name.
Thx!
Unless I am missing something that should be pretty easy:
def instanceList = A.withCriteria {
b {
eq('name','whatever')
}
}

Grails : from HQL to DetachedCriteria Query

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();

Resources