我有3个模型 - Datapoints
,Dimensions
以及将这2个模型绑定在一起的模型 - DatapointDimensions
class Datapoint < ActiveRecord::Base
belongs_to :metric
has_many :datapoint_dimensions
has_many :dimensions, :through => :datapoint_dimensions
validates :metric, :presence => true
validates :value, :presence => true
validates :timestamp, :presence => true
validates :duration, :presence => true
end
class Dimension < ActiveRecord::Base
belongs_to :dimension_group
belongs_to :parent_dimension, class_name: "Dimension", foreign_key: "parent_dimension_id"
has_many :child_dimensions, class_name: "Dimension", foreign_key: "parent_dimension_id"
has_many :datapoint_dimensions
has_many :datapoints, through: :datapoint_dimensions
validates :dimension_group, :presence => true
validates :code, :presence => true, :uniqueness => true
validates :name, :presence => true, :uniqueness => true
end
class DatapointDimension < ActiveRecord::Base
belongs_to :datapoint
belongs_to :dimension
validates :datapoint, :presence => true
validates :dimension, :presence => true
end
现在,我需要编写一个查询,其中我检查具有特定度量,特定持续时间和DatapointDimension的Datapoint,它具有3个维度的组合
表格数据是这样的
----datapoints----
id metric_id duration
1 13 1M2012
2 13 1M2012
----datapoint_dimensions----
datapoint_id dimension_id
1 2
1 45
1 127
2 2
2 38
我希望能够在datapoint_dimensions
中搜索metric_id = 13的Datapoints与尺寸2,55和127(全部3个)相关联我试过
Datapoint.where(metric_id: 13).where(duration: '1M2012').joins(:datapoint_dimensions).where(datapoint_dimensions: {dimension_id: 2, dimension_id: 45, dimension_id: 127}).readonly(false)
这将返回两个数据点(id = 1和id = 2) 但我希望它只返回id = 1
的数据点提前致谢。
答案 0 :(得分:0)
您看到的问题与包含where
参数的dimension_id
子句有关。在您的情况下,您传递的哈希值包含三个键值对,但键的名称实际上是相同的。结果,当你认为你正在传递
{dimension_id: 2, dimension_id: 45, dimension_id: 127}
您实际上正在传递
{dimension_id: 2}
因为您基本上覆盖(或挤压)同名的其他键。 注意:&#34;挤压&#34;在其他版本的Ruby中,行为可能会有所不同。在我的(Ruby 2.0+)上,我的参数是127,而你指的是2.
由于您说要提取包含所有这三个Datapoint
值的dimension_id
对象,因此可以通过使用WHERE ... IN
和子查询来实现此目的。过滤不完全匹配的结果。它比原始版本复杂一点,但试试这个(格式化为可读性):
Datapoint.joins(:datapoint_dimensions)
.where(metric_id: 13, duration: '1M2012')
.where(datapoint_dimensions: {dimension_id: [2, 45, 127]})
.where("datapoint_id != (#{
DatapointDimension
.select(:datapoint_id)
.where("dimension_id NOT IN (?)", [2, 45, 127]).to_sql
})").group(:datapoint_id)
注意子查询和笨拙的字符串插值。可能有更好的方法来写这个,但这是我想到的唯一方法。
Rails 4具有where.not
函数,这使得该子查询稍微不那么笨拙,但实质上此查询将返回dimension_id
为2,45和127的每一行。
另请注意,如果您愿意,可以将.where
条款压缩到一个方法调用中,我只是将其分开以便于阅读。
如果这有所不同,请告诉我。
修改强>
如果上述方法无效,请尝试以下方法:
Datapoint.joins(:datapoint_dimensions)
.where(metric_id: 13, duration: '1M2012')
.where(datapoint_dimensions: {dimension_id: [2, 45, 127]})
.where("NOT EXISTS (SELECT 1 FROM datapoint_dimensions dd2 WHERE dd2.datapoint_id = datapoint_dimensions.datapoint_id AND dimension_id NOT IN (2, 45, 127))")
.group(:datapoint_id)