Method to find inside of relation

Hey, I ran into this problem fairly recently. I needed to get a large number of records and search them over and over again on different attributes. I found myself writing code that looked a lot like this.

records = Model.where(…)

records.select { |r| r.attribute1 == attribute1 && r.attribute2 == attribute2 && … }

Although this is a simple case, I found myself rewriting this over and over again.

Is there anything equivalent that might fit the form

records = Model.where(…)

records.find_in_relation(attribute1: attribute1, attribute2: attribute2, attribute3, …)

Was just wondering if there was a place for such syntactical sugar or a desire to add some. If there is too simple a change to warrant, I understand.

You should be able to use find_by and pass in a hash with attributes and respective values to find a record.

Please take a look at the docs for find_by on the api guides.

Carlos Antonio da Silva - via celular

If attribute1, attribute2, etc are database columns then I think `where` is the same as your `find_in_relation`…

records = Model.where(...)
records.where(attribute1: attribute1, attribute2: attribute2, attribute3, …)

Can you explain further what `find_in_relation` is intended to do differently?

—Matt Jones

Hey everyone,

I’ve obviously done a bad job of explaining this :). Let me try to elaborate.

Suppose I’ve ran xyz = Model.where(attribute: true)
A SQL query is created and executed. The results are saved to xyz as an ActiveRecord_Relation. The query looks something like this:

SELECT “models”.* FROM “models” WHERE “models”.“attribute” = ‘t’

If I run xyz.find_by(attribute2: false), a new SQL query is created and executed and it will hit the database again. The query looks something like this:

SELECT “models”.* FROM “models” WHERE “models”.“attribute” = ‘t’ AND “models”.“attribute2” = ‘f’ LIMIT 1

On the other hand, the proposed find_in_relation is a method on an ActiveRecord_Relation that doesn’t hit the database and searches the relation in memory.

If I type xyz.find_in_relation(attribute2: false), no new sql query is created and it will not touch the database. The code would be equivalent to running:

xyz.select { |x| x.attribute2 == false }

Ideally, find_in_relation would return another ActiveRecord_Relation.

Sunny Juneja

Hey everyone,

I’ve obviously done a bad job of explaining this :). Let me try to elaborate.

Suppose I’ve ran xyz = Model.where(attribute: true)
A SQL query is created and executed. The results are saved to xyz as an ActiveRecord_Relation. The query looks something like this:

SELECT “models”.* FROM “models” WHERE “models”.“attribute” = ’t’

This is a place that people frequently get confused - doing this only runs the query in the REPL, where the result has inspect called on it for printing.

Try this in the REPL to see the difference:

xyz = Model.where(attribute: true); nil

This will set xyz but not trigger the query. Any subsequent code that attempts to access elements of xyz will trigger the query - so to_a, or each, etc.

Note that some methods will trigger a different query - for instance, calling xyz.first will perform a query with LIMIT 1 if the records haven’t been loaded yet.

If I run xyz.find_by(attribute2: false), a nw SQL query is created and executed and it will hit the database again. The query looks something like this:

SELECT “models”.* FROM “models” WHERE “models”.“attribute” = ‘t’ AND “models”.“attribute2” = ‘f’ LIMIT 1

On the other hand, the proposed find_in_relation is a method on an ActiveRecord_Relation that doesn’t hit the database and searches the relation in memory.

If I type xyz.find_in_relation(attribute2: false), no new sql query is created and it will not touch the database. The code would be equivalent to running:

xyz.select { |x| x.attribute2 == false }

Ideally, find_in_relation would return another ActiveRecord_Relation.

Relation is intended to build queries. The results of in-memory filtering are not going to make that possible, in general, short of flattening everything to a giant id IN (?) clause.

The only use case I can see for something like this is when some of the attributes you’re filtering by aren’t DB columns but instead are model methods. For that case, select seems

to make the in-memory filtering aspect clearer.

—Matt Jones