Rails: Sorting ActiveRecord with hyphens - ruby-on-rails

Friends,
I have a simple query like this one:
ContactForm.where(:is_active => true).order(:name => :asc)
But I want one of my records to appear always first, so I prefixed it with a couple hyphens "--", however, the order clause is not swapping that record to the first place, in fact it's still in the middle, as it wouldn't have those hyphens.
What can be happening?
Thank you.

I suggest you to create another column instead of using that approach. You can use a column named priority with 0 as default.
add_column :contact_form, :priority, :integer, default: 0
Then, you can use this code (assuming that you have flagged the priority record with 1 ):
ContactForm.where(:is_active => true).order(:priority, :name => :asc)

Related

How to check if there is an index on a column through Rails console?

Let's say I do Image.column_names and that shows all the columns such as post_id but how do I check if post_id has an index on it?
There is an index_exists? method on one of the ActiveRecord "connection adapters" classes.
You can use it like on of the following methods:
ActiveRecord::Migration.connection.index_exists? :images, :post_id
ActiveRecord::Base.connection.index_exists? :images, :post_id
If you know the name of the index, instead you'll need to use index_name_exists?
ActiveRecord::Base.connection.index_name_exists? :images, :index_images_on_join_key
As others have mentioned, you can use the following to check if an index on the column exists:
ActiveRecord::Base.connection.index_exists?(:table_name, :column_name)
It's worth noting, however, that this only returns true if an index exists that indexes that column and only that column. It won't return true if you're using compound indices that include your column. You can see all of the indexes for a table with
ActiveRecord::Base.connection.indexes(:table_name)
If you look at the source code for index_exists?, you'll see that internally it's using indexes to figure out whether or not your index exists. So if, like me, their logic doesn't fit your use case, you can loop through these indexes and see if one of them will work. In my case, the logic was thus:
ActiveRecord::Base.connection.indexes(:table_name).select { |i| i.columns.first == column_name.to_s}.any?
It's also important to note, indexes does not return the index that rails automatically generates for ids, which explains why some people above were having problems with calls to index_exists?(:table_name, :id)
The following worked for me:
ActiveRecord::Base.connection.index_exists?(:table_name, :column_name)
For an updated answer, as of Rails 3+ multi-column, uniqueness and custom name are all supported within the #index_exists? method.
# Check an index exists
index_exists?(:suppliers, :company_id)
# Check an index on multiple columns exists
index_exists?(:suppliers, [:company_id, :company_type])
# Check a unique index exists
index_exists?(:suppliers, :company_id, unique: true)
# Check an index with a custom name exists
index_exists?(:suppliers, :company_id, name: "idx_company_id")
Source: Rails 6 Docs

Rails + validates_overlap gem: When to add indexes to the database?

I'm using the validates_overlap gem (https://github.com/robinbortlik/validates_overlap) in a Rails app. Here is the Model code:
validates :start_time, :end_time, overlap: { scope: "device_id", exclude_edges: ["start_time", "end_time"] }
And here is the SQL it triggers:
SELECT 1 AS one FROM "bookings" WHERE
((bookings.end_time IS NULL OR bookings.end_time > '2014-04-11 13:00:00.000000') AND
(bookings.start_time IS NULL OR bookings.start_time < '2014-04-11 16:00:00.000000') AND
bookings.device_id = 20) LIMIT 1
I just want to know if I should be adding an index in my postgres database that covers start_time, end_time and device_id, or something similar? e.g. something like this:
add_index :bookings, [:device_id, :start_time, :end_time], unique: true
Adding the above index to ensure database consistency would make no sense. After all you are validating the Range AND excluding the actual edges (the unique index would check exactly the edges!).
Adding a non unique index to speed up the validation is a good idea. If so you should analyze your data and app queries.
The easiest approach is to simply add a single index for each column. Postgres can still use these for the multicolumn query (see heroku devcenter ).
Only if it really matters (or you do not query the columns in other combinations) a multicolumn index is necessary. If so the device_id should be first in index Rule of thumb: index for equality first—then for ranges.

Order_by multiple fields, including a boolean

I'm trying to order my default_scope in a way that the itens with the boolean important = true show first, and all order by created_at desc.
So, I have the following code:
default_scope order_by(:important => :desc, :created_at => :desc)
But, looks like important field order is ignored.
How can I made it work?
Thanks in advance
EDIT:
I just shift the order of the order params and it works:
default_scope order_by(:created_at => :desc, :important => :desc)
Just that simple.
This is an example on how to sort two columns in rails (probably you might have to slightly modify it to match your requirement), But I think you get the idea
<Model>.all(:order => 'important, created_at')
HTH

Rails find conditions... where attribute is not a database column

I think it's safe to say everyone loves doing something like this in Rails:
Product.find(:all, :conditions => {:featured => true})
This will return all products where the attribute "featured" (which is a database column) is true. But let's say I have a method on Product like this:
def display_ready?
(self.photos.length > 0) && (File.exist?(self.file.path))
end
...and I want to find all products where that method returns true. I can think of several messy ways of doing it, but I think it's also safe to say we love Rails because most things are not messy.
I'd say it's a pretty common problem for me... I'd have to imagine that a good answer will help many people. Any non-messy ideas?
The only reliable way to filter these is the somewhat ugly method of retrieving all records and running them through a select:
display_ready_products = Product.all.select(&:display_ready?)
This is inefficient to the extreme especially if you have a large number of products which are probably not going to qualify.
The better way to do this is to have a counter cache for your photos, plus a flag set when your file is uploaded:
class Product < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :product, :counter_cache => true
end
You'll need to add a column to the Product table:
add_column :products, :photos_count, :default => 0
This will give you a column with the number of photos. There's a way to pre-populate these counters with the correct numbers at the start instead of zero, but there's no need to get into that here.
Add a column to record your file flag:
add_column :products, :file_exists, :boolean, :null => false, :default => false
Now trigger this when saving:
class Product < ActiveRecord::Base
before_save :assign_file_exists_flag
protected
def assign_file_exists_flag
self.file_exists = File.exist?(self.file.path)
end
end
Since these two attributes are rendered into database columns, you can now query on them directly:
Product.find(:all, :conditions => 'file_exists=1 AND photos_count>0')
You can clean that up by writing two named scopes that will encapsulate that behavior.
You need to do a two level select:
1) Select all possible rows from the database. This happens in the db.
2) Within Ruby, select the valid rows from all of the rows. Eg
possible_products = Product.find(:all, :conditions => {:featured => true})
products = possible_products.select{|p| p.display_ready?}
Added:
Or:
products = Product.find(:all, :conditions => {:featured => true}).select {|p|
p.display_ready?}
The second select is the select method of the Array object. Select is a very handy method, along with detect. (Detect comes from Enumerable and is mixed in with Array.)

How to sort Rails AR.find by number of objects in a has_many relationship

How can I write an AR find query to have the results ordered by the number of records in a has_many association?
class User < ActiveRecord::Base
has_many :photos
end
I want to do something like...
User.find(:all, :order => photos.count)
I realize my find is not valid code. Say I have the following data.
User 1, which has 3 photos
User 2, which has 5 photos
User 3, which has 2 photos
I want my find to bring me back the users in the order of...
User 2,
User 1,
User 3
based on the count of of the users photos
The easiest way to achieve this is probably to add a counter cache to that model and then sort by that column.
class Photo < ActiveRecord::Base
belongs_to :user, :counter_cache => true
end
And be sure to add a column to your users table called photos_count.
Then you will be able to...
User.find(:all, :order => 'photos_count')
If you don't want an extra column, you could always ask for an extra column in the returned result set:
User.all(:select => "#{User.table_name}.*, COUNT(#{Photo.table_name}.id) number_of_photos",
:joins => :photos,
:order => "number_of_photos")
This generates the following SQL:
SELECT users.*, COUNT(photos.id) number_of_photos
FROM `users` INNER JOIN `photos` ON photos.user_id = users.id
ORDER BY number_of_photos
If you don't want to add a counter cache column, your only option is to sort after the find. If you :include the association in your find, you won't incur any additional database work.
users = User.find(:all, :include => :photos).sort_by { |u| -u.photos.size }
Note the negative sign in the sort_by block to sort from high to low.
I would advise you not to write direct SQL, since implementations of it may vary from store to store. Fortunately, you have arel:
User.joins(:photos).group(Photo.arel_table[:user_id]).
order(Photo.arel_table[:user_id].count)
Counter cache will help, but you'll need an extra column in the db.
I'd add this as a comment on the top answer, but I can't for some reason. According to this post:
http://m.onkey.org/active-record-query-interface
The User.all(options) method will be deprecated after Rails 3.0.3, and replaced with a bunch of other (handy, chainable) active record type stuff, but it makes it very hard to figure out how to put together the same kind of query.
As a result, I've gone ahead and implemented the counter cache method. This was pretty easy and painless with the exception that you need to remember to update the column information in your migration, otherwise all existing records will have "0."
Here's what I used in my migration:
class AddUserCountToCollections < ActiveRecord::Migration
def self.up
add_column :collections, :collectionusers_count, :integer, :default => 0
Collection.reset_column_information
Collection.all.each do |c|
Collection.update_counters c.id, :collectionusers_count => c.collectionusers.count
end
end
def self.down
remove_column :collections, :collectionusers_count
end
end
In theory this should be faster, too. Hope that's helpful going forward.
Your question doesn't make sense. The :order parameter specifies a column name and an optional ordering direction i.e. asc(ending) or desc(ending).
What is the result that you're trying to achieve?

Resources