Цепочка ActiveRecord :: QueryMethods # из объединений дает неверный SQL, отсутствующие объединения - PullRequest
2 голосов
/ 28 июня 2011

Я использую postgres tsearch в проекте с Rails 3.0.9.Чтобы сделать запрос tsearch, мне нужно включить дополнительный SQL в мое предложение from.Например, скажем, у меня есть эта модель:

class User < ActiveRecord::Base
  has_one :profile
  has_many :memberships
  has_many :groups, :through => :memberships
end

Я хочу выполнить полнотекстовый поиск пользователей и их профилей.Я могу сделать это:

User.joins(:profile).where(
  "(profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query)"
).from(
  "to_tsquery('MYQUERY') as tsearch_query, users")

Это приводит к следующему SQL, и он отлично работает:

"SELECT \"users\".* FROM to_tsquery('MYQUERY') as tsearch_query, users INNER JOIN \"profiles\" ON \"profiles\".\"user_id\" = \"users\".\"id\" WHERE ((profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query))"

Но если я присоединяюсь к другому соединению, я получаю плохой SQL:

User.joins(:profile).where(
  "(profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query)"
).from(
  "to_tsquery('MYQUERY') as tsearch_query, users").joins(:groups)

Вот ошибка:

ActiveRecord::StatementInvalid: PGError: ERROR:  missing FROM-clause entry for table "memberships" at character 108
: SELECT "users".* FROM to_tsquery('MYQUERY') as tsearch_query, users INNER JOIN "groups" ON "groups"."id" = "memberships"."group_id" WHERE AND ((profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query))

В этом запросе должно быть три оператора соединения.пользователи в профили, пользователи в членство и членство в группы.Включено только последнее объединение, поэтому мы получаем ошибку для ссылки на таблицу членства без предварительного присоединения к ней.

Но AR :: Relation знает об обоих соединениях:

irb(main)> _.send(:joins_values)
=> [:profile, :groups]

Я думаюпроблема заключается в добавлении этого вызова из области видимости.Если я вырежу это, я получу оба своих соединения.Например, я могу даже предоставить фиктивный вызов «от» и получить ту же ошибку:

User.joins(:profile).from( "users" ).joins(:groups)

ActiveRecord::StatementInvalid: PGError: ERROR:  missing FROM-clause entry for table "memberships" at character 68
: SELECT "users".* FROM users INNER JOIN "groups" ON "groups"."id" = "memberships"."group_id"

irb(main)> _.send(:joins_values)
=> [:profile, :groups]

Удаление вызова «от», это прекрасно работает:

User.joins(:profile).joins(:groups)

irb(main)> _.to_sql
=> "SELECT \"users\".* FROM \"users\" INNER JOIN \"profiles\" ON \"profiles\".\"user_id\" = \"users\".\"id\" INNER JOIN \"memberships\" ON \"users\".\"id\" = \"memberships\".\"user_id\" INNER JOIN \"groups\" ON \"groups\".\"id\" = \"memberships\".\"group_id\"

Так что яЯ не уверен, как обойти это.

Моя конечная цель - иметь возможность выполнять поиск по пользователю и его профилю, а также ограничивать результаты по группам, в которых находится пользователь.

Ответы [ 2 ]

1 голос
/ 29 июня 2011

Еще одно не очень удачное решение - дождаться рельсов 3.1.

Этот тест добавлен к внутреннему activerecord inner_join_association_test.rb:

def test_from_clause_clobbers_multiple_joins
  result = Author.joins(:posts).from('authors').joins(:categorizations).where(:categorizations => {:id => 1}, :posts => {:id => 1}).to_a
  assert_equal authors(:david), result.first
end

не работает на 3.0.9, но проходит 3.1-rc1

1 голос
/ 28 июня 2011

FWIW, это не хороший ответ, но неплохой обходной путь: я могу заставить это работать, передавая фактический SQL для объединений в User.joins, а не используя имена отношений.Возможно, придется сделать сейчас.

Тем временем, я полагаю, я составлю отчет об ошибке.

...