How Do I Query In Groovier Way? - grails

I have User class which has a field type, which is in turn a list.
So type might look like : ["a","b"]
I have a another list, say search like ["c","b"] (this is pre-defined)
Now what I want is to search all my User instances such that I can find users type matching with any one of the elements from search.
I came up with a partial solution :
def newQ = User.findAllByUsernameIsNotNull()
newQ.each { eachIndex ->
query = eachIndex.type
result = query.findAll { it == "c" }
if(result.size() > 0 )
{
ans << eachIndex.username
}
}
The above code works, ans list have all User which satisfy my condition. But you can clearly see that in query.findAll line, I'm doing a search only for one element from search. I want to perform search operation for all search element against query(which is User's type field).
How can I achieve that? Apart from my solution are there any easy way to do that?
Thanks in advance.

You could do something like:
def ans = []
search.each{s->
ans += User.findAll("from User u where u.username is not null and ? in elements(u.type)",[s])
}
I can't think of a way to do it in a single query

User.withCriteria {
isNotNull("username")
'in' ("type", search)
}

When searching you want to go to the database as few times as possible since those are usually the most expensive operations. If the User.withCriteria {..} works I'd use that (I'm not as familiar with .withCriteria{}). This would work as well if you still wanted to use the dynamic finders since mockDomain doesn't work with HSQL (again not sure if .withCriteria{} works with mockDomain).
def search = ["c", "b"]
def users = User.findAllByUsernameIsNotNull()
users = users.findAll {it.type.intersect(search)}

Related

How to use Findall(closure) in grails project

I've been trying to add search functionality to my grails project and I'm running into a bit of a snag.
Here's my domain class
class Worker{
String name
}
and here's my controller
package main
class SiteController {
def search()
{
def results = Worker.findAll{
it.name ==~ /.*John.*/
}
[results:results]
}
}
I'm trying to figure out how to use the findAll(closure) function, but I can't find examples anywhere and I can't seam to figure it out via testing either, I just want to find all of the workers by the test criteria I put in the closure.
EDIT 1
I'm having another problem, for some reason whenever I use any special characters in my regex, such as [. * ?] or any of those, my findAll doesn't return anything. If I have a workers whose name is "John Smith" and I do
name ==~ /John Smith/
it works as it should, but if I use any of those special characters such as
name ==~ /John.*/
or even
name ==~ /John S.ith/
it won't work, this is very confusing and the regexs works as they should right outside the findall function too, if you could provide some insight into this that'd be very helpful
The example in the docs show's that you reference the attribute directly:
// Use where criteria (since Grails 2.0)
def results = Person.findAll {
lastName == "Simpson"
}
So applying to your query:
def results = Worker.findAll{
name ==~ /.*John.*/
}
Use LIKE in query instead, Try this:
results = Person.findAllByLastNameLike("%John%")
Grails uses hibernate underneath which uses HQL language which is similar to SQL. Alternatively, you can also run full query
results = Person.findAll("from Person as p where p.lastName LIKE :lastname order by p.lastName",
[lastname: '%John%'])
To look for into HQL queries:
http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/queryhql.html
And it is sad but true, HQL/SQL doesn't support regular expressions, it only supports LIKE clause.

Rails/Mongoid best way to save sortable ordered list

I've got a sortable list of players pulled from my db. Users can rank them in order of their preference.
What's the best way to store that order in the db associated with each player's preference and retrieve player stats back in that same order?
Right now I'm retrieving by #playersdraftlist = #draftlist.map { |id| Player.find(id) }, but this hits the db many times.
So, currently, I have a User model and a Draft model. The draft only has an associated user_id.
1) Would it be better to just store the draftees as a user field instead of a separate model?
2) Is there a better way to store a list like this when I need to retain dupes and order?
Structure:
PlayerDraft model has an associated user_id:
draftees: [
"52f4fd9f52e39bc0c15674ea", #Peyton
"52f4fd9f52e39bc0c15674eb", #Tom
"52f4fd9f52e39bc0c15674ea" #Peyton
],
user_id: "52f581096b657612fe020000"
#draftees = Draft.find(user_id = current_user._id).first.draftees will return the draftees array.
Calling Player.find(#draftees) will remove the duplicates and order by _id.
Edit
Here is my poor solution:
def index
#players = Player.all
if current_user.draft_list.draftees == [""]
#playersdraftlist = ""
else
# Get all picks for current user
#picks = current_user.draft_list.draftees
#draft = Player.find(#picks)
#playersdraftlist = #picks.map { |id| Player.find(id) }
end
end
The way to do this is to use the identity map in mongoid.
There are more details on this here: http://mongoid.org/en/mongoid/docs/identity_map.html
But the short and simple way to do this is in your mongoid.yml set the following property:
identity_map_enabled: true
Then search for the following: Player.find(#draftees)
And this will return your draftees in the order of the array that you passed in. One caveat is that it won't return duplicates so your #draftees array above if used would return only two values (the order is based on the first appearance of the id, so it will return the player with id "52f4fd9f52e39bc0c15674ea" first and then the player with id "52f4fd9f52e39bc0c15674eb"). So you will have to recreate the duplicates via some sort of abstraction.
Here is one possible way to do this, and it was inspired by a comment posted by #muistooshort below:
In your User class add the following function
def get_draftee_list
players = Player.find(self.draftees).each_with_object({}) { |p, h| h[p.id] = p }
return self.draftees.map { |id| players[Moped::BSON::ObjectId.from_string(id)] }
end
Note: This was tested on Mongoid 3
This should solve your problem since each call to raw_data.find(id) is searching the result set from your the initial query which is stored in memory and not making another query to the db

Filtering GORM objects based on attribute grails

I have a filter, for example lets assume the name John Doe is who a user is searching for.
I have the following domain object structure: Company--->Employee--->Name. Where employee is an attribute of company, and name is an attribute of employee. Now, the filter is for the name attribute, but I need to keep a reference to the Company. So for example I did:
companyList.events.each {
if(it.employee!=null){
if(it.employee.name.toString().toLowerCase().contains(filter)){
filterSet.add(it)
}
}
Unfortunately, this operation is very slow for 1500 entries. However if I do something like,
def searchResults = Company.findAll{
employee.name == filter
}
It is very fast, but I need the filter to get matched with partial names (i.e. saying Joh would still match John Doe. I know there is an operation called like but I have been unsuccessful using it.
Any help would be really appreciated.
I suggest you look at using criteria. So in this case you could do this:
def filter = 'joh'
def searchResults = Company.createCriteria().list() {
employee {
ilike('name', "%${filter}%")
}
}
Try this:
Company.findAll ("from Company c where c.employee.name like '%'| |:filterName| |'%'", [filterName: yourFilterName])
This query has to be fast.

Grails get any of the children in a hasMany

I have a domain class which has many of another domain class. I want any one of the children and don't care which. Example
class MyDomainClass {
static hasMany = [thingies:OtherDomainClass]
}
I can do this the stupid way like:
def findOne
myInstance.thingies.each{
findOne=it
}
But is there a better way like:
def findOne = myInstance.thingies.grabTheMostConvenientOne()
thingies is a Collection, so you have everything from Collection at your disposal.
A simple way you might do this is:
def one = myInstance.thingies.asList().first()
However, you probably want to make sure the collection actually has some elements first. The documentation doesn't explicitly say that first() throws an IndexOutOfBoundsException if the list is empty, but I have a feeling it still might. If that's the case, you probably want:
def one = myInstance.thingies.size() > 0 ? myInstance.thingies.asList().first() : null
Or, if you want to be super-concise at the expense of some readability, you can use this approach (courtesy John Wagenleitner):
def one = myInstance.thingies?.find { true }

What is the best way to get two parts of a Groovy string?

If I have a string in Groovy like so...
'user.company.name'
... and I want to get the the word "company" and "name" from that string, what is the best way to go about it?
I was thinking of something like this, but I'm not sure if it's the most efficient/groovy way:
def items = 'user.company.name'.tokenize('.')
def company = items[-2]
def name = items[-1]
Is there a better way to do this?
One alternative if you're always looking for the last 2 sections of the string would be to use a regex like this:
'user.company.name'.find(/(.*)\.(.*)\.(.*)/) { full, user, company, name ->
assert user == "user"
assert company == "company"
assert name == "name"
}
One advantage to this is that you don't need to worry about array index exceptions if the string doesn't have the "user.company.name" format. If the regex doesn't match, the closure won't be executed.
You could also use split() that will return an array of String. tokenize() returns a List so I would use it if I need to iterate through the tokens.
items = 'user.company.name'.split('\\.')
company = items[1]
name = items[2]
See the link here for the description of those 2 String methods

Resources