【问题标题】:Rails HABTM query -- Article with ALL tagsRails HABTM 查询 -- 带有所有标签的文章
【发布时间】:2014-03-04 08:12:27
【问题描述】:

我在我的应用程序(Rails 3)中创建了两个表:

def change
  create_table :articles do |t|
    t.string :name
    t.text :content
    t.timestamps
  end

  create_table :tags do |t|
    t.string :name
    t.timestamps
  end

  create_table :articles_tags do |t|
    t.belongs_to :article
    t.belongs_to :tag
  end

  add_index :articles_tags, :article_id
  add_index :articles_tags, :tag_id
end

我希望能够通过两种方式根据标签搜索文章:

  1. 带有任何给定标签的文章 (union)
  2. 具有所有给定标签(intersection)的文章

所以,换句话说,让我可以这样做的东西:

tag1 = Tag.create(name: 'tag1')
tag2 = Tag.create(name: 'tag2')

a = Article.create; a.tags << tag1
b = Article.create; b.tags += [tag1, tag2]

Article.tagged_with_any(['tag1', 'tag2'])
# => [a,b]

Article.tagged_with_all(['tag1', 'tag2'])
# => [b]

第一个相对容易。我刚刚在文章上做了这个范围:

scope :tagged_with_any, lambda { |tag_names|
  joins(:tags).where('tags.name IN (?)', tag_names)
}

问题是第二个。我不知道如何在 ActiveRecord 或 SQL 中执行此操作。

我认为我可以做一些像这样令人讨厌的事情:

scope :tagged_with_all, lambda { |tag_names|
  new_scope = self

  # Want to allow for single string query args
  Array(tag_names).each do |name|
    new_scope = new_scope.tagged_with_any(name)
  end
  new_scope
}

但我敢打赌,这太低效了,而且闻起来很臭。有关如何正确执行此操作的任何想法?

【问题讨论】:

  • 最终决定这是自制解决方案无法像我喜欢的那样工作的众多案例之一。所以我刚刚安装了this gem,它做了一些非常简洁的东西,看起来很愉快。有点逃避,但是...

标签: sql ruby-on-rails ruby-on-rails-3 postgresql activerecord


【解决方案1】:

正如你所说,这个范围非常低效(而且丑陋)。

试试这样的:

def self.tagged_with_all(tags)
  joins(:tags).where('tags.name IN (?)', tags).group('article_id').having('count(*)=?', tags.count).select('article_id')
end

密钥在having 子句中。您可能还想看看表之间的 SQL 除法操作。

【讨论】:

  • 谢谢!这是我以前从未见过的一些很酷的 SQL 东西。但是,在这种情况下它似乎不起作用,因为 Tag 表没有 article_id 列。有一个具有 article_id 和 tag_id 的连接表 ArticleTags,但我似乎无法直接了解它。此外,这不会查找具有任何标签的所有文章,并且与搜索的标签数量完全相同吗?因此,如果还有更多,它将找不到它们,并且如果它具有标签之一但其他标签不同,它会?
  • 基本思路是:用标签加入文章,但只加入具有指定名称的标签。然后,按 article_id 对连接结果进行分组,如果该组的大小等于指定的标签,则意味着文章包含所有标签。首先在 postgres sql 控制台中编写 sql 并在查询有效时编写 ruby​​ 代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
相关资源
最近更新 更多