尝试构造一个查询,以便我有多个语句指定连接,每个连接都有一个链接到它们的where消息。当查询运行时,我得到所有连接,但只有我第一次调用的位置。这是进行查询的方法体:
observations_joins = Observation.joins(:obs_session => :project).where(:obs_sessions=>{:project_id=>self.project.id})
descriptor_hash = descriptor_where_hash if tag_descriptors && tag_descriptors.size > 0
puts "The descriptor_hash: #{descriptor_hash}"
observations = observations_joins.joins(:obs_descriptors).where("#{descriptor_hash['query_string']}", descriptor_hash['match_values']) if tag_descriptors && tag_descriptors.size > 0
arel = observations.arel
puts "The arel sql should be: #{arel.to_sql}"
observations
我有另一个从第二个连接语句内部调用的方法,它迭代潜在的匹配值并生成字符串和使用的值;身体在这里:
match_values = []
query_string = "obs_descriptors.tag_name = ?"
tag_descriptors.each_index do |index|
query_string = query_string + " #{tag_descriptors.fetch(index).qualifier_key} obs_descriptors.tag_name = ?" if index != 0
match_values << tag_descriptors.fetch(index).tag_name
end
{:match_values=>match_values, :query_string=>query_string}
因此生成的sql看起来像:
SELECT `observations`.* FROM `observations` INNER JOIN `obs_sessions` ON `obs_sessions`.`id` = `observations`.`obs_session_id` INNER JOIN `projects` ON `projects`.`id` = `obs_sessions`.`project_id` INNER JOIN `obs_descriptors` ON `obs_descriptors`.`observation_id` = `observations`.`id` WHERE (`obs_sessions`.`project_id` = 1)
并且不包括第二组where条件。我也打印哈希,只是为了确保我不会失去理智,那里有价值,而且确实有。
那么,我想要的是什么让我按照我的期望去做?
答案 0 :(得分:5)
在这里回答我自己的问题。我发现最优雅,最简洁的方法就是直接下载到arel。此外,发布的原始代码存在一些问题,但即便如此,我还是需要使用arel来获得正确的分组条件。对于上下文,我有一个对象,根据它的相关数据,需要动态构建一个半高级查询,所以我想做一些事情,比如检查某些相关数据是否存在,如果存在,那么就应该在额外的连接和轮播。以下是相关方法的最终版本:
def find_observations
observations = Observation.select('distinct observations.*').includes(:obs_session).includes(:judgements).includes(:concepts).includes(:obs_descriptors)
observations = observations.joins(:obs_session => :project).where(:obs_sessions=>{:project_id=>self.project.id})
if tag_descriptors && tag_descriptors.size > 0
observations = observations.where(descriptor_predicate)
end
if session_descriptors && session_descriptors.size > 0
observations = observations.where(session_predicate)
end
if user_descriptors && user_descriptors.size > 0
observations = observations.where(user_predicate)
end
#puts "observations sql is: #{observations.to_sql}"
observations.all
end
上述方法可选地调用其余方法,这些方法在构建最终查询时返回链接AR对象时where调用中使用的arel。注意区域;我有一个完全使用arel的版本,似乎工作,但实际上返回重复。我找到了使用group(some_attribute)来伪造东西的引用,但结果却引起了链条上的问题,可以这么说。所以我回过头来使用ActiveRelation指定distinct,join和includes,以及其余的arel。
下一个原本是给我带来很多麻烦的部分;存在可变数量的可能性,并且每个可能是AND或OR条件,并且需要单独分组,以免弄乱生成的where子句的其余部分。
def descriptor_predicate
od = Arel::Table.new :obs_descriptors
predicate = nil
self.tag_descriptors.each_index do |index|
descriptor = self.tag_descriptors.fetch(index)
qual_key = descriptor.qualifier_key
tag_name = descriptor.tag_name
if index == 0
predicate = od[:descriptor].eq(tag_name)
else
if qual_key == "OR"
predicate = predicate.or(od[:descriptor].eq(tag_name))
else
predicate = predicate.and(od[:descriptor].eq(tag_name))
end
end
end
predicate
end
最后是潜在连接实体值的其他谓词方法:
def session_predicate
o = Arel::Table.new :observations
predicate = nil
self.session_descriptors.each_index do |index|
obs = self.session_descriptors.fetch(index)
if index == 0
predicate = o[:obs_session_id].eq(obs.entity_id)
else
predicate = predicate.or(o[:obs_session_id].eq(obs.entity_id))
end
end
predicate
end
def user_predicate
o = Arel::Table.new :observations
predicate = nil
self.user_descriptors.each_index do |index|
obs = self.user_descriptors.fetch(index)
if index == 0
predicate = o[:contributor_id].eq(obs.entity_id)
else
predicate = predicate.or(o[:contributor_id].eq(obs.entity_id))
end
end
predicate
end
def descriptor_where_string(included_where_statements)
tag_descriptors.each_index do |index|
qual_key = tag_descriptors.fetch(index).qualifier_key
tag_name = tag_descriptors.fetch(index).tag_name
if index == 0
query_string = "obs_descriptors.descriptor = #{tag_name}"
else
if qual_key == "OR"
query_string = query_string + " #{qual_key} obs_descriptors.descriptor = #{tag_name} AND #{included_where_statements} "
else
query_string = query_string + " #{qual_key} obs_descriptors.descriptor = ?"
end
end
end
query_string
end
最终,我发现最好的解决方案是利用ActiveRelation链接来提供distinct和includes,并直接使用arel来处理相关值的条件。希望这在某些方面对某人有所帮助。