Grails - How to get list of distinct User Objects - grails

Is there a way that, i can get list of distinct User objects(based on username). And still get result as a List of User Objects rather than, List of username's.
My code is
def criteria = User.createCriteria()
def users = criteria.list() {
projections {
distinct("username")
}
setResultTransformer(CriteriaSpecification.ROOT_ENTITY)
}
return users
Currently am getting List of the usernames, not User.

Ya projection is like filtering and selecting by username you should change it to
def criteria = User.createCriteria()
def users = criteria.listDistinct() {
projections {
groupProperty("username")
}
}
return users
JOB DONE!

One of these should work - I haven't tested any of them, I leave that up to you :)
User.list().unique()
User.list().unique() with the equals() method on the User domain class overridden to compare objects using the username
User.list().unique { it.username } (might need toArray() after list())

def criteria = User.createCriteria()
def users = criteria.list() {
projections {
distinct("username")
}
setResultTransformer(CriteriaSpecification.ROOT_ENTITY)
}
Just replace setResultTransformer(CriteriaSpecification.ROOT_ENTITY) with resultTransformer(ALIAS_TO_ENTITY_MAP). You will get a list of string as a result
otherwise just replace .list with .listDistinct and use do not need distinct("username"), just can be property("username");
Usually people get problems with pagination. not results. If you already had something like:
User.createCriteria().list([max:params.max,offset:params.offset],{
createAlias("others", "others", CriteriaSpecification.LEFT_JOIN);
ilike("others.firstName", "%${query}%");
});
It could result in row duplicates. Because .listDistinct() does not support pagination, just add
resultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
So query will look like this:
User.createCriteria().list([max:params.max,offset:params.offset],{
resultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
createAlias("others", "others", CriteriaSpecification.LEFT_JOIN);
ilike("others.firstName", "%${query}%");
});

Where ever you got a collection (list, array, ...) (I don't know if work with any type of collection, but work in all that i could test). Use unique{ it.property }:
Example:
def users = []
for (def room in rooms) {
users.addAll(room.users)
}
return users.unique{ it.id }

Using where query(Detached criteria):
def userListQuery = User.where{
// Your search criteria
}
def userList = userListQuery.list().unique{ it.username }
it should result one query for distinct results.

This will get you the distinct user object based on userName
def userInstance = User.list().unique{ it.user_name}

Related

Grails distinct projection get the result count of distinct items

I am using grails-2.5.6 version. I am using spring-security-core plugin. I have a criteria query on UserRole table. Where I want to find all distinct users by a role. It is working properly.
But the problem is the pagination effect. When I am counting on the list it is counting on UserRole list object. But I need the count on distinct projection items. Here is my attempt below:
def list(Integer max) {
def userInstanceList = UserRole.createCriteria().list(params) {
createAlias('user', 'au')
createAlias('role', 'ar')
projections {
distinct("user")
}
if (params.roleId) {
eq('ar.id', params.getLong("roleId"))
}
}
def totalCount = userInstanceList.totalCount
[userInstanceList: userInstanceList, totalCount: totalCount]
}
Here, totalCount is the number of UserRole list. But I want the distinct projection count.
I would tackle this slightly differently, you want to analyse the users, not the userroles.
So I'd do something like:
List<User> usersWithRole = UserRole.createCriteria().list(params) {
role {
eq('id', params.getLong("roleId"))
}
}*.user
int count = usersWithRole.size()
Unless of course there's hundreds or thousands of users, in which case I wouldn't want to load all of them each time and would revert to SQL.
Is this a custom version of spring security you're using? I've never seen Roles with a 'long' based ID, usually, the key is a String representing the Authority name.
Usually the DBAs see the use of distinct keyword as a code-smell.
In your case I would rather use the User as the main domain object to run the query against and a group by clause:
long id = params.getLong "roleId"
def list = User.createCriteria().list( params ) {
projections{
groupProperty 'userRole.role.id'
}
if( id )
userRole{
role{
eq 'id', id
}
}
}

How to get a list of distinct records with projections in grails?

Is there a way that I can get a list of distinct Order objects (based on customerName) with projections (selected fields only)?
Assuming only the id would be different, I want to fetch orders having unique customerName. Is it possible using projections or any other way?
My code is:
def criteria = Order.createCriteria()
def orders = criteria.list() {
and {
eq("showAddress", true)
like("customerName", "%abcdPqrs%")
}
projections {
distinct("customerName")
property("deliveryAddress")
property("billingAddress")
property("")
}
}
return orders
The above code fetches duplicate (customerName) records from Order, how can I fix this?
If you will see the SQL query generated by GORM, you will find that the distinct will apply on a complete row instead of the customerName. You can enable the logs by putting
logSql = true
in datasource.groovy.
You can try this
def criteria = Order.createCriteria()
def orders = criteria.list() {
and {
eq("showAddress", true)
like("customerName", "%abcdPqrs%")
}
projections {
groupProperty("customerName")
property("deliveryAddress")
property("billingAddress")
property("")
}
}

How do I write a createCriteria in grails which pull only few columns from the table instead of all columns?

How do I write a createCriteria in grails which pull only few columns from the table instead of all columns?
I have a table called Ads. I want to retrieve only columns "Title" , "Price" and "Photo".
def c = Classified.createCriteria()
def records = c.list {
eq('publish_date', '2014-06-06')
}
maxResults(8)
}
Above query retrieves all the records. How to restrict to only few columns?
You can use projections to achieve this - at the simplest
projections {
property('title')
property('price')
property('photo')
}
would cause c.list to return a list of three-element lists, where records[n][0] is the title, records[n][1] is the price etc. If you want to be able to access the properties by name rather than by number then you need to assign aliases and use a result transformer
import org.hibernate.transform.AliasToEntityMapResultTransformer
def c = Classified.createCriteria()
def records = c.list {
eq('publish_date', '2014-06-06')
maxResults(8)
projections {
// first param is the property name, second is the alias definition -
// typically you'd leave them the same but here I make them different
// for demonstration purposes
property('title', 'ttl')
property('price', 'cost')
property('photo', 'picture')
}
resultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
}
Now records will be a list of maps rather than a list of lists, and you can access the projected properties by alias name - records[n].ttl, records[n].cost, etc.
Try this:
def records = Classified.withCriteria {
eq('publish_date', '2014-06-06')
projections {
property('title')
property('price')
property('photo')
}
maxResults(8)
}

gorm projection and loss of metainformation

When using projection on the properties, the result is returned as the list with the elements in the same sequence as that defined in the projections block. At the same time the property names are missing from the list and that is really disadvantageous to the developer as the result would be passed along and the caller needs to know what value belongs to which property. Is there a way to return a map from the Criteria query with property name as the key to the value?
so, the following code:
def c = Trade.createCriteria()
def remicTrades = c.list {
projections {
property('title', 'title')
property('author.name', 'author')
}
def now = new Date()
between('publishedDate', now-365, now)
}
This returns:
[['book1', 'author1']['book2', 'author2']]
Instead I would like it to return:
[[book:'book1', author:'author1'][book:'book2', author:'author2']]
I know I can arrange this way after getting the result but I earnestly feel that the property alias should have been used by the criteria to return a list of map that mimics the result of the SQL query and not a bland list.
Duplicate: Grails queries with criteria: how to get back a map with column?
And the corresponding answer (and solution): https://stackoverflow.com/a/16409512/1263227
Use resultTransformer.
import org.hibernate.criterion.CriteriaSpecification
Trade.withCriteria {
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
projections {
property('title', 'title')
property('author.name', 'author')
}
def now = new Date()
between('publishedDate', now-365, now)
}
Agree with your question reasoning, this really should be part of the core GORM solution. That said, here's my workaround;
def props = ['name','phone']
def query = Person.where {}.projections {
props.each{
property(it)
}
}
def people = query.list().collect{ row->
def cols = [:]
row.eachWithIndex{colVal, ind->
cols[props[ind]] = colVal
}
cols
}
println people // shows [['name':'John','phone':'5551212'],['name':'Magdalena','phone':'5552423']]

How can I avoid redundancy in nearly identically criteria?

Imagine I have the following query:
def result = Test.createCriteria().list(params) {
// image here a lot of assocs, criterias, ...
}
In many cases you need the row count of the query above, e.g. list actions of many controllers.
def resultTotal = Test.createCriteria().list(params) {
// Imagine here a lot of assocs, criterias, ...
// Change to the criteria above is only the projection block
projections { rowCount() }
}
How can I avoid this?
You can:
Extract the Criteria creation to a conditional factory/builder method;
Use named queries;
Use named query parameters (and Groovy code!) to alter the query "on the fly", like:
.
static namedQueries = {
byLocation { Location location ->
if (location) {
... // some more criteria logic
}
}
}
If you aren't paginating the results of the query, you can simply do the following after the first query is invoked:
def resultTotal = result?.size()
For the same set of query being used at many places, I create them as a closure and change its delegate to the criteria in question.
For example :
def query = {
projections{
rowCount()
}
eq('type', myType)
}
def criteria = Customer.createCriteria()
query.delegate = criteria
myType = CustomerType.guest
List records = criteria.list(params) {
query()
}
I use totalCount like this:
def list() {
def myInstanceList = MyInstance.createCriteria().list(params){
eq('name', params.name)
}
[myInstanceList: myInstanceList, myInstanceListTotal: myInstanceList.totalCount]
}

Resources