GORM criteria like query on string set - grails

I have a domain as below:
class Event {
String name
Set tags
//.... other properties
static hasMany = [tags: String]
}
Now, I want to implement an query for search for Event using a list of String. The search should support a 'like' based search. i.e if an Event has tag like 'annual meeting', then string 'meeting' should give that event as result.
Can this be achieved using GORM criteria?

Due to https://hibernate.atlassian.net/browse/HHH-869 there is no way to query a collection of value types with Hibernate (which GORM uses)
You must use HQL instead.

Related

HQL Injection/findAll with sorting

In my application, the query is being built by appending the first part(where clause) with the second part(order by) using a separate script like QueryBuilder.groovy and hence the order by part is prone to HQL injection which can't be sanitized by using Named Parameters. Therefore, I want to use findAll to retrieve a set of records by passing it a query and sorting and paging parameters separately. I saw an implementation like this:
domainClass.findAll(query,[namedParams],[max: 10, offset: 5])
When i passed sortColumn and sortDirection as named parameters, sortColumn worked fine but sortDirection didn't work. i need a way to either make sortDirection work as a named parameter or any other way which will combine 'sorting by direction' with the findAll result. Many people have suggested on various forums to just use the parameters directly as part of the query but it is unacceptable for my application as it will expose the query to HQL Injection.
Thanks in advance.
here is an example:
queryString = "FROM BookCatalog b WHERE b.bookNumber = :bookNumber"
this is passed to the QueryBuilder.groovy where something like this happens:
sort = "$params.sortColumn $params.sortDirection"
queryString.order(sort)
public void sort(String query){
this.query = this.query+" order by "+query
}
finally findAll retrieves the list of records:
def list = findAll(queryString,namedParams,queryParams)
so as the logic just appends the sorting parameters to the query string a potential hacker can do something like this:
bookCatalogView?offset=2&max=5&sortColumn=1,2,3 **or 1=1**
or
bookCatalogView?offset=2&max=5&sortColumn=1,2,3;**select * from whatever**
Don't concat strings, it's bad practice.
If you want to create complex queries, consider using createCriteria()
SomeDomainClass.createCriteria().list {
order("propName", "desc")
}
or, if you need more control, in the sessionFactory way:
query = sessionFactory.getCurrentSession().createCriteria(DomainClass.class)
query.addOrder(Order.asc("someField"))
query.addOrder(Order.desc("someotherField"))

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

Grails select domain objects based on an enum value in an enum list property

I'm having trouble selecting items from a list of domain objects based on a value in an enum list.
My domain object looks like this:
class Truck {
static hasMany = [ makes: Make ]
}
where a Make looks like this:
enum Make {
KENWORTH, MACK, VOLVO
}
I'm not really sure how do something like Truck.findByMake(Make.MACK) to give me all of the Trucks that have this Make in their list of Makes. That call gives me this error:
No property found for name [make] for class [class Truck]
Any ideas? Grails 1.2.2.
This one's tricky and not supported by the dynamic finders. I also don't know how to do this with Criteria queries, but the HQL would be
def mackTrucks = Truck.executeQuery(
'select t from Truck t left join t.makes make where make=:make',
[make: Make.MACK])
You can make ist with criteria query the answer is her in the forum but you have to customize it. Maybe like this:
Truck.createCriteria.list ={makes{eq('name', Make.MACK)}
}
I think each Enum has the attribute name.

grails findAll tag

How to use "SELECT id, name, part, description FROM user " in grails findAll tag.
I tried
User.findAll("SELECT id, name, part, description FROM user")
instead using
User.findAll("FROM user")
But shows errors.
What is the tag?
finadAll() returns a Collection of domain objects, so enumerating columns to select does not make sense; the queries it understands are not real SQL, and consist basically only of WHERE clauses. Since you don't seem to want to constrain the result set, this is probably all you need:
User.findAll()
It will return a collection of all User objects. If you need constraints, the syntax ist
User.findAll("from User as u where u.id=?", [userId])
Or, even simpler, you can use a dynamic finder:
User.findAllById(userId);
If you want to run report-style queries like this, use the executeQuery method:
def rows = User.executeQuery("SELECT id, name, part, description FROM User")
The return value will be a List of Object[] where each element in the object array is the type of the column, i.e. the 1st element will be a long, 2nd a String, etc.
Note that User has to be capitalized since you're referring to the Hibernate entity - this isn't a SQL query, it's HQL.
If you want to query for only certain fields, you can use a criteria query with a projection.
Example:
def userProperties = User.withCriteria {
projections {
property('id')
property('name')
property('part')
property('description')
}
}
This query will return an array of Strings (or whatever the database column type is mapped to) for each matching row, instead of a domain object.
It will return an ArrayList of objects you only have to access that objects values. For example:
def result = Code.findAll("from Code as c where c.user_code=?",[pass])
result[0].user_code
Where my Code class is something like this:
class Code {
String user_code
boolean flg_active
static constraints = {
user_code nullable:true, blank:true, size:0..Text.MID
flg_active nullable:true, blank:true, default:1
}
}

Resources