Active Record Rails 4

Asked

Viewed 241 times

1

If I have a model Evaluation and I do avaliacaoes = Avaliacao.all and then avaliacoes.where("score = ?", 3). In the first command Rails searches all the ratings, what happens in the second command? Rails picks up base ratings or it uses the ratings that are loaded in memory?

  • In Rails 4 o where method accepts a hash: Avaliacao.where(score: 1) - you don’t need all.

2 answers

2

In Rails 4, different from 3, when calling the method all Active Record does not return a Array, but an object child of ActiveRecord::Relation. Once the darlings return this Relation, now we can chain our queries so that they produce only one sql:

Client.all.where("name = ?", "Juliano")
=> Client Load (0.7ms)  SELECT "clients".* FROM "clients" WHERE (name = 'Juliano')

However, doing as you asked, Activerecord will generate two queries, since they are not chained. The valuations object is a Relation, so it will rather make a call to the database.

For it to be single access in the bank, you must chain the two calls. If not possible, you can handle the result already in memory with the method select:

avaliacaoes = Avaliacao.all
avaliacoes.select {|a| a.score == 3}

Errata

As pointed out by @Guigs, part of my reply should be corrected. When you use Relation more than once in the same method, it will only be evaluated at the end, so your code will only do one search in the database, although the calls are not visibly linked (obj.metodo1.metodo2) they will be evaluated only when data is required.

It is only important to note that if you have several conditions, some may be ignored.

avaliacaoes = Avaliacao.all
avaliacoes.where("score = ?", 3)
avaliacoes.where("score = ?", 2)

Will generate

=> Avaliacao Load (0.7ms)  SELECT "avaliacoes".* FROM "avaliacoes" WHERE (score = 2)

Sorry for the mistake, I fell for this because I tested my answer directly on the console, then each evaluation was done immediately. My bad! ;)

  • I asked because I’m doing a recommendation algorithm, and I have 1,800 items in the base and 6,000 reviews that can be positive or negative. I have as a premise that I will have to bring all the data from the database to perform the algorithm. I think it’s best to just do 1 query and use #select to 'segregate' the data. Do you think this is the best approach? Before I was doing Item.all {|item| item.likes ... item.dislikes} and for each item was at least 2 bank queries that gives about 3600 rsrs queries

  • 3600 queries? No doubt it’s better to keep in memory and work the data with selects and maps! D

  • @Juliano, part of your answer is not correct. The way Awkward did it is a chaining and will only generate a query in the database.

  • @Guigs you’re right. I’ll correct the answer.

0

In this your situation I find it more valid to bring the filtered evaluations already, using the .where(condition) that so is generated sql correctly and makes a query only, example:

Avaliacoes.where("score = ?", 3)

For the recommendation, take a look at the method .partition class Enumerable, with it you will receive as return, two arrays, one if the condition passed is true, and another if false, example:

(1..6).partition {|v| v.even? }
#=> [[2, 4, 6], [1, 3, 5]]

Browser other questions tagged

You are not signed in. Login or sign up in order to post.