ActiveRecord update_column: How to increase a record value depending on it's current value with just one query - ruby-on-rails

The below hits database twice - how do I reduce it to single query?
#user = User.find(123)
#user.update_column(:views, #user.views + 1)
Something like this:
User.find(123).update_column(:views, self.views + 1)

User.where(id: 123).update_all('views = views + 1')
It will produce the following query:
UPDATE users SET views = views + 1 WHERE users.id = 123

Rails has a dedicated method increment_counter to increment a numeric field by one:
User.increment_counter(:views, 123)
# UPDATE `users` SET `views` = COALESCE(`views`, 0) + 1 WHERE `users`.`id` = 123
COALESCE(`views`, 0) ensures that it also works with NULL.

User.connection.execute(%|
UPDATE users SET views = views + 1 WHERE id = 123
|)

User.find(123).increment(:views)
see
http://apidock.com/rails/ActiveRecord/Base/increment
http://apidock.com/rails/ActiveRecord/Base/increment!

Related

Rails: How to access session parameter / ActiveRecord::StatementInvalid in Orders#create

I am working on a multistep form for an order placement section which uses a session session[:order_params] to store all form inputs before submit.
I need to be able to access a particular parameter (land) from the session in order to query for another resource (shippingservice) when navigating back in the form.
In my orders_controller.rb I have:
#shippingservices = #cart.available_shipping_services.joins(:lands).where(:lands => {:id => params[:id]})
but would need to specify the land.id from the session[:order_params].
When using session[:order_params] I get ActiveRecord::StatementInvalid in Orders#create:
Mysql::Error: Unknown column 'id.ship_to_last_name' in 'where clause': SELECT `shippingservices`.* FROM `shippingservices`
INNER JOIN `zones` ON `zones`.`id` = `shippingservices`.`zone_id`
INNER JOIN `lands_zones` ON `lands_zones`.`zone_id` = `zones`.`id`
INNER JOIN `lands` ON `lands`.`id` = `lands_zones`.`land_id`
WHERE `id`.`ship_to_last_name` = 'Smith'
AND `id`.`ship_to_address` = 'Somewherestreet'
AND `id`.`ship_to_city` = 'Nowheretown'
AND `id`.`ship_to_postal_code` = '99999'
AND `id`.`phone_number` = 'some number'
AND `id`.`shippingservice_id` = '34'
AND `id`.`email` = 'someone#example.tld'
AND `id`.`land_id` = '85'
AND `id`.`ship_to_first_name` = 'John'
AND (weightmin <= 200 AND weightmax >= 200 AND heightmin <= 12 AND heightmax >= 12 AND shippingservices.shippingcarrier = '1') AND (lengthmax >= 210 AND widthmax >= 149)
Since the correct land_id is present I am wondering how to provide only that value to the query.
Thank you in advance!
As per the description mentioned in the post, you want to access a particular key stored in session at a particular key.
Assuming order_params is a hash, you can get land_id using the below mentioned code:
session[:order_params][:land_id]
This will return the value of land_id and thus you can use it in the query.
To set session variable, you can set some data in a controller action
For eg:
app/controllers/sessions_controller.rb
def create
# ...
session[:current_user_id] = #user.id
# ...
end
And read it in another: app/controllers/users_controller.rb
def index
current_user = User.find_by_id(session[:current_user_id])
# ...
end

Activerecord where array with less than condition

I have an array of conditions i'm passing to where(), with the conditions being added one at a time such as
conditions[:key] = values[:key]
...
search = ModelName.where(conditions)
which works fine for all those that i want to compare with '=', however I want to add a '<=' condition to the array instead of '=' such as
conditions[:key <=] = values[:key]
which of course won't work. Is there a way to make this work so it i can combine '=' clauses with '<=' clauses in the same condition array?
One way of doing it:
You could use <= in a where clause like this:
User.where('`users`.`age` <= ?', 20)
This will generate the following SQL:
SELECT `users`.* FROM `users` WHERE (`users`.`age` <= 20)
Update_1:
For multiple conditions, you could do this:
User.where('`users`.`age` <= ?', 20).where('`users`.`name` = ?', 'Rakib')
Update_2:
Here is another way for multiple conditions in where clause:
User.where('(id >= ?) AND (name= ?)', 1, 'Rakib')
You can add any amount of AND OR conditions like this in your ActiveRecord where clause. I just showed with 2 to keep it simple.
See Ruby on Rails Official Documentation for Array Conditions for more information.
Update_3:
Another slight variation of how to use Array Conditions in where clause:
conditions_array = ["(id >= ?) AND (name = ?)", 1, "Rakib"]
User.where(conditions_array)
I think, this one will fit your exact requirement.
You could use arel.
conditions = {x: [:eq, 1], y: [:gt, 2]}
model_names = ModelName.where(nil)
conditions.each do |field, options|
condition = ModelName.arel_table[field].send(*options)
model_names = model_names.where(condition)
end
model_names.to_sql --> 'SELECT * FROM model_names WHERE x = 1 and y > 2'

ROR + Count of all JSON value present in one column

In my rails app, I want to add few values present in one column in the form of key-value pair. I am not getting the way to add them.
(byebug) p #timing_params.data_date_wise
{"2"=>"7", "3"=>"8", "4"=>"9", "5"=>"10", "6"=>"11", "9"=>"", "10"=>"", "11"=>""
, "12"=>"", "13"=>"", "16"=>"", "17"=>"", "18"=>"", "19"=>"", "20"=>"", "23"=>""
, "24"=>"", "25"=>"", "26"=>"", "27"=>"", "30"=>"", "31"=>""}
Controller:
total_hour = 0
total_day_count = #timing_params.data_date_wise.count
puts "total_day_count = #{total_day_count}"
for i in 1..total_day_count
total_hour+= #timing_params.data_date_wise["i"] if #timing_params.data_date_wise["i"].to_i > 0
puts "date : #{#timing_params.data_date_wise['i']}"
end
puts "TotalHour : #{total_hour}"
Another problem I think is - All details are not in sequence so that only values will be calculated. For example count is 22 then as per the data - 23 to 31 will be missed.
Please suggest something...
How about changing your for loop to iterate through the hash like this:
#timing_params.data_date_wise.each do |date,value|
total_hour += value.to_i if value.to_i > 0
end

Rails Nested Query

I have follow query
notes = Note.where('notes.id IN
(
SELECT "notes"."id" FROM "notes"
WHERE "notes"."circle_id" = ?
)
OR notes.id IN
(
SELECT "notes"."id" FROM "notes"
INNER JOIN "circles_dreams"
ON "circles_dreams"."dream_id" = "notes"."dream_id"
WHERE "circles_dreams"."circle_id" = ?
)', #circle.id, #circle.id)
How to simplify this query?
Thanks.
First of all you can collect all needed notes id.
I supposed to think what you already have relations between Note and CirclesDream
note_ids = Note.where(circle_id: #circle.id).pluck(:id) # your first SELECT
dream_ids = CirclesDream.where(id: #circle.id).pluck(:note_id) # your second SELECT
notes_ids = note_ids | dreams_ids # combine them
notes = Note.where(id: notes_ids) # Now your
upd: I've just fixed typo. Changed id to note_id in second request
Try this
note_ids = Note.where('circle_id = ?', #circle.id).pluck(:id)
dream_note_ids = Note.joins(:circle_dreams).where('circle_dreams.circle_id = ?', #circle.id).plunk(:note_id)
notes_ids = note_ids | dream_note_ids
If your circle_dreams table can contain records having note_id = null, then you have to apply join. So i think this will work in your case....

plus 1 to integer record for each record in DB

I need plus some value to integer column for many records in db.
I'm trying to do it in "clean" way:
Transaction.where("account_id = ? AND date > ?", t.account_id, t.date).
update_all("account_state = account + ?", account_offset)
or
Transaction.where("account_id = ? AND date > ?", t.account_id, t.date).
update_all("account_state += ?", account_offset)
i get error:
QLite3::ConstraintException: constraint failed:
UPDATE "transactions" SET account_state = (account_state + ?)
WHERE (account_id = 1 AND date > '2012-03-01') AND (-10000)
But works "dirty" way:
Transaction.where("account_id = ? AND date > ?", t.account_id, t.date).
update_all("account_state = account + #{account_offset}")
Is there any "clean" way to do this?
The second parameter of update_all is not the value of the ?, but the conditions (optional) of the SQL request.
You may try with
Transaction.where("account_id = ? AND date > ?", t.account_id, t.date).update_all(["account_state = account + ?", account_offset])

Resources