Using .list() in Grails with other query options - grails

I would like to use the .list call with another query (for example a date range or all of a certain type). Is this possible or do I need to build a custom .find call wrapping all the options in .list like offset and max?

You'll probably want to look at the Dynamic Finders and/or Where Queries sections of the Grails doc. If you want to give a more specific example of what you're trying to accomplish I can better answer your questions.

You can try something like:
filter = { it.datePublished >= 'date1' && it.datePublished <= 'date2' }
def filteredList = Book.list(max: 10, offset: 100).collect(filter)

Related

Use dynamic finders with list of Object IDs

With the given class structure
class MyObject {
Status status;
}
class Status {
Integer id;
}
I want to use dynamic finders to query based on a list of Status ID values. What I want to be able to do is something like this
MyObject.findAllByStatusInList([1,2,3]);
This does not work though because my list needs to be Status objects. I know I can build a criteria to do this, but I just want to know if there is a way to accomplish this with Dynamic Finders?
You can still accomplish this using the dynamic finder.
def statuses = [1, 2, 3].collect { Status.load(it) }
MyObject.findAllByStatusInList(statuses)
load() will create a proxy for you that won't require retrieving the instance from the database as long as you don't access any properties other than id.
You can use the where clause:
MyObject.where {status.id in [1,2,3]}.find()
UPD
Dynamic finders don't support aliasing so Criteria (or DetachedCriteria) is the solution to be used.
In case if Status class is a domain entity you could retrieve a list of them (or load their proxies) from the database and then query MyObjects by the status list.
So I see no other appropriate solution but using Criteria in your case.
You can use this.
List<Integer > statuses = [1, 2, 3]
MyObject.findAllByStatusInList(statuses)

Grails: query or criteria against a string/value pairs map property

Grails gives the possibility of creating simple string/value map properties section "Maps of Objects", first paragraph.
I was wondering, is there a way to later query the domain class (using Gorm dynamic finders, criterias or HQL) using the map property as part of the query (i.e adding a condition for the key X to have the value Y)?
After playing with it a bit and almost give up, I discovered the map syntax to (surprisingly) work in HQL. Assuming the class looks like:
class SomeClass {
Map pairKeyProperty
}
You can build queries that look like the following:
select * from SomeClass sc where sc.pairKeyProperty['someKey'] = 'someValue' and sc.pairKeyProperty['someOtherKey'] = 'someOtherValue'
Pretty neat! I still would prefer to use criterias as they are much cleaner to compose, but they seem to not support the same syntax (or I couldn't find it).
I created a sample app in GitHub:
https://github.com/deigote/grails-simple-map-of-string-value-pairs
It can be visisted at:
http://grails-map-of-string-pairs.herokuapp.com/
The form above uses a cross join. To enforce an inner join use
join sc.pairKeyProperty pk1 on index(pk1) = 'someKey'
where 'someValue' in elements(pk1)

Grails PagedResultList

Is there any way to get a PagedResultList in grails without using criteria? I would like to avoid criteria as they are slightly more complex and make unit testing rather annoying. Code Below
def pagedResultList = MyDomainClass.createCriteria().list(max:10, offset:0)
{ order("id", "asc") }
//Below does not return pagedResultList
def aList = MyDomainClass.list(sort:"id", order:"asc", max:10, offset: 0)
PagedResultList is just used to wrap the results of Criteria-based queries (you can see its use in the source here). If you really want to use it, you could always just invoke the constructor directly, since it will handle any list. Of course the totalCount property (which is probably what you are interested in) will then be unset.
If the idea is to get both the paged results list and the total number of results, I'm not aware of any magic that can get both in one query (even the use of PagedResultList in the source linked above issues two queries).
This seems to break in Grails 2.x because there is no costructor that just takes a list. Compare http://grails.org/doc/2.1.0/api/grails/orm/PagedResultList.html and http://docs.huihoo.com/grails/1.3.7/api/grails/orm/PagedResultList.html

Updating "it" inside a Groovy closure

I have a domain class that is just a list of strings (youtubeLinks).
When saving these links I want to strip out the video ID and save it instead of the entire URL entered on the UI side.
This is what I'm trying (ignore that the regex is flawed)
youtubeLinks.each {
def youtubeRegex = /v=(.*)/
def matcher = ( it =~ youtubeRegex )
it = matcher[0][1]
}
When I save this, it saves the original value of "it". Is there a way to update this reference and have it save properly?
Thanks.
Groovy's each loop is merely an iterator, and as such it neither affects the collection on which it operates, nor returns a value of its own. It's basically equivalent to Java's "advanced for loop (for-each)," only with the convenience of dynamic typing and an implicit loop variable (it). While it can be modified, it's a futile enterprise, as you'd be simply changing a reference to the original value, not the value itself. See this question for more on that.
When you need to modify every element within a collection somehow, the idiomatic Groovy (Grails) solution is to use the collect method. Collect transforms each element via the closure you provide, ultimately returning a new collection ( so, it doesn't actually "modify" anything).
Basically, you'll probably want to do something like this:
def links = '''http://www.youtube.com/watch?v=fl6s1x9j4QQ
http://www.youtube.com/watch?v=tCvMKcNJCAY
'''
assert (links =~ /watch\?v=(.*)/).collect{match -> match[1]} == ["fl6s1x9j4QQ", "tCvMKcNJCAY"]
..Though there are actually a number of ways one could go about such a task in Groovy.
Additionally, Ted Naleid's blog has some nice examples of Groovy pattern matching that you may find useful.
Edit
Here are several ways in which you could abbreviate the solution you submitted:
youtubeLinks = youtubeLinks.collect{link -> (link =~ /\?v=(.*)$/)[0][1]}
or
youtubeLinks = youtubeLinks.collect{link -> link.replaceAll(/^.*\?v=/, "") }
or this (Though it's a little contrived)
youtubeLinks = youtubeLinks.join('\n').replaceAll(/.*\?v=/, '').split()
You were right, it ended up being like
youtubeLinks = youtubeLinks.collect {
def youtubeRegex = /v=(.*)[&]/
def matcher = ( it =~ youtubeRegex )
return matcher[0][1]
}
Thanks, Northover.

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