Sql Rails:在3个模型上进行复杂搜索,只返回最新的-如何做到这一点?
我正在尝试向我的应用程序添加一个高级搜索选项,用户可以根据3种不同型号的属性搜索某些链接 我的应用程序设置为Sql Rails:在3个模型上进行复杂搜索,只返回最新的-如何做到这一点?,sql,ruby-on-rails,postgresql,ruby-on-rails-3.2,Sql,Ruby On Rails,Postgresql,Ruby On Rails 3.2,我正在尝试向我的应用程序添加一个高级搜索选项,用户可以根据3种不同型号的属性搜索某些链接 我的应用程序设置为用户有多个:网站,网站有多个:链接,链接有多个:统计数据 我知道如何在Rails中创建带有连接或包含等的SQL查询,但我陷入了困境,因为我只想检索每个链接的最新统计数据,而不是所有链接的最新统计数据,我不知道最有效的方法 例如,假设一个用户有两个网站,每个网站有10个链接,每个链接有100个统计数据,总共有2022个对象,但我只想搜索42个对象(每个链接只有1个统计数据) 一旦我在数据库查
用户有多个:网站
,网站有多个:链接
,链接有多个:统计数据
我知道如何在Rails中创建带有连接或包含等的SQL查询,但我陷入了困境,因为我只想检索每个链接的最新统计数据,而不是所有链接的最新统计数据,我不知道最有效的方法
例如,假设一个用户有两个网站,每个网站有10个链接,每个链接有100个统计数据,总共有2022个对象,但我只想搜索42个对象(每个链接只有1个统计数据)
一旦我在数据库查询中只得到了这42个对象,我就可以添加.where(“类似属性?”,用户输入)
,并返回正确的链接
更新
我已尝试将以下内容添加到我的链接模型中:
has_many :stats, dependent: :destroy
has_many :one_stat, class_name: "Stat", order: "id ASC", limit: 1
但这似乎不起作用,例如,如果我这样做:
@links = Link.includes(:one_stat).all
@links.each do |l|
puts l.one_stat.size
end
我得到的不是1,1,1…
而是所有统计数据的编号:125,40,76…
我可以使用“限制”选项来获得我想要的结果吗?还是不可以
第二次更新
我已经根据Erez的建议更新了代码,但仍然无法正常工作:
has_one :latest_stat, class_name: "Stat", order: "id ASC"
@links = Link.includes(:latest_stat)
@links.each do |l|
puts l.latest_stat.indexed
end
=> true
=> true
=> true
=> false
=> true
=> true
=> true
Link.includes(:latest_stat).where("stats.indexed = ?", false).count
=> 6
Link.includes(:latest_stat).where("stats.indexed = ?", true).count
=> 7
它应该返回1和6,但它仍在检查所有统计信息,而不是只检查最新的统计信息。我会尝试以下方法:
has_one :latest_stat, class_name: "Stat", order: "id ASC"
@links = Link.includes(:latest_stat)
@links.each do |l|
puts l.latest_stat
end
注意:您无法打印最新的统计大小,因为它是统计对象本身,而不是关系。我会尝试以下方法:
has_one :latest_stat, class_name: "Stat", order: "id ASC"
@links = Link.includes(:latest_stat)
@links.each do |l|
puts l.latest_stat
end
注意:您不能打印最新的统计大小,因为它是统计对象本身而不是关系。这就是您要查找的吗
@user.websites.map { |site| site.links.map { |link| link.stats.last } }.flatten
对于给定用户,这将返回一个数组,其中包含该用户网站上链接的最新统计信息 这就是你要找的吗
@user.websites.map { |site| site.links.map { |link| link.stats.last } }.flatten
对于给定用户,这将返回一个数组,其中包含该用户网站上链接的最新统计信息 有时,您必须突破AR抽象,启动SQL。就一点点 让我们假设你有非常简单的关系:
网站
有很多:链接
,链接
属于:网站,有很多:统计
,统计
属于:链接。任何地方都没有非规范化。现在,您需要构建一个查询,该查询将查找它们的所有链接,并为每个链接查找最新的统计数据,但只能查找具有某些属性的统计数据(也可以是具有某些属性的网站或具有某些属性的链接)
未经测试,但类似于:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where("stats.id = (select max(stats2.id)
from stats stats2 where stats2.link_id = links.id)")
最后一位子选择属于每个链接的统计信息并查找最大id。然后,它过滤掉与最大id不匹配的统计信息(从顶部的连接)。查询返回网站,每个网站都有一定数量的链接,每个链接在其stats
集合中只有一个统计信息
一些额外信息
我最初写这个答案的时候用的是,这被证明是过分的,但我想我还是应该在这里讨论一下,因为,嗯,很有趣。您会注意到,我们上面使用的聚合函数技巧之所以有效,是因为我们根据它的ID来确定要使用哪个stat,而这个ID正是我们从联接中筛选stats所需的属性。但假设您只需要根据ID以外的一些标准排名的第一个统计数据,例如,点击次数
;这一技巧将不再有效,因为聚合将无法跟踪ID。这就是窗口功能的用武之地
同样,完全未经测试:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where(
"(stats.id, 1) in (
select id, row_number()
over (partition by stats2.id order by stats2.number_of_clicks DESC)
from stat stats2 where stats2.link_id = links.id
)"
)
最后一个
,其中
子选择与每个链接相匹配的统计信息,并按单击次数的升序排列,然后部分中的将其与连接中的统计信息相匹配。请注意,窗口查询不能移植到其他数据库平台。您还可以使用此技术来解决您提出的原始问题(只需将stats2.id
交换为stats2.u点击次数
);可以想象,它的性能会更好,这正是它所提倡的。有时,您必须突破AR抽象,并启动SQL。就一点点
让我们假设你有非常简单的关系:网站
有很多:链接
,链接
属于:网站
,有很多:统计
,统计
属于:链接。任何地方都没有非规范化。现在,您需要构建一个查询,该查询将查找它们的所有链接,并为每个链接查找最新的统计数据,但只能查找具有某些属性的统计数据(也可以是具有某些属性的网站或具有某些属性的链接)
未经测试,但类似于:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where("stats.id = (select max(stats2.id)
from stats stats2 where stats2.link_id = links.id)")
最后一位子选择属于每个链接的统计信息并查找最大id。然后,它过滤掉与最大id不匹配的统计信息(从顶部的连接)。查询返回网站,每个网站都有一定数量的链接,每个链接在其stats
集合中只有一个统计信息
一些额外信息
我最初写这个答案的时候用的是,这被证明是过分的,但我想我还是应该在这里讨论一下,因为,嗯,很有趣。您会注意到,我们上面使用的聚合函数技巧之所以有效,是因为我们根据它的ID来确定要使用哪个stat,而这个ID正是我们从联接中筛选stats所需的属性。但假设您只需要根据ID以外的一些标准排名的第一个统计数据,例如,点击次数
;这一技巧将不再有效,因为聚合将无法跟踪ID。这就是窗口功能的用武之地
同样,完全未经测试:
Website
.includes(:links => :stats)
.where("stats.indexed" => true)
.where(
"(stats.id, 1) in (
select id, row_number()
over (partition by stats2.id order by stats2.number_of_clicks DESC)
from stat stats2 where stats2.link_id = links.id
)"
)
最后一个,其中
子选择与每个链接相匹配的统计信息,并按单击次数的升序排列,然后部分中的将其与连接中的统计信息相匹配。注意,窗口