2011-02-08 37 views
4

尝试构造一个查询,使得我有多个语句指定连接,每个语句都链接到它们的where消息。当查询运行时,我得到所有连接,但只有我第一次调用的地方。下面是在做查询方法中:在rails3中链接多个连接的轮子

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条件。我还打印散列,只是为了确保我没有失去主意,并且存在价值,事实上确实如此。

那么,我错过了什么让它如我所愿呢?

回答

5

在这里回答我自己的问题。我发现最优雅,最简洁的方法是直接下载到arel。此外,还有一些原始代码发布的问题,但即使如此,我仍然需要使用arel才能获得正确的分组条件。对于上下文,我有一个对象,基于它的相关数据,需要动态构建一个半高级查询,所以我想做一些事情,比如检查某些相关数据的存在,如果存在的话,额外的连接和wheres。下面是相关方法的最终版本:

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 

上述方法选择调用其余的方法,它会返回在其中使用的AREL链接的AR对象时,同时建立最终的查询调用。注意到这种情况;我完全使用了一个使用了arel的版本,看起来可以工作,但事实上它正在返回重复。我找到了使用group(some_attribute)来伪造东西的参考,但是结果却导致问题出现,可以这么说。所以我回到使用ActiveRelation来指定不同的,加入和包括,其余的都是。

下一个是最初给我很多麻烦的部分;有多种可能性,每一种可能都是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链接,用于提供不同的,并包括,与直接使用AREL对于相关值的条件。希望这有助于某些人。