1

Could you please tell me how to do a projection on a Grails domain class?! In my case, I want to get a list of (for example) User's name by their Ids. It means in my method, I pass a list of userId and get a list of user's names. Does dynamic methods of Groovy domain support this feature? At present, I'm using my below function:

public String getUserNamesByIds(String[] ids) {
    StringBuffer names = User.get(Integer.parseInt(ids[0]).getName())
    if(ids.length > 1) {
        (1..ids.length - 1).each{
             names.append(", " + User.get(Integer.parseInt(ids[it])).getName())
        }
    }
    return names.toString()
}

As you see, I just want to get the name (and build the total string). I think it's not good because I have to do many small steps, and perform many queries to database to get the User object. Is there any better method to do this? Thank you so much!

Đinh Hồng Châu
  • 5,300
  • 15
  • 53
  • 90

2 Answers2

7

You can use a Criteria and with projections to get the names of the users given its IDs, doing something like:

public List getUserNamesByIds(String[] ids) {
    def criteria = User.createCriteria()
    return criteria.list {
       projections {
         property("name")
       }
       'in'("id", ids)
    }
}

Then you can convert the List of user names into the a String by doing:

List users = getUserNamesByIds(...)
String concatenatedNames = (users ? users.join(",") : "")

You can read more about criterias and projections here in the Grails user guide.

Maricel
  • 2,089
  • 13
  • 17
  • Thank you so much! But can you help me how to use projection on many properties? I don't find any sample to refer. Thanks again! – Đinh Hồng Châu Mar 18 '11 at 10:27
  • I've used projections only with one property, but I think it is possible to add multiple ones, you can just try it and see what happens :-) Also, check this thread to see if that helps you http://stackoverflow.com/questions/3720060/using-groupproperty-and-countdistinct-in-grails-criteria – Maricel Mar 18 '11 at 16:16
1

That's a terrible idea. The reason is every call to User.get(id) makes a call over the wire across the network. This is very expensive, and even if your database responds in 20 ms doing 1000 of them is going to take 20 seconds--quite a long time for your user to wait. A better idea is to get the set of users you are interested in by some sort of query then iterate over them while they are in memory. Which takes 1 ns or less for each loop invocation.

This implementation is literally one of the largest reasons web apps are slow...

Justin Thomas
  • 5,680
  • 3
  • 38
  • 63
  • So with the set of user that I've fetched from a query, can I perform a projection on that set to get all names, instead of looping through that set to get each name?! Thank you! – Đinh Hồng Châu Mar 17 '11 at 18:05
  • You can by looping through, which is fine if it's in memory and of smallish size. If you have to do this for every web request it's bad, but if you do this in a scheduled job for all users and it doesn't impact performance of the app while it'd running go for it. – Justin Thomas Mar 17 '11 at 19:55