Hi
I wonder how to work around the problem I have with the pagination gem "Kaminari".
For what I've understood you cant paginate #user = User.all.page(5)?
But what if I have this code and want to paginate that, is it possible or do I need to change the code?
#price = Price.joins(:retailer, :retailer => :profile).
where(['product_id=? AND size_id=?', params[:prod_id], params[:si_id]]).
group(:retailer_id).order("SUM((prices.price * #{params[:amount].to_i}) + profiles.shippingCost)").all
The only thing I receive right now when applying.page(5) to that code is
undefined method `page' for #<Class:0x000001023c4558>
You don't need the .all because the joins call, along with where and group, is returning an array of objects for you that meet your criteria. Remove your .all and call page on the instance variable (which you might want to rename to #pages or something else plural).
Related
I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.
I'm missing something simple - I do not want to access the results of this query in a view.
Here is the query:
#adm = Admin.where({:id => {"$ne" => params[:id].to_s},:email => params[:email]})
And of course when you inspect you get:
#adm is #<MongoMapper::Plugins::Querying::DecoratedPluckyQuery:0x007fb4be99acd0>
I understand (from asking the MM guys) why this is the case - they wished to delay the results of the actual query as long as possible, and only get a representation of the query object until we render (in a view!).
But what I'm trying to ascertain in my code is IF one of my params matches or doesn't match the result of my query in the controller so I can either return an error message or proceed.
Normally in a view I'm going to do:
#adm.id
To get the BSON out of this. When you try this on the Decorated Query of course it fails:
NoMethodError (undefined method `id' for #<MongoMapper::Plugins::Querying::DecoratedPluckyQuery:0x007fb4b9e9f118>)
This is because it's not actually a Ruby Object yet, it's still the query proxy.
Now I'm fundamentally missing something because I never read a "getting started with Ruby" guide - I just smashed my way in here and learned through brute-force. So, what method do I call to get the results of the Plucky Query?
The field #adm is set to a query as you've seen. So, to access the results, you'll need to trigger execution of the query. There are a variety of activation methods you can call, including all, first, and last. There's a little documentation here.
In this case, you could do something like:
adm_query = Admin.where({:id => {"$ne" => params[:id].to_s},:email => params[:email]})
#adm_user = adm_query.first
That would return you the first user and after checking for nil
if #adm_user.nil?
# do something if no results were found
end
You could also limit the query results:
adm_query = Admin.where( ... your query ...).limit(1)
I would like to be able to pull all records from the db:
u = User.all
And then once loaded be able to apply AR methods to the resulting collection:
u.first
Is this possible in rails?
Once you actually query the database, the results become an array instead of an ActiveRecord::Relation. (Though #first would still work fine, since it's a method that also exists on Array).
If you just need a starting point to build an ActiveRecord::Relation though, you can use scoped:
# Doesn't execute a query yet
u = User.scoped
# This now executes a query similar to SELECT * FROM users LIMIT 1
u.first
Note that in Rails 4.0, #all now does the same thing as #scoped (whereas in Rails 3, it returns an array).
Why don't you try it?
User.all doesn't return an AR collection it returns an Array. Get rid of the .all and you will have a working example.
I want to create pagination for a messaging system in which the first page shown contains the oldest messages, with subsequent pages showing newer messages.
For example, if normal pagination for {a,b,c,d,e,f,g,h,i} with 3 per page is:
{a,b,c}, {d,e,f}, {g,h,i}
Then reverse pagination would be:
{g,h,i}, {d,e,f}, {a,b,c}
I plan to prepend the pages so the result is the same as normal pagination, only starting from the last page.
Is this possible with kaminari?
Kaminary.paginate_array does not produce query with offset and limit. For optimization reason, you shouldn't use this.
Instead you can do this:
#messages = query_for_message.order('created_at DESC').page(params[:page]).per(3)
Where query_for_message stands for any query which you use to retrieve the records for pagination. For example, it can be all the messages of a particular conversation.
Now in the view file, you just need to display #messages in the reverse order. For example:
<%= render :collection => #messages.reverse, :partial => 'message' %>
<%= paginate #messages %>
There's a good example repo on Github called reverse_kaminari on github. It suggests an implementation along these lines (Source).
class CitiesController < ApplicationController
def index
#cities = prepare_cities City.order('created_at DESC')
end
private
def prepare_cities(scope)
#per_page = City.default_per_page
total_count = scope.count
rest_count = total_count > #per_page ? (total_count % #per_page) : 0
#num_pages = total_count > #per_page ? (total_count / #per_page) : 1
if params[:page]
offset = params[:page].sub(/-.*/, '').to_i
current_page = #num_pages - (offset - 1) / #per_page
scope.page(current_page).per(#per_page).padding(rest_count)
else
scope.page(1).per(#per_page + rest_count)
end
end
end
All credits go to Andrew Djoga. He also hosted the app as a working demo.
One way to solve this problem would be this one:
Reverse pagination with kaminari?
It does not look very clean nor optimal, but it works :)
Yes, but the method I have come up with isn't exactly pretty. Effectively, you have to set your own order:
Message.page(1).per(3).order("created_at DESC").reverse!
The problem with this approach is twofold:
First the reverse! call resolves the scope to an array and does the query, nerfing some of the awesome aspects of kaminari using AR scopes.
Second, as with any reverse pagination your offset is going to move, meaning that between two repeat calls, you could have exactly 3 new messages send and you would get the exact same data back. This problem is inherent with reverse pagination.
An alternative approach would be to interrogate the "last" page number and increment your page number down towards 1.
I'm pulling my hair out trying to build a little random photo JSON feed using DataMapper/Sinatra. Here's what I have so far..
Photo.favorites.to_json(:methods => [:foo, :bar])
So that works fine. The to_json method is provided in the dm-serializer library. All I want to do is randomize that feed so the photos don't show up in the same order every time. Since DataMapper doesn't have built-in support for random selects, I tried sorting the results, but to_json gets mad because the sort_by turns the DataMapper::Collection into an Array..
Photo.favorites.sort_by{rand}.to_json(:methods => [:foo, :bar])
# wrong argument type Hash (expected Data)
I searched for that error and saw a lot of Rails-related stuff about ActiveRecord and conflicts between competing to_json methods, but nothing really about DataMapper. A lot of people recommended using json_pure instead of the json gem, so I gave that a try by adding require 'json/pure' to my Sinatra app. Now the query above gives me this error instead..
Photo.favorites.sort_by{rand}.to_json(:methods => [:foo, :bar])
# undefined method `[]' for #<JSON::Pure::Generator::State:0x106499880>
I also tried doing the randomization with straight SQL:
def self.random
repository(:default).adapter.query('SELECT * FROM photos WHERE favorite = 1 ORDER BY RAND();')
end
But that doesn't really work for me because it returns Struct objects with attributes, rather than instances of the actual Photo class. This means I can't leverage the handy to_json arguments like :methods.
Lastly I tried using find_by_sql, but I guess the method's been removed from DataMapper?
def self.random
find_by_sql("SELECT * FROM `photos` ORDER BY RAND();")
end
# undefined method `find_by_sql' for Photo:Class
Sheesh! Any thoughts on how to resolve this?
The find_by_sql method was moved to the dm-ar-finders plugin.