Rails 3 многие ко многим условиям запроса - PullRequest
2 голосов
/ 04 января 2012

Я пытаюсь установить простое отношение Post / Tags в rails 3. Все работает нормально, кроме случаев, когда я хочу запросить сообщения, связанные с несколькими тегами.По сути, я хотел бы сделать такую ​​вещь:

Post.joins(:tags).where('tags.name ILIKE ?', var)

Но вместо того, чтобы иметь только одну переменную, я бы хотел использовать массив.Я попытался:

Post.joins(:tags).where('tags.name IN (?)', names_array)

Но, к сожалению, он выполняет простое LIKE (не ILIKE) и работает как условие ИЛИ, которое звучит совершенно логично.

Я также нашел другое решение с помощью find_by_sql в этомpost http://snippets.dzone.com/posts/show/32

Но мне это кажется немного уродливым.

Чтобы лучше понять проблему.У меня есть 3 сообщения: PostA PostB PostC

PostA связан с TagA TagB и TagC тегами.PostB связан с тегами TagA и TagB.PostC относится только к TagA.

Если я ищу сообщения TagA и TagC, я бы хотел найти PostA, поскольку он связан с обоими тегами.Использование условия хеширования возвращает PostA PostB и PostC.То, что я хочу, - это сообщения, которые связаны, по крайней мере, со всеми указанными тегами.

Так что у кого-нибудь есть лучший способ справиться с этим?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 06 января 2012

Я не знаю, решит ли это вашу проблему, но для таких сложных запросов я почти всегда просто использую Squeel .

Затем сделайте что-то вроде этого:

@posts = Post.joins(:tags)
  .where{tags.name.like_any names_array}
  .group("post_id")
  .having("count(post_id) = #{names_array.size}")

Надеемся, что SQL выглядит примерно так

SELECT "posts".* FROM "posts"
  INNER JOIN "tags" ON "tags"."post_id" = "posts"."id"
  WHERE (("tags"."name" LIKE "TagA" OR "tags"."name" LIKE "TagB"))
  GROUP BY post_id
  HAVING count(post_id) = 2

Если я помню, squeel довольно хорошо использует ILIKE вместо LIKE в зависимости от используемой базы данных.(по крайней мере, лучше, чем AR)

Также вы можете сделать это, используя AR без squeel , но мне ДЕЙСТВИТЕЛЬНО нравятся некоторые идеи и помощники, которые идут с squeel как _all


Что касается объяснения ...

Предположим, я искал Теги A и B.

Что делает, так это находит все Сообщения с этими тегами.

Таким образом, у вас будет что-то вроде:

  • PostA TagA
  • PostA TagB
  • PostB TagA
  • PostB TagB
  • PostC TagA

Затем он объединит все эти различные результаты Post по объединенным тегам, используя post_id.

  • PostA TagA TagB
  • PostBTagA TagB
  • PostC TagA

Затем он проверит количество тегов в строке SQL, проверив, сколько присутствует forgien_ids.Поскольку A и B имеют 2 тега, вы знаете, что они совпадают со всеми введенными вами.

0 голосов
/ 21 августа 2012

Я застрял на той же проблеме .Документ squeel предполагает, что это возможно.В разделе «Сложные условия» он перечисляет три способа сделать что-то похожее.

Учитывая

names = ['Ernie%', 'Joe%', 'Mary%']

Тогда вы можете сделать

Person.where('name LIKE ? OR name LIKE ? OR name LIKE ?', *names)

или

Person.where((['name LIKE ?'] * names.size).join(' OR '), *names)

или

Person.where{name.like_any names}

Документация подразумевает, что мы можем использовать AND вместо OR или like_all в отличие от like_any.

Однако я не могу заставить его работать на habtmотношения.Он продолжает давать мне undefined method 'call' за экземпляр ActiveRecord.

0 голосов
/ 05 января 2012

Хорошо, поэтому я не нашел способа избежать find_by_sql.Вот что я сделал.

@posts = Post.find_by_sql([ "SELECT * FROM posts p
    JOIN (
        SELECT pt.post_id FROM posts_tags pt
        JOIN posts p ON p.id = pt.post_id
        JOIN tags t ON t.id = pt.tag_id
        WHERE t.label IN (?)
        GROUP BY pt.post_id
        HAVING count(pt.post_id) = ?
    ) ct ON c.id = ct.post_id", names_array, names_array.size])

Лично я не совсем понимаю этот запрос (найден на http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/ - # 3).Особенно часть, где это присоединяется к избранному.Поэтому, если бы кто-нибудь мог объяснить, как на самом деле работает этот запрос, это было бы здорово.

Более того, если кто-нибудь знает, как сделать это более «рельсовым» способом (чем жестко закодированный запрос), я бы с удовольствиемэто.

Надеюсь, это поможет некоторым людям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...