Dynamic MongoDb queries in Grails - grails

I have a domain similar to this in Grails
class User {
static mapWith = "mongo"
ObjectId id
String name
String address
Integer age
}
I am building a search front-end in Grails to query a MongoDb database using the MongoDb plugin. Searching can be done on any of the fields in the database and any field that is not set by the user must be not be used in the query. I.e. no fields shall be compared to null. For example leaving all the fields empty returns all Users but searching for name returns only documents which matches on name.
Initially my queries was simple and I used User.find(new User(params)); in my controller which worked fine. Now I need to be able to also query integer fields using intervals, greater than and less than. I've looked at withCriteria() and to build the query depending on what fields the user have set but so far I've been unsuccessful.
TL;DR
How can I do a query where I don't know which fields the user want to include in the query?

I solved it by using withCriteria() like this:
def c = User.createCriteria()
def users = c.list {
if(params.name)
eq('name', params.name)
if(params.address)
eq('address', params.address)
if(params.age_gt?.isInteger())
gt('age', params.age_gt as Integer)
if(params.age_lt?.isInteger())
lt('age', params.age_lt as Integer)
}

Related

How to bulid a criteria query to get the data of my domain

I know that have to be really easy, but I'm new in grails and I don't find any clear answer. That I want to do is read and get with a criteria query the data that I have in my domain do a search for each parameter.
This is my domain Person:
String name
String surname
String address
String village
String country
This is that I'm trying to do:
def getData = Person.createCriteria()
I can see in the log that I have an object (com.mypackagename.Person: 1.), but not the data that I have in the database. example (myname, mysurname, myaddress, myvillage, mycountry)
I only have one row of data in my database and I want to get the data of every column and do a search for each parameter
Thanks in advance.
Let me show you the code first, then I'll explain it:
class SomeController {
def search() {
def people = Person.withCriteria {
params
.findAll { name, value -> name in ['name', 'surname', 'address', 'village', 'country'] }
.each { name, value -> eq(name, value) }
}
// Do as you please with 'people'; a list of Person instances.
}
}
Ok, so let's say you have a controller method (ex. search()) which receives the query parameters from the user. The parameters would be in the params Map. For example, if the user searches for the name John and the country USA, params would look like this: [name: 'John', country: 'USA']. Even though there are other search parameters available, we won't use them because the user did not specify them.
Within the criteria query, first search for the param key/value pairs which you care about; the searchable properties of Person. Then, for each of those pairs call eq(String propertyName, Object value) to set up the query criteria (the WHERE clause).
Using the example data, Hibernate will generate SQL that looks something like this:
SELECT name, surname, address, village, country
FROM person
WHERE name = 'john' AND country = 'USA'
And that's it!
Note: You will see the same output in the log (ex. com.mypackagename.Person: 1). That's because you're logging personInstance.toString(). So if you want the log entry to look differently, you'll need to override Person.toString() A very easy way to remedy this is to use Groovy's #ToString AST.
For more about creating criteria queries, take a look at my series of articles. I cover criteria queries using SQL terminology.
Try to use:
def persons = Person.createCriteria().list{}
or if you want just one result:
def persons = Person.createCriteria().list {
maxResults 1
}
Moreover please read about using Criteria and Querying with GORM

grails validation when fetching rows

Is it possible to fetch a default value in grails if a column is null? If I were to represent following query via grails domain object then how could I achieve it:
SELECT IFNULL(empsalary,0.00) from Employee;
Domain object:
class Employee{
Integer id,
Float empsalary
static constraints = {
id unique: true, blank:false
empsalary nullable:true
}
}
making empsalary nullable false isn't an option due to existing data
validator on empsalary seems to work when inserting rows but not while data fetch
we can consider writing say getEmpSalary() method on domain and perform check there but there are several other fields we need to do this so trying to avoid massive code changes
If you want a default value to come out of the database without having to code anything into your classes, I suggest you update every row where it is null and set it to 0 in the database. If data is getting inserted from another application and that application is allowing a null value, put a 'DEFAULT 0' on your database column.
Grails also offers an "afterLoad" event which is run when a domain object gets loaded from the database. See the documentation here: http://grails.org/doc/2.3.7/guide/GORM.html.
I think you can do this with HQL:
def salary = Employee.executeQuery('SELECT COALESCE(empsalary, 0.0) FROM Employee')[0]
See this SO Question.
Please try setting Float empsalary = 0.0 in your domain object.

Lucene Search Sorting

I need to search member's by first name and last name, which I have done successfully.
Next thing which I have to do is that member's connection should come first in the list (sorting by connection.), like in Facebook, friends come first in the list and than other users of the community.
I am using grails plugin Searchable. One simple way to do this is to sort the searchListFromSearchable w.r.t. connection's list.
Following is the domain structure.
class Member extends {
String firstName
String lastName
static searchable = {
analyzer "simple"
only = ['firstName', 'lastName']
firstName boost: 5.0
}
static hasMany = [connections: Connection]
}
And Connection class is as follow
class Connection {
String uuid
Member connectedMember
static belongsTo = [member: Member]
}
Is there any lucene way to do this ?
I think you can add the sort process in the collect step or score step in Lucene. I think you get the relationship first, and when search the member, you can check whether the member is in the relationship or not. If the member is in the relationship, you can add score of this doc, such as write your own collector which extend TopFieldDocCollector and add score *= 10f before super.collect() in the collect method .
On of the solution to add friends ids to index. In this case, your search query should have followed form +name:firstName +name:lastName friend:userId^10. The friendId has a bigger boost and will cause friends to a high rank in your query.

Projecting Grails Searchable plugin result set

I have a complex searchable configuration for a domain class and its associated domain classes. when I search for about 200 results (max:200) it takes too long to respond.
in the result set I have all fields (simple or association) specified for search in my domain class. I need to return only an id list and ignore other fields of domain class. is it possible? I want to do this for speeding up my search. this id list will be used for querying another no-sql db. it seems that fetching all of the fields is slowing down my search.
I think you can achieve what you want (let the property be searchable but not return it) by setting the property store to no.
For example:
class MyDomain {
String name
String email
static searchable = {
email index:'analyzed', store:'no'
name index:'analyzed'
}
}
In this domain I say that name and email are indexed and analyzed (so they can be searched) but the email property is not being stored, so it will be null when the object is returned. For other properties check: http://grails.org/Searchable+Plugin+-+Mapping+-+Searchable+Property

Grails: find by one-to-many association with String

I have a following domain class:
class User {
static hasMany = [roles:String]
}
I would like to find every user which has role ROLE_ADMIN. Is there any possibility to do that with dynamic finders? user.findAllByRoles('ROLE_ADMIN') seems to give me an error.
UPDATE: it is quite easy to query association where Class A has a list of class B instances and both A and B are domain classes. But here class A is a domain class and class B is a simple Java string.
The code for querying association containing list of another domain objects would look like this:
`User.findAll { roles { role_name=='ROLE_ADMIN' } }`
What i am looking for is a way to specify the value of a String, for example:
`User.findAll { roles {THIS_VALUE=='ROLE_ADMIN' }}`
UPDATE 2: as far as i have found it is not possible to use criteria with collections of primitive types. It is possible to use HQL though:
User.findAll("from User a where :roles in elements(roles)",[roles:'ROLE_ADMIN'])
However it is not as usefull as a findAll or where query. I cannot chain findAll methods so defining other methods that for example: get ROLE_ADMIN users with username like 'xxx' requires rewriting whole HQL query. Maybe it is possible to express above HQL condition in form of a where expression?
Maybe you can do something like:
if you have already a user list (userList)
def list = userList.findAll { user -> user.roles =~ 'ROLE_ADMIN' }
Hope this help!
I have the same problem How to find records by value in their association property via DetachedCriteria
I made some investigation and, as I found, it's impossible.
The GORM DSL itself doesn't have any method to check that value contains in association.
It conains oly that criterias that are in SQL: AND, OR, IN.
But! You can join association as table in criteria Querying by Association Redux

Resources