我有两个型号,书签和标签。标签由act-as-taggable-on gem实现,但我将在这里解释主要观点。
书签模型包含 url 字段。给定书签实例@bookmark,@ bookmart.tags返回其标签。不同的书签可以共享相同的标签(即标签中的许多部分)。
标签有name和taggings_count。 taggings_count字段存储标记标记的书签数量。在幕后,有一个标签表,但这没关系。
现在问题是,我想检索所有那些由具有特定网址值的书签标记的标记,结果应该按照标记某个标记的书签数量排序(该数字不相同)作为taggings_count字段,表示所有书签的标记计数,但此处需要特定网址的书签)。如何才能使生成的sql高效?
我知道我可以直接在sql中编写效率,但我也想知道Rails是否可以在不损害太多性能的情况下做同样的事情,因此我不必在我的Rails应用程序中注入sql代码
以下是表定义,在taggings表中,taggable_id充当Bookmark的外键,tag_id是Tag的外键:
CREATE TABLE `bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`description` text,
`private` tinyint(1) DEFAULT NULL,
`read` tinyint(1) DEFAULT NULL,
`list_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `taggings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag_id` int(11) DEFAULT NULL,
`taggable_id` int(11) DEFAULT NULL,
`taggable_type` varchar(255) DEFAULT NULL,
`tagger_id` int(11) DEFAULT NULL,
`tagger_type` varchar(255) DEFAULT NULL,
`context` varchar(128) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `taggings_idx` (`tag_id`,`taggable_id`,`taggable_type`,`context`,`tagger_id`,`tagger_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`taggings_count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `index_tags_on_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
答案 0 :(得分:1)
这是一个解决方案:
bookmark_ids = Bookmark.where(url: "http://foobar.com").pluck(:id)
taggings = Tagging.where(taggable_id: bookmark_ids).where(taggable_type: "Bookmark")
tag_ids = taggings.pluck(:tag_id).uniq
tags = Tag.where(id: tag_ids).order(taggings_count: :desc)
理论上它可以使用ActiveRecord中的joins()
方法编写,但我不知道您使用的宝石如何定义关联。
这可能会也可能不会起作用:
Tag.order(taggings_count: :desc)
.joins(taggings: :bookmark)
.where(bookmark: { url: "http://foobar.com" })
您也可以编写原始SQL,但在rails中感觉很脏。
答案 1 :(得分:0)
事实证明,acts-as-taggable-on实际上可以对关联进行操作。文档有样本,我只是没有注意到:
Bookmark.where(url: url).tag_counts_on(:tags).sort_by(&:taggings_count).reverse.map(&:name)
唯一的问题是,结果是按照这些标签的受欢迎程度排序,无论它们在哪种上下文中流行。但这并不重要。关键是这个rails语句只生成一个sql:
SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN bookmarks ON bookmarks.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Bookmark' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT bookmarks.id FROM "bookmarks" WHERE "bookmarks"."url" = 'http://kindleren.com/forum.php?gid=49')) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id