Rails search condition following order of a sequentially generated collection - ruby-on-rails

An array of IDs are being collected
#valid_ts = []
#valid_ts = #valid_ts << dst_1.id
#valid_ts = #valid_ts << dst_2.id
note : each row shown above is in fact run for each member of a collection (i.e. dst_1.size > 1)
When comes time to query the database
slots = Timeslot.where('id IN (?)', #valid_ts).all
the goal is to generate the collection in the order that these members were added to the collection. Rails defaults to updated_at, otherwise the query has to invoke one of the class's attributes, none of which are useful for the targetted context.
How can the originating order of the array be respected with Rails?

If you are using Mysql you can use field function.
Timeslot.where(id: #valid_ts).order("field(id, #{ids.join ','})")
If you are using Postgres you can use position function. -
Timeslot.where(id: #valid_ts).order("position(id::text in '#{ids.join(',')}')")
But ActiveRecord can perform for both if you are using rails >= 5.2.0 as it is added from that version. Most likely it is also backported in rails 5.0. The pull request and the commit and the documentation.
Timeslot.find(#valid_ts)
Check this SO post for more ideas.

Related

Rails SQL select, doesn't seem to work as expected with the CASE statement

I'm trying to understand how to use the select method in queries to return custom columns.
I'm running Rails 5.2, database is postgresql.
m = Message.all.select("messages.*, CASE WHEN id > 30 THEN TRUE ELSE FALSE END AS above_30")
returns only the messages table with all its columns. How do I get the above_30 column, preferable eager loaded?
The above_30 is there, but Rails will not define an access for it because there is no column by that name in your schema.
You can define one yourself, or use m[:above_30] to access the "raw" attributes of the object as returned from the query.

ActiveRecord: Check Number of Database Calls

For demo purposes, suppose that I have a class called DemoThing with a method called do_something.
Is there a way that (in code) I can check the number of times that do_something hits the database? Is there a way that I can "spy" on active record to count the number of times that the database was called?
For instance:
class DemoThing
def do_something
retVal = []
5.times do |i|
retVal << MyActiveRecordModel.where(:id => i)
end
retVal
end
end
dt = DemoThing.new
stuff = dt.do_something # want to assert that do_something hit the database 5 times
ActiveRecord should be logging each query in STDOUT.
But for the above code, it's pretty obvious that you're making 5 calls for each iteration of i.
Queries can be made more efficient by not mixing Ruby logic with querying.
In this example, gettings the ids before querying will mean that the query isn't called for each Ruby loop.
ids = 5.times.to_a
retVal = MyActiveRecordModel.where(id: ids) # .to_a if retVal needs to be an Array
Sure is. But first you must understand Rails' Query Cache and logger. By default, Rails will attempt to optimize performance by turning on a simple query cache. It is a hash stored on the current thread (one for every active database connection - Most rails processes will have just one ). Whenever a select statement is made (like find or where etc.), the corresponding result set is stored in a hash with the SQL that was used to query them as the key. You'll notice when you run the above method your log will show Model Load statement and then a CACHE statement. Your database was only queried one time, with the other 4 being loaded via cache. Watch your server logs as you run that query.
I found a gem for queries count https://github.com/comboy/sql_queries_count

Rails Active Record to make arithmetic calculation over a select statement

I am trying to calculate values from existing table column and use it on an external variable.
Let my table columns be as : ["id","unit_price","quantity","extras_1","extras_2"]
I am presenting what i want to do in rails using sql command as reference.
SQL Command:
SELECT unit_price*quantity AS "regular_price",
unit_price*quantity-unit_price*quantity*discount AS "price_after_discount"
FROM order_details;
In Rails Active Record Query i tried same as:
OrderDetail.select('unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount')
From the above query.i tried sorting based on derived attributes.it worked perfectly.But i cannot see the derived attribute values by querying.
The output i got is without the derived attributes as:
[#<OrderDetail >,#<OrderDetail >]
But i need output as:
[#<OrderDetail regular_price: 43, price_after_discount: 54>,#<OrderDetail regular_price: 54, price_after_discount: 76>]
I tried below query to sort the data.It sorted the data perfectly:
OrderDetail.select('unit_price,quantity,unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount').order('regular_price desc')
I can access the values using below command:
OrderDetail.select('unit_price,quantity,unit_price*quantity AS extras_1,unit_price*quantity-unit_price*quantity*discount AS extras_2')
above commmand worked because extras_1 and extras_2 are table columns.
But it is working when assigned to existing table column.I need derived attribute to be non-existing table column name.
How can i access a derived attributes values from a record.I can access them by assigning them to an existing table column.But i want the attributes name given how i want irrespective of table columns.
You will not be able to see derived(alias) attributes. But they are present.
OrderDetail.select('unit_price*quantity AS regular_price,unit_price*quantity-unit_price*quantity*discount AS price_after_discount').first.regular_price
Will print regular_price.
What you see in rails console is output of inspect method. inspect method is not implemented to show alias attributes. Hence the confusion arises.
Go through this doc: http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
If an alias was specified, it will be accessible from the resulting
objects:
Model.select('field AS field_one').first.field_one
#=> "value"
Your attributes are perfectly accessible. Just call them! :)
As an additional remark I'd suggest you to use a more modern way of writing the query:
class OrderDetail
scope :with_calculated_prices, -> do
regular_price = (arel_table[:unit_price] * arel_table[:quantity])
price_after_discount = (regular_price - regular_price * arel_table[:discount])
select(regular_price.as('regular_price'), price_after_discount.as('price_after_discount'))}
end
price_after_discount is better defined as
price_after_discount = (regular_price * (1 - arel_table[:discount]))

Access hash by primary key - ruby rails

I thought this could be done trivially but I am unable to do so.
My current code is:
#player_types = squad.player_types
Now I loop and lookup for the id,
params[:player_types].each do |p_type|
#player_types.find(p_type[:id])
end
Why does #player_types.find(p_type[:id]) have to execute the the select query when I look up the server logs, havent I loaded this. Is it because of the lazy evaluation and is there a way to load everything at the start and access it as an index in the hash?
The better way to use arel gem, and to select all record with specific ids is this:
ids = params[:player_types].map {|v| v[:id] }
#player_types.where(#player_types.class.arel_table[:id].in(ids))
If player_types is a table, you can directly select all of the player_types in the params[:player_types] array by passing the array to a where condition.
#player_types = PlayerType.where(id: params[:player_types])

Rails order not working with only

I can't find this documented anywhere but here is my problem: when I query via active record and use "only" the "order" clause is ignored. For example I have a Blog model:
Blog.order('id desc')
returns all the blogs with the highest ID first - as expected but:
Blog.order('id desc').only(:id)
returns only the id's (as expected) but the order clause is completely ignored, the smallest id comes first.
I have tested this with Ruby 1.9.3p327 and both Rails 4.0.0.beta1 and Rails 3.2.13 and I get the same results.
Is this a feature or a bug? To me it's a bug because the Rails crew were trumpeting how find_by_sql is not really needed but in this case it is:
Blog.find_by_sql("select id from blogs order by id desc")
which gives the correct answer.
Try using pluck instead of only. only is used to override portions of the previously formed query chain. As the the docs demonstrate:
Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
results in:
SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
This is because the limit modification will be ignored, since the list passed to only doesn't include :limit.
UPDATE
If you need an actual model object returned instead of an array of id's, use select:
Blog.order('id desc').select('id')

Resources