`to_a` avoid rerun a query? - ruby-on-rails

I notices in the console an unusual user of to_a method in ActiveRecord_Relation. I'll reproduce it below:
Selecting from a table to the variable a:
a = Crypto::ExchangeQuotation.all
[DEBUG] Crypto::ExchangeQuotation Load (121.1ms) SELECT "crypto_exchange_quotations".* FROM "crypto_exchange_quotations" LIMIT $1 [["LIMIT", 11]]
Calling a:
2.4.1 :003 > a
[DEBUG] Crypto::ExchangeQuotation Load (0.8ms) SELECT "crypto_exchange_quotations".* FROM "crypto_exchange_quotations" LIMIT $1 [["LIMIT", 11]]
a to array:
2.4.1 :004 > a.to_a
[DEBUG] Crypto::ExchangeQuotation Load (0.9ms) SELECT "crypto_exchange_quotations".* FROM "crypto_exchange_quotations"
Calling a again:
2.4.1 :005 > a
=> #<ActiveRecord::Relation [#<Crypto::ExchangeQuotation id: 1, exchange_code: "ARN", base_currency_code: "BRL", currency_code: "BTC", origin: "bitvalor", created_at: "2018-03-15 23:24:06", updated_at: "2018-03-15 23:24:06", buy: 17810.0, sell: 17810.0>,
Calling to_a in the variable, calling it again after is really avoiding rerun the query again? If yes, why? Because what is returned in a still is an ActiveRecord_Relation.

Calling to_a is not mutating base object, but it is duplicating this and returning it as an array. Here look ad code of to_a:
def to_a
records.dup
end
To really change it to array you have to override variable a
a = a.to_a

Related

What the difference between implicit_order_column and default_scope in Rails?

What the difference between:
self.implicit_order_column = 'id'
and
default_scope { order('id ASC') }
self.implicit_order_column lets you use another column then the primary key as the implicit ordering column. This effects the way that methods like .first and .last work:
User.class_eval do
self.implicit_order_column = 'created_at'
end
User.first
# => SELECT "users".* FROM "users" ORDER BY "users"."updated_at" ASC LIMIT $1 [["LIMIT", 1]]
User.last
# => SELECT "users".* FROM "users" ORDER BY "users"."updated_at" DESC LIMIT $1 [["LIMIT", 1]]
Setting self.implicit_order_column = 'id' is utterly pointless since the default is the primary key column anyways. The implicit_order_column is of course not used if you provide an explicit order. It does not actually change any other scopes spawned off the class.
default_scope on the other hand tacks on a default scope to any scopes that you spawn off the class.
irb(main):001:0> User.all
(0.5ms) SELECT sqlite_version(*)
User Load (0.1ms) SELECT "users".* FROM "users" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
irb(main):002:1* User.class_eval do
irb(main):003:1* default_scope { order(id: :asc) }
irb(main):004:0> end
=> [#<Proc:0x00000000043703a8 (irb):3>]
irb(main):005:0> User.all
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
irb(main):006:0>
The difference here is not immediately apparent. But if you consider that in the SQL world a query with no order clause will not return the records in a determinate order (its implementation dependent) and here we are actually getting the records in a determinate order. Om many RDBMS the results will be hard to destinguish as they might return the records in the order they are modified (if they feel like it).
Seems brilliant until you realize how non-intuitive default_scope is and how many bugs it leads to down the line.
irb(main):006:0> User.all.order(:created_at)
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC, "users"."created_at" ASC LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, role: "admin", created_at: "2020-11-08 19:31:31", updated_at: "2020-11-08 19:31:47">]>
irb(main):007:0>
Or this example:
irb(main):001:1* User.class_eval do
irb(main):002:1* default_scope { where(admin: true) }
irb(main):003:0> end
=> [#<Proc:0x0000000002bde460 (irb):2>]
irb(main):004:0> User.new
(0.6ms) SELECT sqlite_version(*)
=> #<User id: nil, role: "visitor", created_at: nil, updated_at: nil, admin: true>
Yikes! default_scope is thus widely considered evil.
See:
https://github.com/rails/rails/pull/34480
https://rails-bestpractices.com/posts/2013/06/15/default_scope-is-evil/
https://www.reddit.com/r/rails/comments/ha6gqj/default_scope_is_evil/

Active Record calculations in Ruby

Could someone please help on how to use 'where' condition in this active record condition or why this throws error in rails 4.2?
p = Project.first
Project Load (34.6ms) SELECT `projects`.* FROM `projects` ORDER BY `projects`.`id` ASC LIMIT 1
=> #<Project id: 1, name: "First Project", created_at: "2015-12-29 16:27:42", updated_at: "2015-12-29 16:27:42">
2.2.1 :031 > p.tasks.sum(:priority)
(26.8ms) SELECT SUM(`tasks`.`priority`) FROM `tasks` WHERE `tasks`.`project_id` = 1
=> 9
2.2.1 :032 > p.tasks.sum(:priority).where(:complete => 0)
(0.2ms) SELECT SUM(`tasks`.`priority`) FROM `tasks` WHERE `tasks`.`project_id` = 1
**NoMethodError: undefined method `where' for 9:Fixnum**
sum returns Fixnum, not AR relation. You need to reverse where and sum order:
p.tasks.where(:complete => 0).sum(:priority)

Why does User.first take more time to query the database for the first time?

When I open the console of an existing application and type in :
2.1.1 :001 > User.first
User Load (17.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: xxxx .... >
typing the query next time :
2.1.1 :002 > User.first
User Load (0.8ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: xxxx .... >
You can see the difference between the time taken by rails to query the database. Is something like caching available in console. How does this behave in running applications. Does it more time or even it is cached, where exactly does it do it.
The database server caches queries in the query cache.
See here for documentation on the mysql database query cache: https://dev.mysql.com/doc/refman/5.1/en/query-cache.html
ActiveRecord also performs query caching:
http://edgeguides.rubyonrails.org/caching_with_rails.html
Both of these may be happening depending on your system configuration.
If you're using rails 4, the database connection is not established straight away, until you make a query so:
2.1.1 :001 > User
=> User(no database connection)
2.1.1 :002 > User.first
User Load (28.8ms)
2.1.1 :003 > User.first
User Load (1.9ms)
2.1.1 :004 > User.last
User Load (2.8ms)
So the initial query is establishing a database connection. Those after are then using the cache (as mentioned in other answer) and already established connection.

Why does not querying for a date return anything?

I have a model with a dep_date field, which is a date (not datetime). When querying the model for records with a given date, nothing is returned. Using RoR 3.2 with SQLite
1.9.3-p327 :019 > Flight.find(62)
Flight Load (0.4ms) SELECT "flights".* FROM "flights" WHERE "flights"."id" = ? LIMIT 1 [["id", 62]]
=> #<Flight id: 62, dep_city_id: 3, arr_city_id: 15, dep_date: "2013-03-28", dep_time: nil, price_per_adult: #<BigDecimal:b7e3c94,'-0.0',9(9)>, price_per_child: #<BigDecimal:b7e3c30,'-0.0',9(9)>, free_seats: nil, flight_status: nil, created_at: "2013-03-14 22:15:48", updated_at: "2013-03-14 22:15:48", arr_date: "2013-04-15", arr_time: nil, c_flight_time: 0, c_flight_distance: 0>
1.9.3-p327 :020 > Flight.where(:dep_date => "2013-03-28")
Flight Load (0.5ms) SELECT "flights".* FROM "flights" WHERE "flights"."dep_date" = '2013-03-28'
=> []
My first guess would be that Rails is interrupting "2013-03-28" as a string and not as a date. I would suggest trying this:
Flight.where(:dep_date => "2013-03-28".to_date)
This was caused by SQLite3. Querying on dates works fine with PostgreSQL.

ActiveRecord::Querying#first returns third member of the collection

[1] pry(main)> User.first
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
=> #<User id: 3, email: "chasity#kiehn.net", encrypted_password: "$2a$10$lqsgKvQuz9iSIu/..FMRJu76H9KNhfVz5x9DmxphC0TK...", reset_password_token: ... >
[2] pry(main)> User.find(1)
User Load (12.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
=> #<User id: 1, email: "admin#example.com", encrypted_password: "$2a$10$pGDcv0/EgiDk4KcZN9pli.evx5Ni1qOoujQD15HgWH8Y...", reset_password_token: ... >
[3] pry(main)> Product.first
Product Load (0.6ms) SELECT "products".* FROM "products" LIMIT 1
=> #<Product id: 1, name: "Ruby on Rails Jr. Spaghetti", created_at: "2012-01-25 10:13:26", updated_at: "2012-01-25 10:13:26", properties: ...>
This is happening in the console and during runtime and only for the User model. What could cause such behaviour?
You haven't specified an order - the database is free to order the results in any way it wants. It could be primary key order, it could be how things are laid out on disc but without an order clause there are no guarantees.
If you want the first record in a specific order, then make sure you ask for it
User.first is the same as User.all.first, there is no specified order, so the DB returns the list of element in any order. If you want to get the User with smallest id, use User.order("id").first.
If your request returns several elements and you want to pick any of them, you may want to use first without specified order.

Resources