views:

31

answers:

2

In the case below an author object has an existing list of 'books'. How do you paginate through this list of books if the controller already has an 'author' object and you do not want to go back to the DB with another request of Book.findAll("from Book as b where b.author=:author", [author:author], [max:10, offset:5])

class Author {
   String name
   static hasMany = [ books:book]
}
class Book {
   String title
   static belongsTo = [ author:Author ]
}
A: 

I admit I am a grails newbie - I reckon I must be missing something.

So after a lot of messing here is my very ugly solution:

def timeline = {
println "timeline[" + params+ "]"
if (params.id) {
  def author= Author.findByName(params.id) 

  def allbooks = author?.books as List

  def max=params.max?:(allbooks.size()>2 ? 2: allbooks.size())
  def offset=params.offset?:(0)

  offset = offset as int
  max = max as int

  def start = offset*max > allbooks.size()-1 ? allbooks.size()-1 : offset*max
  def end = ((offset*max)+max-1) > allbooks.size()-1 ? allbooks.size() -1 : ((offset*max)+max-1)

  def books= allbooks.size() > 0?allbooks[(start)..(end)]:allbooks

  def bookCount = allbooks.size()

  println "max:" + params.max + ""
  println "books.size:" + books.size() + ", bookCount:" + bookCount

  [author, bookCount, books]
  }
Bill Comer
@Bill Comer not a good idea, like Rob said the extra query is fine, let the GORM deal with the caching and handle your database concerns, that is what it is there for
Aaron Saunders
+1  A: 

Hiya Bill.

long time no see!

I would certainly agree with Rob here.

What you will see in the sql logs is multiple queries being made for the author and books in both cases. Grails defaults to lazy loading collections, and so at the statement

def author= Author.findByName(params.id)

You have only loaded the author, not the books.

Adding an extra statement to load the books in the normal paginated way will be more efficient, and more maintainable, as it is cleaner and more obvious what it is doing.

This is the style I normally employ to get this kind of data.

def timeline = {  
    println "timeline[" + params+ "]"  
    if (params.id) {  
    def author= Author.findByName(params.id)   
    def books = Book.withCriteria {  
      eq('author', author)  
      firstResult(5)  
      maxResults(10)  
    }  
    def totalBooks = Book.createCriteria().count {  
      eq(author, author)  
    }  

    ...  
}

Also, (not sure on this), but the size() statement on the books collection will trigger a count query, but may also trigger other queries too, so it pays when you have such a large data set to be specific in what you get GORM to do.

(updated as per Bills comment below)

David Dawson
cheers David. I did need quotes on the line eq('author', author). That certainly looks better than my solution. It still upsets though me not to be able to use author.books directly
Bill Comer