Complex pagination issue...

I wonder if anyone can give me some direction.

I'm trying to figure out how to handle pagination when I have a very complex way of building up a collection of objects. I've used the old classic pagination before, and I've used will_paginate, but I don't think either will work in my case.

Briefly, I have a Location model that defines a physical location. I then build up a list of @locations by a variety of "filters"... for example, a text-based search query plus a geolocation radial search, plus one or more category selections. Underneath the hood, these are each separate queries that build a list of location objects. I then merge the results into a single instance variable: @locations, which is the array of matched Location objects.

Now, I wanna paginate that.

I suppose one option might be for me to try and build the thing into a single, monster SQL query that I can then add some LIMIT fields to... but for now, let's assume that's not an option. (I shiver at the complexity of it. oy!)

So... is there anything I can do with an existing set? Can will_paginate support that? (I'm thinking no, but I figured I'd ask). I think the classic_pagination plugin can, but I'm not really sure how.

Help?

-Danimal

Never mind.

I was gonna just delete this post as I figured it out, but I'll leave it and post my solution in case it helps anyone else.

Turns out will_paginate can handle this. You just wrap your Array with a WillPaginate::Collection and split it manually into the "windows" needed/desired for pagination.

Here's my code snippit. I start with @locations being the full array of matched locations. Then...

[code]   page = (params[:page] ||= 1).to_i   PAGE_SIZE = 10 # could be defined somewhere global   offset = (page - 1) * PAGE_SIZE   @locations = WillPaginate::Collection.new(page, PAGE_SIZE, "#{@locations.length}").concat(@locations[offset..(offset + PAGE_SIZE - 1)]) [/code]

Then, I just use the normal: <%= will_paginate @locations %> code in my view and voila! Instant pagination! Whee!

"Dang, that was easy." <-- me, about so many things of the Rails persuasion.

-Danimal

Getting the final @locations variable looks complicated to me, is this something you had to code for the solution to work, or is this how you’ve been using will_paginate from the beginning?

If the @locations collection is that complicated to assemble you might also want to introduce some caching on the results. Unless I misunderstand what you're doing it appears that you are going to have to calculate that collection on every page request and then select the appropriate results to return.

It was coded that way because of the application needs. Ultimately, it's that I have a potentially large set of Location objects and the end-user can choose one or more methods of "whittling" that set down (search, geolocation, categories, etc). So I have mulitple, complex finds that AND or OR merge together for a final results set. And that final set is what I need to paginate.

It may be worth a refactor attempt, but I think I might just have to live with it as-is and just do as much performance enhancements as I can... i.e. like caching as Andy suggested.

-Danimal

Quoting Danimal <fightonfightwell@gmail.com>:

Never mind.

I was gonna just delete this post as I figured it out, but I'll leave it and post my solution in case it helps anyone else.

Turns out will_paginate can handle this. You just wrap your Array with a WillPaginate::Collection and split it manually into the "windows" needed/desired for pagination.

Here's my code snippit. I start with @locations being the full array of matched locations. Then...

[code]   page = (params[:page] ||= 1).to_i   PAGE_SIZE = 10 # could be defined somewhere global   offset = (page - 1) * PAGE_SIZE   @locations = WillPaginate::Collection.new(page, PAGE_SIZE, "#{@locations.length}").concat(@locations[offset..(offset + PAGE_SIZE - 1)]) [/code]

Then, I just use the normal: <%= will_paginate @locations %> code in my view and voila! Instant pagination! Whee!

Here is an even simpler way:

[code]     @locations = WillPaginate::Collection.create(page, PAGE_SIZE,          locations.size) do |pager|       pager.replace(locations[pager.offset,             [pager.per_page, locations.size].min])     end [/code]

locations is the full array of locations, @locations is the WillPaginate object.

HTH,   Jeffrey

P.S. not tested or guaranteed to work. I copy and pasted it from working code with editing of variables to match your code.

Jeffrey,

Thanks for the refactor! I like the block style! I'll have to incorporate that. Nice & clean.

-Danimal