I'm creating a digg-like site using Ruby on Rails that ranks the item (based on this algorithm). I'm using the will-paginate gem list the items in pages.
The problem is, will-paginate only allows me to insert ':order =>' based on the table data. I would like to make will-paginate to sort by a number which is calculated using a function based on different fields on the table (e.g number of votes, age hours).
How can I do that?
According to the comments/documentation in the code, you can use
Post.paginate_by_something
in order to use
Post.find_all_by_something
to get the collection of items you want to use paginate for. The :order option is required to set the order of the items.
So for your problem you can create a custom find method, adding a rank column using your algorithm. Use the :order option to sort on this ranking.
You can also use the paginate method on named scopes. If you make a scope for the ordering you could just do Article.ordered.paginate.
Related
I am using Rails 4. I have a Room model with hour_price day_price and week_price attributes.
On the index, users are able to enter different times and dates they would like to stay in a room. Based on these values, I have a helper method that then calculates the total price it would cost them using the price attributes mentioned above.
My question is what is the best way to sort through the rooms and order them least to greatest (in terms of price). Having a hard time figuring out the best way to do this, especially when considering the price value is calculated by a helper and isn't stored in the database.
You could load all of them and do an array sort as is suggested here, and here. Though that would not scale well, but if you've already filtered by the rooms that are available this might be sufficient.
You might be able to push it back to the database by building a custom sql order by.
Rooms.order("(#{days} * day_price) asc")
I have a Rails Model with a relatively small number of entries (Currently at ~ 300 and will probably never go past 1000).
It currently paginates items on its index page to show 20 results a page.
I have just added Twitter Typeahead to the search field, and I'm using the record's names to supply the autocomplete suggestions. The problem is that as I'm paginating the results, I'm only able to offer suggestions for the 20 items from the current paginated batch.
The only thing I need from each model is its name, and I don't want to load/parse every record as this will undo most of the advantages from pagination.
So how can I retain sensible pagination, but also access the names of all records in an efficient manner?
You could fetch the names separately with pluck.
#names = MyModel.pluck(:name)
Note that in Rails 3 you can only provide 1 column name as argument for pluck.
Pagination usually resorts to LIMIT, so the only way to still retrieve all records, is to do another query.
With pluck you're only retrieving the field that you want from the database, and you won't have the overhead that ActiveRecord brings when you would go through a complete collection of all your models.
I have a database table with some articles (for a website) like so:
Articles:
id title order_id
1 - 1
2 - 4
3 - 3
4 - 2
Now on the webpage I want to use the order_id to order the articles, this works perfectly fine, using ROR active record.
However when I want to update the order_id I would have to update all of the records using this technique, each time a change to the order_id is made. What is a better way of doing this ?
Thanks
You want acts_as_list:
class Article < ActiveRecord::Base
acts_as_list :column => 'order_id'
end
There's no way around updating lots of records when you perform a reordering, but acts_as_list can do all that for you with methods like Article#move_to_top and Article#move_lower.
There are some gems to solve your problem. The Ruby Toolbox has them in the category Active Record Sortables. As the time of writing (March 2017) the top gems in this list are:
act_as_list (Website, GitHub)
This is the most popular choice for managing an ordered list in the database and it is still maintained 10 years after its creation. It will do just what you wanted and manage the numbers of the items. The gem will keep your position field numbers form 1 to n in the correct order. This however means that inserting items in the middle of the list means increasing all of the position values for the list items below it, which can be quite some work for your database.
ranked-model (GitHub)
This gem also manages custom ordered lists for you. However it uses another approach behind the scenes, where your list items get position numbers big and spaced apart across the full range of integer values. This should get you performance benefits if you have large lists and need to reorder the items often. It seems to me that this gem might no longer be maintained though, since the author is now doing Ember.js development, it should work though. Edit: It is still maintained.
sortable (GitHub)
This seems to be the same like act_as_list but with the ability to put your items into multiple list. I'm not really sure if this is a valid use-case since you could just create multiple items. It looks like it was not maintained for a long time and not used by many.
resort (GitHub)
This gem uses a linked list approach, i.e. every database entry gets a pointer to the next entry. This might be a good idea if you need a lot of inserts in the middle of your lists, but seems like a terrible idea for just getting the list of entires or if something goes wrong in the database and the chain breaks. It is quite new, so let's see how it develops.
acts_as_restful_list (GitHub)
This gem is "Just like acts_as_list, but restful". It seems to aim for a nicer API. The company behind it does no longer exist, so I'd rather use act_as_list and deal with its API, which is not too bad anyway.
I have a rails 3 app that displays user submissions in order of how many votes they have. I have a model method called "rank" that calculates the score for each submission. Currently, when listing all the submissions I am using the following in my submissions_controller.rb
def index
#submissions = Submission.all.sort_by(&:rank).reverse
end
However, I want to add pagination to this, and it seems that neither 'will_paginate' or 'kamninari' will work properly here. This is because I need to sort the database columns by rank before paginating. Is there a better way to phrase my query so that pagination could be created with one of these gems, or do you know of a good custom pagination solution?
Thanks!
This ended up working:
#submissions = Submission.all.sort_by(&:rank).reverse
#submissions = Kaminari.paginate_array(#submissions).page(params[:page]).per(25)
Perhaps you could calculate rank in SQL? Or, add a column and save it with the record (e.g. before_save :calculate_rank).
I have a little example Rails app called tickets, which views and edits fictional tickets sold to various customers. In tickets_controller.rb, inside def index, I have this standard line, generated by scaffolding:
#tickets = Ticket.find(:all)
To sort the tickets by name, I have found two possible approaches. You can do it this way:
#tickets = Ticket.find(:all, :order => 'name')
... or this way:
#tickets = Ticket.find(:all).sort!{|t1,t2|t1.name <=> t2.name}
(Tip: Ruby documentation explains that sort! will modify the array that it is sorting, as opposed to sort alone, which returns the sorted array but leaves the original unchanged).
What strategy do you normally use? When might you use .sort! versus the :order => 'criteria' syntax?
Use :order => 'criteria' for anything simple that can be done by the database (ie. basic alphabetical or chronological order). Chances are it's a lot faster than letting your Ruby code do it, assuming you have the right indexes in place.
The only time I could think you should use the sort method is if you have a complex attribute that's calculated at run-time and not stored in the database, like a 'trustworthiness value' based off number of good/bad responses or something. In that case it's better to use the sort method, but be aware that this will screw things up if you have pagination in place (each page will have ITS results in order, but the set of pages as a whole will be out of order).
I specify an order in the ActiveRecord finder or in the model association because sorting using SQL is faster. You should take advantage of the features offered by the RDBMS when you're able to do so.