我在我的应用程序中创建了两个表(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
我希望能够以两种方式搜索基于标签的文章:
所以,换句话说,允许我这样做的事情:
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
}
但是我认为这种效率很低,而且只是闻起来。有关如何正确执行此操作的任何想法?
答案 0 :(得分: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分区操作。