首页 > 解决方案 > ActiveRecord 在 has_and_belongs_to_many 关系上使用 AND 而不是 OR 查询多个

问题描述

我正在尝试设置对我的 Rails 应用程序的搜索。

有 2 个模型具有 has_and_belongs_to_many 关系:

class Post
  has_and_belongs_to_many :tags
end

class Tag
  has_and_belongs_to_many :post
end

为了确认,这里是表格:

create_table "posts" do |t|
  t.bigint "user_id"
  t.string "title"
  ...
end

create_table "tags" do |t|
  t.string "name"
end

create_table "tags_posts", id: false do |t|
  t.bigint "post_id", null: false
  t.bigint "tag_id", null: false
  t.index ["post_id", "tag_id"]
  t.index ["tag_id", "post_id"]
end

我想做一个 AND 查询:All POSTS that have TAGS 1, 2, AND 3

我得到的最接近的是这个简单的查询,但它返回OR而不是AND- 所有具有这 3 个标签中的 1 个的帖子。 Post.includes(:tags).where(tags: { id: [1, 2, 3] }).

如何获得这样的查询,在哪里可以轻松地将 N 个标签 ID 添加到 ActiveRecord 查询并使其成为AND查询。

额外的问题 - 有没有办法添加一个参数,它返回帖子上至少存在 M 个标签的帖子。那么ALL POSTS that have AT LEAST 2 TAGS of 1, 2, AND 3

更新

我发现了这个:https ://stackoverflow.com/a/7994175/659820

翻译:

Post.find_by_sql("
  SELECT p.* FROM posts p,
    post_tags pt1,
    post_tags pt2,
    tags t1,
    tags t2
  WHERE
    p.id = pt1.post_id
  AND t1.id = 4
  AND t1.id = pt1.post_tag_id
  AND p.id = pt2.post_id
  AND t2.id = 11
  AND t2.id = pt2.post_tag_id
")
  1. 这是实现这一目标的最有效方法吗?
  2. 奖金问题仍未得到解答 - 为提供的 N 个标签提供一些 M 匹配项?

标签: ruby-on-railsactiverecordruby-on-rails-5

解决方案


您可以通过使用连接表来解决此问题。为了使用连接表获取正确的帖子,我们需要查看匹配条件的帖子的数量:

tag_ids = [1, 2, 3] # You can use tag name instead of id
Post.joins(:tags).where(tags: { id: tag_ids }).group(:id).having("count(*) = ?", tag_ids.size)

更新

这应该有效:

class Post < ApplicationRecord
  def self.tagged_with(post_tags, m = nil)
    m ||= post_tags.size
    Post.joins(:tags).where(tags: { id: post_tags }).group(:id).having("count(*) >= ?", m)
  end
end

tag_ids = [1, 2, 3]
Post.tagged_with(tag_ids)
#=> Returns posts which has all tags.
Post.tagged_with(tag_ids, 2)
#=> Returns posts which has at least two of given tags.

推荐阅读