Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope with "WHERE ... LIKE" on a related table

I'm trying to get data from a Postgresql table (table1) filtered by a field (property) of an other related table (table2).

In pure SQL I would write the query like this:

SELECT * FROM table1 JOIN table2 USING(table2_id) WHERE table2.property LIKE 'query%'

This is working fine:

scope :my_scope, ->(query) { includes(:table2).where("table2.property": query) }

But what I really need is to filter with a LIKE operator rather than strict equality. However this is not working:

scope :my_scope, ->(query) { includes(:table2).where("table2.property LIKE ?", "#{query}%") }

As I am getting this error:

PG::UndefinedTable: ERROR: missing FROM-clause entry for table "table2" LINE 1: ...ble2" WHERE "table1"."user_id" = $1 AND (tabl... ^ : SELECT "table1".* FROM "table1" WHERE "table1"."user_id" = $1 AND (table2.property LIKE 'query%') ORDER BY last_used_at DESC

What am I doing wrong here?

like image 381
Iwazaru Avatar asked May 25 '15 12:05

Iwazaru


People also ask

What is ActiveRecord in Ruby on Rails?

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.

How do you get rid of N 1?

You can avoid most n+1 queries in rails by simply eager loading associations. Eager loading allows you to load all of your associations (parent and children) once instead of n+1 times (which often happens with lazy loading, rails' default).

What does where return rails?

where returns an ActiveRecord::Relation (not an array, even though it behaves much like one), which is a collection of model objects. If nothing matches the conditions, it simply returns an empty relation. find (and its related dynamic find_by_columnname methods) returns a single model object.


1 Answers

.includes() usually runs 2 separate queries unless it can find that your conditions forces a single LEFT OUTER JOIN query, but it fails to do so in your case as the references are in a string (see this example).

You can force the single query behaviour by specifing .references(:table2):

scope :my_scope, ->(query) { includes(:table2)
                               .references(:table2)
                               .where("table2.property LIKE ?", "#{query}%") }

Or you can you can just use .eager_load():

scope :my_scope, ->(query) { eager_load(:table2)
                               .where("table2.property LIKE ?", "#{query}%") }
like image 76
Marth Avatar answered Oct 21 '22 10:10

Marth