Given the following classes:
Simplified example:
class Query {
Institution institution
}
class Institution {
String name
}
With the following parameters being submitted: query.institution.id=20 and query.institution.name=Example
I would like to include only the institution id and ignore the name from being bound to the query's institution instance.
Is it possible with bindData to explicitly include associated instance's that are nested multiple levels like this?
I haven't seen any examples of this, aside from using the prefix for a single level of nesting, and the following does not seem to work:
Simplified example:
bindData(queryInstance, params, [include: [
'institution.id',
]], 'query')
The best practice is to filter your request parameters through a command object, which can then be used to generate any kind of query.
Command cmd = Command.getInstance()
bindData(cmd, request.params)
if (cmd.validate()) Query query = cmd.generateQuery()
This way you get the benefit of binding only to fields you expose on the command object, while validating and transforming incoming data without involving your domain.
Related
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"))
Using ODataController In one to many to many or in many-to-many relationship, how can I GET child of child entities.
For example in the OData 4 Sample Service here:
https://github.com/OData/ODataSamples/blob/master/Scenarios/TripPin/src/webapi/ODataSamples.WebApiService/Controllers/PeopleController.cs
I need to know how to implement something like:
public class PeopleController : ODataController
{
...
[ODataRoute("People({key})/Trips/PlanItems)]
public IQuerable<PlanItems> GetPlanItems([FromODataUri] string key])
}
That means I want to retrieve all PlanItems for a Person.
Unfortunately, all methods implement [ODataRoute("People({key})/Trips({tripId})/PlanItems)] only
The URL convention .../People(key)/Trips/PlanItems is not valid as you can see from the error message if you try "The request URI is not valid. Since the segment 'Trips' refers to a collection, this must be the last segment in the request URI or it must be followed by an function or action that can be bound to it otherwise all intermediate segments must refer to a single resource." So the alternative is using URL .../People(key)/Trips?$expand=PlanItems
I'm trying to get a list of domain objects back, but only those that the user is allowed to see. This is based on what granted role that user has from spring security.
What I'd like to be able to do in my controller is something along the lines of
[reportInstanceList: Report.list(params).sort{it.name}]
but only get the reports where
Report.role = SecurityContextHolder.getContext().getAuthentication().getAuthorities()
I have the Spring security stuff in another service class, but for simplicity's sake, its inline here.
Is there a way to direct GORM to only pull the records where the roles match?
Something like:
def reports = Report.createCriteria().list( params ) {
'in'( "role", SpringSecurityUtils.principalAuthorities )
}
[ reportInstanceList: reports, totalCount: reports.totalCount ]
Should work...
This is what dynamic finders are for. You can use the findAllBy finder to get all the objects with an attribute.
Report.findAllByRoleInList(SecurityContextHolder.getContext().getAuthentication().getAuthorities())
You can also sort at the db level by passing a map of params.
Report.findAllByRoleInList(roles, [sort: 'name'])
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
My Grails app is using the searchable plugin, which builds on Compass and Lucene to provide search functionality. I have two searchable classes, say Author and Book. I have mapped these classes to the search index, so that only certain fields can be searched.
To perform a search across both classes I simply call
def results = searchableService.search(query)
One of the nice features of doing the search across both class simultaneously, is that the results object includes metadata about number of results included, number of results available, pagination details etc.
I recently added a boolean approved flag to the Book class and I never want unapproved books to appear in the search results. One option is to replace the call above with:
def bookResults = Book.search(query + " approved:1")
def authorResults = Author.search(query)
However, I now need to figure out how to combine the metadata for both results, which is likely to be tricky (particularly pagination).
Is there a way to search across Book and Author with a single query, but only return approved books?
Do you want to be able to find authors or do you want to find books with a given author?
If you want to find books with a given author, you can configure your domain classes in the following way:
class Author {
String name
...
static searchable = {
root false
}
}
this will result in excluding the Author from the searchableService.search(query)-result and you'll find field names like $/Book/Author/name in your index. (use luke to examine your index: http://code.google.com/p/luke/).
You can change the name of those fields by configuring a better prefix in your Book-class:
class Book {
String name
Author author
...
static searchable = {
author component: [prefix: 'author']
}
}
this will change the name of the field in the index to bookauthor.
If you now search with searchableService.search(query), you'll find all books where the name of the book or the name of the author contains the search term. You can even restrict the search to a given author by using the authorname:xyz syntax.
If you really would like to mix the search results, I only know the solution you already mentioned: mixing both results with your own code, but I guess it will be hard to mix the scoring of the hits in a good way.
Update to your response: Here's my pagination code...
.gsp:
<div class="pagination">
<g:paginate total="${resultsTotal}" params="[q: params.q]"/>
</div>
controller:
result = searchableService.search(params.q, params)
[
resultList: result.results,
resultsTotal: result.total
]
So if you just merge the results of your two searches and add the result.totals, this could work for you.
I've created a test app and came to the following solution. maybe it helps...
if the property approved only has the states 0 and 1, the following query will work:
def results = searchableService.search(
"(${query} AND approved:1) OR (${query} -approved:0 -approved:1)"
)
I guess this can be reformulated in a better way if you don't use the QueryParser but the BooleanQueryBuilder.
BTW: if you add a method like
String getType() { "Book" }
and
String getType() { "Author" }
To your domains, you can even configure your search to do it like this
def results = searchableService.search(
"(${query} AND approved:1) OR (${query} AND type:Author)"
)