我在RoR上有一个关于多对多关系(has_many through:
)的简单问题。所以我有一个Tweet
模型,它有很多Tag
,反之亦然。
你有一个索引操作和视图,在那里你可以显示所有带有标签的推文,但你也可以按标签进行过滤。还有一个小细节,Tweet
可以不带标签。
所以如果我做这样的事情:
Tweet.includes(:tags).where(tags: { name: #2017 })
如果你过滤,你只会得到你过滤过的标签,但不会得到Tweet
的所有标签。例如,如果Tweet
有标签#2017, #2016, #2015
,那么最终在您的视图中只会得到#2017
。
所以你可以通过这样做来解决问题:
Tweet.joins(:tags).where(tags: { name: #2017 })
然后你会得到每条推特的所有标签。
但问题是,若索引页面并没有过滤器,我希望所有的推文都显示出来,事件并没有标签。
当然,你可以做一个类似于检查标记参数是否存在的破解操作——做Tweet.joins
,否则做Tweet.includes
。
有没有一种方法可以在1个查询内做到这一点而不需要破解?还有这背后的机制是什么。我理解includes or LEFT OUTER JOIN
背后的逻辑,因为它只是将tweet_id
和中间表中的"tag_id"映射到Tag
表,当您有类似上面提到的where
查询时。但是,为什么INNER JOIN
让我一直给出所有标签,这让我产生了疑问。
谢谢你的回答。
这是因为当您查询时,includes
会进行热切加载
Tweet.includes(:tags).where(tags: {name: "2017"})
它急切地加载所有标签名称为2017的推文。Tweet.tags
将不会再次运行数据库查询,并且只有一个标记值。
但如果加入
Tweet.joins(:tags).where(tags: {name: "2017"})
它让innner加入,只找到那些有标签的推文,标签名称是2017。在执行Tweet.tags
时找到tweets之后,它会再次运行db查询来查找所有相关的标签,从而为您提供所需的结果。