Newbie question on grails "createCriteria" - grails

I am new to Grails criteria builder, can someone please explain what does the following mean?
def c = Account.createCriteria()
def results = c {
like("holderFirstName", "Fred%")
and {
between("balance", 500, 1000)
eq("branch", "London")
}
maxResults(10)
order("holderLastName", "desc")
}
Does it mean
Select * from account where
holderFirstName like 'fred%' and
(balance between 500 and 1000 **and**
branch='london')
Select * from account where
holderFirstName like 'fred%' and
(balance between 500 and 1000 **or**
branch='london')
If i want to use "or" and "and" together how do I do that?

Your example would execute as:
select * from account
where holderFirstName like 'Fred%'
and balance between 500 and 1000
and branch = 'London'
All top level conditions are implied to be AND'ed together. You could create the same Criteria as:
def c = Account.createCriteria()
def results = c {
like("holderFirstName", "Fred%")
between("balance", 500, 1000)
eq("branch", "London")
maxResults(10)
order("holderLastName", "desc")
}
To get your second query use this Criteria:
def c = Account.createCriteria()
def results = c {
like("holderFirstName", "Fred%")
or {
between("balance", 500, 1000)
eq("branch", London")
}
maxResults(10)
order("holderLastName", "desc")
}
Nest your and / or closures for more complex Criteria.

Your current criteria means "and" in the two conditions of balance and branch. so
Select * from account where holderFirstName like 'fred%' and (balance between 500 and 1000 and branch='london') is correct, just that it will hold maximum 10 results and will be sorted on the basis of "holderLastName" in descending order.
To use and and or together you also need to specify an or block in criteria, so your criteria will look something like
Account.createCriteria()
def results = c {
like("holderFirstName", "Fred%")
and {
between("balance", 500, 1000)
eq("branch", "London")
}
or{
eq("prop1", prop1)
eq("prop2",prop2)
}
maxResults(10)
order("holderLastName", "desc")
}
in this criteria there is an and between balance and branch conditions. and also an or between prop1 and prop2

Related

Grails Criteria distinct doesn't work

In my app I use createCriteria for getting a list according some criteria.
roleMapping contains user.
I use the following code:
def getTeamOfCompany(def company,def offset=0){
def c = roleMapping.createCriteria()
def result = c.list{
eq('company',company)
eq('isCurrentCompany',true)
firstResult offset
maxResults 10
distinct('user')
user{
order "lastname", "asc"
}
}
return result
}
I use the distinct in order to not get the same user twice, but it didn't work.
If I put projections on the distinct I'll get a list of users instead roleMapping

Filtering by value calculated in instance method

I have a House model that has a price attribute which is calculated using an instance method.
Let's say that the price is constantly changing due to market rates, inflation, etc. Thus we need a price_today method to get the current price.
How can I create a named scope (or something) to filter the houses using a maximum price and minimum price?
I tried doing something like this, but I feel it's a little hacky...
def index
#houses = # get houses from DB
if not params[:max_price].nil?
#houses.keep_if { |h| h.price_today <= params[:max_price].to_f }
end
if not params[:min_price].nil?
#houses.keep_if { |h| h.price_today >= params[:min_price].to_f }
end
end
A named scope (or a where clause) would definitely be faster that querying and filtering the instantiated objects.
However, I'm afraid that if price_today does not exist in the DB you won't be able to use them.
You can improve your code, though.
The conditions can be made simpler, and you can use a single call to keep_if.
max = params[:max_price].presence
min = params[:min_price].presence
if max || min
#houses.keep_if do |house|
a = max ? (house.price_today <= max.to_f) : true
b = min ? (house.price_today >= min.to_f) : true
a && b
end
end
If you want, instead of keep_if you can use select.
Another variation:
{ max_price: ->(price, max) { price <= max },
min_price: ->(price, min) { price >= min } }.each { |prop, cond|
#houses.select! { |h| cond.(h.price_today, params[prop].to_f) } unless params[prop].nil?
}
In case of lambda for particular property you can easily add another conditions=)

Grails lower() in query not working

Let's say I have code like this:
def c = Account.createCriteria()
def results = c {
between("balance", 500, 1000)
eq("branch", "London")
or {
like("holderFirstName", "Fred%")
like("holderFirstName", "Barney%")
}
maxResults(10)
order("holderLastName", "desc")
}
I want to use lower() function to transforming data to lower case
def c = Account.createCriteria()
def results = c {
between("balance", 500, 1000)
eq("branch", "London")
or {
like("lower(holderFirstName)", "Fred%")
like("lower(holderFirstName)", "Barney%")
}
maxResults(10)
order("holderLastName", "desc")
}
My code doesn't work. What is the correct syntax? I have a problem with umlauts so I don't want to use ilike
Don't know which lower function you'd like to use but I guess you want to fetch data based on holderFirstName property ignoring the case.
Here you could use ilike, which is an case-insensitive like:
def c = Account.createCriteria()
def results = c.list {
between("balance", 500, 1000)
eq("branch", "London")
or {
ilike("holderFirstName", "Fred%")
ilike("holderFirstName", "Barney%")
}
maxResults(10)
order("holderLastName", "desc")
}
By the way - you missed to call list() on your criteria ...
Update
You could try to add a formula to your domain class like this:
static mapping = {
lowerFirstName formula: "lower(holder_first_name)"
}
and change the property in your criteria to lowerFirstName:
like("lowerFirstName", "fred%") // changed 'Fred%' to 'fred%'
Code is not tested but should work.
To use database functions in a criteria you need to use sqlRestriction() that add's restrictions directly to the generated sql.
def c = Account.createCriteria()
def results = c.list {
...
sqlRestriction("lower(holder_first_name) like '%%'")
}
Note that with this you use your column name, and not attribute name.
If you are trying to compare for case insensitivity, another option is to use ilike for that purpose. Ilike is similar to Like, but its case insensitive. here
If you do not want to use ilike (as added to the question), I think you alternative approach is executeQuery and hql.
Account.executeQuery(" select * from Account where .... or (lower(holderFirstName) = 'Fred%' ...")

Grails query not inList

I'm writing a criteria to make a query, but I can't figure out how to negate an inList criteria...Is there a way to do something like:
def result = c {
not inList('id', ids)
}
Thanks
Your criteria should like this...
def ids = [1, 2, 3];
def c = Foo.createCriteria();
def results = c.list {
not {'in'("id",ids)}
}
The documentation can be found here. I believe this was just added in grails 2.+

Grails Pagination: how to use pagination to restrict the number of rows

I have a query which gives a result of about 100 rows. Here is the code for the controllers
def bandsOneTrack = {
def bands = Band.executeQuery("Select b from Band as b where size(b.tracks) > (select count(t.id) from Band as ba join ba.tracks as t where ba.id = b.id and t.ourContract is not null) and (select count(t.id) from Band as ba join ba.tracks as t where ba.id = b.id and t.ourContract is not null) >= 1" )
render(view: 'bands_list' , model: [ bands : bands ])
}
It gives me the result set of about 100 rows but they are appearing inside a same page.
Now I want to use pagination so that I can restrict it to only 20 rows per page.
What should I do, and also how to use pagination for this.
On your pagination tag check the total parameter. That should be the total number of records. In your case 100 so that the pagination tag can calculate the total number of pages.
Something like this here:
<g:paginate controller="Book" action="list" total="${bookInstanceTotal}" />
You might need to execute your query once to find the total number of records.
def list() {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
def ls = Book.executeQuery("from Book a",[max: params.max, offset: params.offset])
def totalCount = Book.executeQuery("from Book a").size()
[bookInstanceList: ls, bookInstanceTotal: totalCount]
}
If I remember correctly, you add max and offset properties to the model you're passing to your view, it should automatically wire in pagination. At that point, you should be able to use the paginate tag to iterate through your result sets. See the docs here:
http://grails.org/doc/latest/ref/Tags/paginate.html
Grails Criteria Query and pagination params
params.max = params?.max as Integer ?: 10
params.offset = params?.offset as Integer ?: 0
params.sort = params?.sort ?: "email"
params.order = params?.order ?: "asc"
params.filter = params?.filter ?: ""
params.packet = params?.packet ?: ""
def members = Member.createCriteria().list(params)
{
or
{
if(params.filter != ""){
ilike("firstName", "%" + params.filter + "%")
ilike("lastName", "%" + params.filter + "%")
ilike("email", "%" + params.filter + "%")
try {
params.filter as Long
eq("citizenId" , params.filter.toLong())
}catch (e) {
}
ilike("mobile", "%" + params.filter + "%")
}
}
}
def dataMembers = [:]
dataMembers.data = members
dataMembers.totalRecord = members.totalCount
render dataMembers as JSON
Output
{
"data": [
{
"id":1,
"firstName":name
},
{
"id":2,
"firstName":name
}
],
"totalRecord":5
}

Resources