In my Ruby on Rails app I have a model Message that has a created_at column and a status_updated_at column. Both of them are datetimes.
In order to speed up queries such as Message.where(status: 'delivered', account_id: 11).where('status_updated_at - created_at >= ?', "180 seconds"), I want to add index to the difference between columns status_updated_at and created_at.
I've tried this code:
class AddMessageIndicesForDashboard < ActiveRecord::Migration[5.1]
def change
add_index :messages, [:status, :account_id, :created_at, "(status_updated_at-created_at)"], name: 'dashboard_delivery_time_index'
end
end
which result in PG::UndefinedColumn: ERROR: column "status_updated_at-created_at" does not exist
Strangely, say if I only wanted to add index to status_udpated_at - created_at by doing add_index :messages, "(status_updated_at-created_at)", name: 'dashboard_delivery_time_index', it will work.
Any suggestions? Thanks!
Maybe try
add_index :messages, "(status_updated_at-created_at)", name: 'dashboard_delivery_time_index'
PostgreSQL(9.1) 11.7. Indexes on Expressions.
The syntax of the CREATE INDEX command normally requires writing parentheses around index expressions... The parentheses can be omitted when the expression is just a function call...
Since you want to use an expression rather than a function the extra parentheses should do it according to the docs.
I fixed my problem, you can do add_index :messages, "status, account_id, (status_updated_at-created_at)", name: 'dashboard_delivery_time_index'
Related
I have to run a query where a single column is called twice .
Transaction.where("datetime >= ? && datetime <= ?", params[:date_one], :params[:date_two])
Now while indexing this is the basic we do
add_index :transactions, :datetime
Now my question is can I do something like.....
add_index :transactions, [:datetime, :datetime]
Will it really speedup the search or benefit performance wise. Thanks in advance
You don't have to do that. adding index to column speeds up queries to that column. It does not matter how many times you use this column in your query, only the presence or absence of index matters. Also, you can rewrite your query like this:
Transaction.where("datetime BETWEEN ? AND ?", params[:date_one], :params[:date_two])
I have to create a migration to do a db level validation. The migration:
class DataBaseLevelValidation < ActiveRecord::Migration
def change
add_index :benefits_business_changes, [:benefit_id, :business_change_id], :unique => true
end
end
The problem I have is that when I try to run rake db:migration I have this error:
Index name 'index_benefits_business_changes_on_benefit_id_and_business_change_id' on table 'benefits_business_changes' is too long;
the limit is 62 characters/Users/mariocardoso/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.5/lib/active_record/connection_adapters/abstract/schema_statements.rb:797:in `add_index_options'
But if I change the name to a shorter version I get this:
SQLite3::SQLException: no such table: main.benefits_businessc: CREATE UNIQUE INDEX "index_benefits_businessc_on_benefit_id_and_business_change_id" ON "benefits_businessc"
How can I overcome this problem?
The only ways I see, is to change the 'business_change' model to a shorter name (model, views, migration, ... everything).
There is any way to run this migration without having the error caused by the long name?
You can do
add_index :benefits_business_changes, [:benefit_id, :business_change_id], :unique => true, :name => "a_shorter_name"
A common choice is to use just the first few letters of each column.
I am using hstore_translate within a Rails4 project to handle
my I18n needs.
Assume I have the following model:
class Thingy < ActiveRecord::Base
translates :name
end
with table defined in a migration as
create_table :thingies do |t|
t.hstore name_translations
end
add_index ::thingies, :name_translations, using: :gin
In my Ruby code I wish to retrieve a list of the all the names and ids for Thingies with a name in a specific locale.
Previously, before my Thingy had a localised name, I could just do
thingies = Thingy.order(:name).pluck(:id, :name)
Now I am doing
thingies = Thingy.where("name_translations ? 'en'").order("name_translations -> 'en'").to_a.map do |t|
{id: t.id, name: t.name}
end
But I can't help feeling there's a way I can better leverage Postgres to do this all in one line of code without invoking a loop in Ruby.
I've worked it out with a bit of trial and error.
thingies = Thingy.where("name_translations ? 'en'")
.order("name_translations -> 'en'")
.pluck(:id, ("name_translations -> 'en'"))
does the job.
It's not very DRY but it works.
In my Rails 4 app I have a goal to see all contacts, where field visible_to in contacts table equal to 1. My visible_to is :integer, array: true.
However, I get the following exception:
PG::UndefinedFunction: ERROR: operator does not exist: integer[] = integer
LINE 1: ....* FROM "contacts" WHERE "contacts"."visible_to" IN (1) OR...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.: SELECT "contacts".* FROM "contacts" WHERE "contacts"."visible_to" IN (1) ORDER BY created_at DESC
I searched for answers and as far as I see there is an issue with a type of visible_to. However, I couldn't find the solution. I also tried to get benefit from casts hint, but in vain.
My migration:
class AddVisibleToToContacts < ActiveRecord::Migration
def change
add_column :contacts, :visible_to, :integer, array: true, default: [], using: 'gin'
end
end
Relevant variable from Controller:
#contacts_all_user_sorted = Contact.all.where(visible_to: [1]).order("created_at DESC")
From these two websites:
http://blog.relatabase.com/rails-postgres-arrays
http://adamsanderson.github.io/railsconf_2013/#11
It seems that this syntax should work:
#contacts_all_user_sorted = Contact.all.where("visible_to #> ARRAY[?]", [1])
Does it work?
P.S: As #Quertie pointed out in the comments, you may want to cast the value in the case of a String array, by replacing ARRAY[?] by ARRAY[?]::varchar[]
your migration seems pretty straight forward and correct.
can you please try this:
Contact.where('visible_to IN ?', ['1', '2'])
This answer https://stackoverflow.com/a/973785/1297371 to the question: How to set Sqlite3 to be case insensitive when string comparing?
tells how to create table with "COLLATE NOCASE" column.
My question is how to create such column in a rails migration?
i.e. how from
create table Test
(
Text_Value text collate nocase
);
create index Test_Text_Value_Index
on Test (Text_Value collate nocase);
do:
create_table :tests do
#WHAT HERE?
end
add_index #... WHAT HERE?
I added new migration where I deleted and recreated the index like:
class AddIndexToWordVariations < ActiveRecord::Migration
def change
remove_index :word_variations, :variation_word
add_index :word_variations, :variation_word, :COLLATE => :NOCASE
end
end
where 'word_variations' is my table
For people searching, if 'collate' is still not available in your version of rails (you'll get "Unknown key: :COLLATE"), then you have to create the index manually:
execute("create index Test_Text_Value_Index on Test (Text_Value collate nocase);")
In your 'def up' (the 'drop_table' will drop the index as well in your 'def down')