grails sum one-to-many - grails

I need a help with what I believe is very trivial stuff.
class User {
String name
static hasMany = [files : File]
}
class File {
long size
}
Now when I need the total size of all files a given user has I use the following which is working :
def user = User.get(id)
user.files.each(total+=it.size)
but how ugly is it, when I am sure it can be done with simple select sum query with either plain SQL or GORM/CRITERIA
I have tried something like File.sumBySize..()
Or
def c = File.createCriteria()
def f = c.list{
eq("user", user) // What here ?
projections
{
sum("size")
}
}
I dont know how to specify the parent(user) relationship which is not defined in File class but in Grails join tables
Any help appreciated

Why is that ugly? It only takes 1 statement (with no SQL at all) to sum up all the file sizes. Though if you want, you may be able to use HQL like so:
def user = User.get(id)
def sum = File.executeQuery("select sum(f.size) from File f where f.user = ?", user);
But I think you'd need to add a belongsTo to File to do that. There's probably a simpler HQL/GORM method that's just not coming to my mind at the moment, but honestly, I don't think there's anything easier than what you already did.

You can do it with an HQL query, but it has a small problem with the name of the 'size' field
(appears to be a reserved word). If you rename it to 'length', 'fileSize', etc. then this will work:
User.executeQuery(
'select sum(file.length) from User u join u.files file where u.id=:userId',
[userId: id])[0]

A more elegant way to sum the file sizes in Groovy code is:
def total = user.files.sum {it.size}
But if you can calculate the file size totals in the query that returns the users, that's the way I'd do it.

Related

rails reference model from variable name

i have some code that uses a string to determine dynamically what model class to use and for what field to find_by() on. however, i'm having a hard time with how to use these variables to get the model instance. specifically, i have
class Item
include MongoMapper::Document
key :my_variable, String
in my code i have
m = "Item"
f = "my_variable"
and i want to be able to
i = m.find_by_my_variable( f )
result = i[f]
any help is appreciated!
Since you're in Rails (judging by the category tag) you can use:
m = m.constantize
to make the string a constant, and then would something like this work for your query?
m.where("#{f} = ?", some_value)
(EDIT) or use send as ismaelga suggested, if you don't want an ActiveRecord::Relation array object
You could do eval(m.capitalize).send("find_by_#{variable_name}", variable_content)
Capitalize it's only to be sure it gets a capital letter.

Avoiding subqueries in HQL using Grails

I have two object, a room type and a reservation. Simplified they are:
class Room {
String description
int quantity
}
class Reservation {
String who
Room room
}
I want to query for all rooms along with the number of rooms available for each type. In SQL this does what I want:
select id, quantity, occupied, quantity-coalesce(occupied, 0) as available
from room left join(select room_id, count(room_id) as occupied from reservation)
on id = room_id;
I'm not getting anywhere trying to work out how to do this with HQL.
I'd appreciate any pointers since it seems like I'm missing something fairly fundamental in either HQL or GORM.
The problem here is your trying to represent fields that are not your domain classes like available and occupied. Trying to get HQL\GORM to do this can be a bit a little frustrating, but not impossible. I think you have a couple options here...
1.) Build your domain classes so that there easier to use. Maybe your Room needs to know about it's Reservations via a mapping table or, perhaps write what you want the code to look like and then adjust the design.
For example. Maybe you want your code to look like this...
RoomReservation.queryAllByRoomAndDateBetween(room, arrivalDate, departureDate);
Then you would implement it like this...
class RoomReservation{
...
def queryAllByRoomAndDateBetween(def room, Date arrivalDate, Date departureDate){
return RoomReservation.withCriteria {
eq('room', room)
and {
between('departureDate', arrivalDate, departureDate)
}
}
}
2.) My second thought is... It's okay to use the database for what it's good for. Sometimes using sql in you code is simply the most effective way to do something. Just do it in moderation and keep it centralized and unit tested. I don't suggest you use this approach because you query isn't that complex, but it is an option. I use stored procedures for things like 'dashboard view's' that query millions of objects for summary data.
class Room{
...
def queryReservations(){
def sql = new Sql(dataSoruce);
return sql.call("{call GetReservations(?)}", [this.id]) //<-- stored procedure.
}
}
I'm not sure how you can describe a left join with a subquery in HQL. INn any case you can easily execute raw SQL in grails too, if HQL is not expressive enough:
in your service, inject the dataSource and create a groovy.sql.Sql instance
def dataSource
[...]
def sql= new Sql(dataSource)
sql.eachRow("...."){row->
[...]
}
I know it's very annoying when people try to patronize you into their way of thinking when you ask a question, instead of answering your question or just shut up, but in my opinion, this query is sufficiently complex that I would create a concept for this number in my data structure, perhaps an Availability table associated to the Room, which would keep count not only of the quantity but also of the occupied value.
This is instead of computing it every time you need it.
Just my $.02 just ignore it if it annoys you.

Grails per-query one-to-one eager fetching

Let's say you have face and nose, and you want to get the nose based on a faceId.
The Grails user guide tells you how to use mapping = { fetch:join } in a domain class to eagerly fetch the nose in one query whenever you fetch the face.
But I don't want to eagerly fetch all the time. I just want to do on this particular case to use one query instead of two. Can this be done on a per-query basis? Is there some way to do something like:
Face.get(faceId, [join:nose])?
Do you mean somthing link taht:
static fetchMode = [nose:'eager']
found here
Update:
You can solve the problem with a criteria query. Here you can set the fetch mode in the query:
import org.hibernate.FetchMode as FM
def c = MyDomain.createCriteria()
def results = c.list {
maxResults(10)
firstResult(50)
fetchMode("aRelationship", FM.EAGER)
}

grails find first

I know this is simple question but taking more time
How to find first record from table in grails .
I need to get only the first record with out knowing the id number .
Is there any method like find :first in grails ?
thanks in advance .
Updating to Grails 2.1.1 or later adds two new methods (first and last) for GORM to address this needed feature.
From the docs:
class Person {
String firstName
String lastName
Integer age
}
// retrieve the first person ordered by the identifier
def p = Person.first()
// retrieve the first person ordered by the lastName property
p = Person.first(sort: 'lastName')
// retrieve the first person ordered by the lastName property
p = Person.first('lastName')
Well, you have to define by what measure this record is supposed to be the "first".
Assuming that you mean the record with the earliest creation timestamp, the easiest and most robust approach would be to add a dateCreated property to your domain class and then querying for the entity with the lowest such date. In fact you don't even have to set the creation date manually, because Grails does this for you (as long as you name the property dateCreated) - see Automatic timestamping in the Grails Documentation.
The HQL query would be something like:
def firstObject = YourClass.find("FROM YourClass ORDER BY dateCreated")
Check out hibernate criteria and projections, e.g:
def location = Location.createCriteria()
def firstRecord = location.list{
maxResults(1)
order("id","asc")//assuming auto increment just to make sure
}[0]
http://grails.org/doc/1.0.3/ref/Domain%20Classes/createCriteria.html
If timestamp doesn't matter, you could try if Daniel's answer without ORDER BY works, i.e.
def firstObject = YourClass.find("FROM YourClass")
You can use the grails findBy methods to return the first result of a query.
-> From the 1.3.7 docs
findBy*
Purpose
Dynamic method that uses the properties of the domain class to allow
the creation of Grails query method expressions that return the first
result of the query
-> from the latest docs
findBy* Purpose
Dynamic method that uses the properties of the domain class to execute
a query returning the first matching result.

How to order by more than one field in Grails?

Is there a way to get a list ordered by two fields, say last and first names?
I know .listOrderByLastAndFirst and .list(sort:'last, first') won't work.
Hates_ criteria answer didn't seem to work for me; putting "last,first" in order will only cause exceptions saying, "Property 'last,first' not found". To order on two fields, you can do the following:
def c = MyDomain.createCriteria()
def results = c.list {
and{
order('last','desc')
order('first','desc')
}
}
This is quite old but helped me in finding a suitable solution. A "cleaner" code example by using withCriteria shortcut:
def c = MyDomain.withCriteria {
and {
order('last', 'desc')
order('first', 'desc')
}
}
This old solution no longer works. Please see mattlary's answer below
You may have to write a custom finder in HQL or use the Criteria Builder.
MyDomain.find("from Domain as d order by last,first desc")
Or
def c = MyDomain.createCriteria()
def results = c.list {
order("last,first", "desc")
}
More complicated ordering criteria, (tested in Grails 2.1.0)
def c = MyDomain.withCriteria {
property {
order('last', 'desc')
}
order('first', 'desc')
}
sorts first by MyDomain.property.last then by MyDomain.first
MyDomain.findAll(sort: ['first': 'desc','last':'desc'])
works with grails-datastore-gorm:6.0.3
I think a criteria is the best bet, but you did the right thing by attempting a finder first. When retrieving domain objects from GORM, the right order to make the attempt is: dynamic finder, criteria, HQL.
This query is working on the basis of first field. When the first field is blank then it is shorted by the second field.
order('last','desc')
order('first','desc')
you can do this
def results=MyDomain.findAll([sort:"last",order:'desc'],[sort:"first",order:'desc']);
this line of code will first sort results from domain class "MyDomain" first by last name and then by first name of the person .
If you were sorting lists on the contents of their items, you would need to implement a comparator which would have some smarts to enable to you decide the sort order based on multiple properties.
Some examples of Groovy-style comparators are shown here
However if the list you are sorting is being returned from a database query, you would be better off sorting it using a CrteriaQuery and sorts on that
I has the same problem.
Since my list is not so big, I use groovy sort, as I want to sort on fields of linked domain:
CalendarData -> Attraction
def listCalendar (Calendar calendar) {
respond CalendarData.where {
calendar == calendar
}.list().sort{ "$it.attraction.type?:' '$it.attraction.name" }
}

Resources