SQL error when using pg_search_scope associated_against for a calcuation query with includes scope
Hi,
I have a strange problem. I have a pg_search_scope that searches in associations as well, using associated_against. When I use this scope for a calculation query (e.g. sum) with the includes scope set, then I get an SQL query error. I think I can best explain this using an example:
We have three models: Project, Task and Log. A Task belongs to a project and a Log belongs to a Task (and Project). On the Log model I have the following pg_search_scope defined:
pg_search_scope :search, against: { description: 'A' }, associated_against: { project: { name: 'B' }, task: { name: 'B' } }
I have the following ActiveRecord relation:
@results = Log.includes(task: :project).search('test')
When using this to display all the items. It works fine. However, when I use the same relation to sum something (@results.sum(:something)), then I get the following SQL error:
Log.includes(task: :project).search('test').sum(:id)
(0.6ms) SELECT SUM("logs"."id") FROM "logs" LEFT OUTER JOIN "tasks" "tasks_logs" ON "tasks_logs"."id" = "logs"."task_id" LEFT OUTER JOIN "projects" "projects_tasks" ON "projects_tasks"."id" = "tasks"."project_id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("projects"."name"::text, ' ') AS pg_search_86a5b22b6348a78c634fcf FROM "logs" INNER JOIN "projects" ON "projects"."id" = "logs"."project_id" GROUP BY "logs"."id") pg_search_65e3f8265dfa723d721ade ON pg_search_65e3f8265dfa723d721ade.id = "logs"."id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("tasks"."name"::text, ' ') AS pg_search_27264e2376734ab2733873 FROM "logs" INNER JOIN "tasks" ON "tasks"."id" = "logs"."task_id" GROUP BY "logs"."id") pg_search_c8df7499cbfe4dcae83666 ON pg_search_c8df7499cbfe4dcae83666.id = "logs"."id" WHERE (((setweight(to_tsvector('simple', coalesce("logs"."description"::text, '')), 'A') || setweight(to_tsvector('simple', coalesce(pg_search_65e3f8265dfa723d721ade.pg_search_86a5b22b6348a78c634fcf::text, '')), 'B') || setweight(to_tsvector('simple', coalesce(pg_search_c8df7499cbfe4dcae83666.pg_search_27264e2376734ab2733873::text, '')), 'B')) @@ (to_tsquery('simple', ''' ' || 'test' || ' '''))))
PG::UndefinedTable: ERROR: invalid reference to FROM-clause entry for table "tasks"
LINE 1: ...ects" "projects_tasks" ON "projects_tasks"."id" = "tasks"."p...
^
HINT: Perhaps you meant to reference the table alias "tasks_logs".
: SELECT SUM("logs"."id") FROM "logs" LEFT OUTER JOIN "tasks" "tasks_logs" ON "tasks_logs"."id" = "logs"."task_id" LEFT OUTER JOIN "projects" "projects_tasks" ON "projects_tasks"."id" = "tasks"."project_id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("projects"."name"::text, ' ') AS pg_search_86a5b22b6348a78c634fcf FROM "logs" INNER JOIN "projects" ON "projects"."id" = "logs"."project_id" GROUP BY "logs"."id") pg_search_65e3f8265dfa723d721ade ON pg_search_65e3f8265dfa723d721ade.id = "logs"."id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("tasks"."name"::text, ' ') AS pg_search_27264e2376734ab2733873 FROM "logs" INNER JOIN "tasks" ON "tasks"."id" = "logs"."task_id" GROUP BY "logs"."id") pg_search_c8df7499cbfe4dcae83666 ON pg_search_c8df7499cbfe4dcae83666.id = "logs"."id" WHERE (((setweight(to_tsvector('simple', coalesce("logs"."description"::text, '')), 'A') || setweight(to_tsvector('simple', coalesce(pg_search_65e3f8265dfa723d721ade.pg_search_86a5b22b6348a78c634fcf::text, '')), 'B') || setweight(to_tsvector('simple', coalesce(pg_search_c8df7499cbfe4dcae83666.pg_search_27264e2376734ab2733873::text, '')), 'B')) @@ (to_tsquery('simple', ''' ' || 'test' || ' '''))))
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: invalid reference to FROM-clause entry for table "tasks"
LINE 1: ...ects" "projects_tasks" ON "projects_tasks"."id" = "tasks"."p...
^
HINT: Perhaps you meant to reference the table alias "tasks_logs".
: SELECT SUM("logs"."id") FROM "logs" LEFT OUTER JOIN "tasks" "tasks_logs" ON "tasks_logs"."id" = "logs"."task_id" LEFT OUTER JOIN "projects" "projects_tasks" ON "projects_tasks"."id" = "tasks"."project_id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("projects"."name"::text, ' ') AS pg_search_86a5b22b6348a78c634fcf FROM "logs" INNER JOIN "projects" ON "projects"."id" = "logs"."project_id" GROUP BY "logs"."id") pg_search_65e3f8265dfa723d721ade ON pg_search_65e3f8265dfa723d721ade.id = "logs"."id" LEFT OUTER JOIN (SELECT "logs"."id" AS id, string_agg("tasks"."name"::text, ' ') AS pg_search_27264e2376734ab2733873 FROM "logs" INNER JOIN "tasks" ON "tasks"."id" = "logs"."task_id" GROUP BY "logs"."id") pg_search_c8df7499cbfe4dcae83666 ON pg_search_c8df7499cbfe4dcae83666.id = "logs"."id" WHERE (((setweight(to_tsvector('simple', coalesce("logs"."description"::text, '')), 'A') || setweight(to_tsvector('simple', coalesce(pg_search_65e3f8265dfa723d721ade.pg_search_86a5b22b6348a78c634fcf::text, '')), 'B') || setweight(to_tsvector('simple', coalesce(pg_search_c8df7499cbfe4dcae83666.pg_search_27264e2376734ab2733873::text, '')), 'B')) @@ (to_tsquery('simple', ''' ' || 'test' || ' '''))))
You may wonder why I set includes for a sum query. The reason is that I use the same relation for displaying the items, and I want to show a summarize with statistics below the results. Of course I could build the relation again without the includes, but I prefer to not do this.
I hope this could be easily fixed in pg_search.
Ps. I am running Rails 4.2.0, ruby 2.2.0, pg 0.17.1 and pg_search 0.7.8 (I also tried the master branch).
I have a simpler test case I think and steps to reproduce locally. I get a similar error when using a pg_search_scope associated_against: in conjunction with a regular scope. Test files are here: https://gist.github.com/doits/df3d61e1eca1817b095a
Simply create a database called test (createdb test), drop Gemfile and test.rb into a folder, run bundle install and ruby test.rb.
When you just run one of each scope separately it works (first two assertions), but combined it throws an error (third assertion) where the table reference went wrong.
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: invalid reference to FROM-clause entry for table "houses"
LINE 1: ...h ON "people"."id" = pg_search.pg_search_id WHERE "houses"."...
^
HINT: Perhaps you meant to reference the table alias "houses_people".
Is the same problem or is it a different one?
Your test crashes on the last assertion for me as well. It indeed seems to be the same problem as I have, but I am not absolutely sure.
Yeah, that's what I wanted to show and to reproduce (for the developer of this gem, so he has a test case). It looks like the same error you got.
I think the error is because of the table aliasing in rails on multiple joins. Maybe worth to take a look at.
+1
I have a second (little bit simpler) test case where the same error is triggered even without an associated_against pg_scope but a regular. In this case it even depends on the order of chaining the scopes (one order works, second triggers the error).
https://gist.github.com/doits/215b3b54496ea47254b8
I believe I'm having a similar issue in #246
I have a similar problem. includes and eager_load does not work with search. joins method works, but I need eager loading! What to do?
+1 Getting the same issue. I don't understand why the associated table is aliased in this way - where does the code for this live?
It seems @DmitryKK is right. The problem already occurs when simply using includes or eager_loads. The pg_search_scope definition does not even have to use the option associated_against for this problem to occur. See the updated issue https://github.com/Casecommons/pg_search/issues/330.
@DmitryKK it might be possible to rewrite your query to two queries to work around this problem. See the workaround at https://github.com/Casecommons/pg_search/issues/330.
Isn't it related with #88?