In Rails, how do I delete all the objects in an array? - ruby-on-rails

I am using Rails 5 and I want to delete an array of objects. In a previous thread, I read "destroy_all" was the truth and the light. I had two arrays of objects, which I subtract to get a third array
unused_currencies = all_currencies - currencies_from_feed
unused_currencies.destroy_all
but when using destroy_all I got this error:
NoMethodError: undefined method `destroy_all' for #<Array:0x007feea8878770>

This code will make a single SQL query:
unused_currencies = all_currencies - currencies_from_feed
CurrencyModel.delete(unused_currencies)
where CurrencyModel is the model of your currencies.
You might want to use destroy if you need to run callbacks on the models:
unused_currencies = all_currencies - currencies_from_feed
CurrencyModel.destroy(unused_currencies.map(&:id))
This code will make a number of queries proportional to the number of unused currencies

Destroy_all is for active-record type things.
What exactly are you trying to do? if you just want to get rid of the array, you can override it with
unused_currencies = []
If you're trying to destroy a bunch of active record objects in the array you're going to have to iterate over it and delete each object individually.

destroy_all works for the ActiveRecord::Relation
If you want to clear the array, you can do: unused_currencies = []
If you want to delete each item in the in the array:
unused_currencies.each(&:destroy). This will generate a delete query per item.
To delete all the objects at once (assuming they all belong to the same model. This will blow up in your face if they don't!)
unused_currencies.first.class.destroy_all(unused_currencies.map(&:id))

If you use map, you load in memory all data. I think you can just do:
all_currencies.where.not(id: currencies_from_feed.select(:id)).destroy_all
if all_currencies and currencies_from_feed are ActiveRecord::Relation, this will generate only one request sql.

Related

Ordering a collection by instance method

I would like to order a collection first by priority and then due time like this:
#ods = Od.order(:priority, :due_date_time)
The problem is due_date_time is an instance method of Od, so I get
PG::UndefinedColumn: ERROR: column ods.due_date_time does not exist
I have tried the following, but it seems that by sorting and mapping ids, then finding them again with .where means the sort order is lost.
#ods = Od.where(id: (Od.all.sort {|a,b| a.due_date_time <=> b.due_date_time}.map(&:id))).order(:priority)
due_date_time calls a method from a child association:
def due_date_time
run.cut_off_time
end
run.cut_off_time is defined here:
def cut_off_time
(leave_date.beginning_of_day + route.cut_off_time_mins_since_midnight * 60)
end
I'm sure there is an easier way. Any help much appreciated! Thanks.
order from ActiveRecord similar to sort from ruby. So, Od.all.sort run iteration after the database query Od.all, run a new iteration map and then send a new database query. Also Od.all.sort has no sense because where select record when id included in ids but not searching a record for each id.
Easier do something like this:
Od.all.sort_by { |od| [od.priority, od.due_date_time] }
But that is a slow solution(ods table include 10k+ records). Prefer to save column to sort to the database. When that is not possible set logic to calculate due_date_time in a database query.

Updating the column of object in rails object by its position in the array

I have an array of objects in Rails for some table. I want to update its order field by the position of that object in the array with a single Active Record Query. How can I do that?
I have tried the update_all.(:"order" => ?) but couldn't get the object which is updating in update_all.
Suppose #user (only a reference you can use your corresponding object) is the array of objects (Activerecord) , then to update order field of all users in the array, you would have to run the following query
#user.update_all(order_field: value)
Suppose you have an array of posts, then you can do something like the following
posts.each_with_index do |post, index|
post.update_attributes order: index
end
This will execute a single query for each post. If you want a single query to update all the posts in the array, that is a lot harder to achieve. Options I can think of:
bulk updating
using a stored procedure

In Rails 3.2, how to "pluck_in_batches" for a very large table

I have a massive table Foo from which I need to pluck all values in a certain field, Foo.who.
The array has millions of rows, but only a few thousand different values in the who column.
If the table was smaller of course I'd simply use Foo.pluck(:who)
If I use Foo.find_in_batches do |a_batch| each set is an Array of Foo records, rather than an activerecord collection of Foo records, so I cannot use .pluck() and AFAIK the only way to extract the who column is via something like .map(&:who) that iterates over the array.
Is there a way to pluck the who column from Foo in batches that does not require then iterating over each element of each batch to extract the who column?
In Rails 5 you can use:
Foo.in_batches do |relation|
values = relation.pluck(:id, :name, description)
...
end
Upd: for prevent memory leaks use:
Foo.uncached do
Foo.in_batches do |relation|
values = relation.pluck(:id, :name, description)
...
end
end
Here's a method to get the ids that were retrieved by the in_batches method itself, without need to run another query yourself.
in_batches already runs pluck(:id) under the hood (when load param is false which is the default) and yield the relation object. This relation object is created with where(id: ids_from_pluck).
Is it possible to get the list of ids directly from the relation object via where_values_hash method, without the need to run another query in DB. For example:
Foo.in_batches do |relation|
ids = relation.where_values_hash['id']
end
This should work on both Rails 5.x and 6.x, but it relies on implementation detail of in_batches so it is not guaranteed to work in future.
Try this:
Foo.select(:id, :who).find_in_batches do |a_batch|
...
end

Rails and Arel's where function: Can I call where on objects instead of making a call to the database?

Consider the following:
budget has many objects in its another_collection.
obj has many objects of the same type as object in its another_collection.
budget and some_collection are already declared before the following loop
they've been previous saved in the database and have primary keys set.
some_collection is a collections of objs.
-
some_collection.each do |obj|
another_obj = obj.another_collection.build
budget.another_collection << another_obj
end
budget.another_collection.collect {|another_obj| another_obj.another_obj_id}
=> [1, 2, 3, 4]
some_obj_with_pk_1 = some_collection.where(obj_id: obj.id)
some_obj_with_pl_1.id
=> 1
budget.another_collection.where(another_obj_id: some_obj_with_pk_1.id)
=> []
This shouldn't happen. What is happening is that rails queries the database for any items in another_collection with another_obj_id = 1. Since this collection hasn't been saved to the database yet, none of these items are showing up in the results.
Is there a function or something I can pass to Arel's where method that says to use local results only? I know I can just iterate over the items and find it but it would be great if I didn't have to do that and just use a method that does this already.
You could always use Enumeable#select which takes a block and returns only the elements that the block returns true for. You'd want to make sure that you had ActiveRecord retrieve the result set first (by calling to_a on your query).
records = Model.where(some_attribute: value).to_a
filtered_records = records.select { |record| record.something? }
Depending on your result set and your needs, it is possible that a second database query would be faster, as your SQL store is better suited to do these comparisons than Ruby. But if your records have yet to be saved, you would need to do something like the above, since the records aren't persisted yet

Get specific column from a collection ActiveRecord objects?

How can I extract a list of IDs from a returned ActiveRecord results without re-querying the ID column alone?
For exmaple:
people = People.all
people.get_ids #Returns an array of IDS
My current solution is to loop through people and get the ID manually(which isn't very elegant, IMHO)
You can use pluck method:
e.g. People.pluck(:id)
Refer http://apidock.com/rails/ActiveRecord/Calculations/pluck
Fetching using pluck would be better compared to the process of fetching the same after taking all results.
Hope that helps..
You can use map method
all_ids = people.map(&:id) #[1,2,3,4]
ids = People.all.map(&:id)
This code will return array of ids

Resources