将多个连接链接到轨道3中的位置

时间:2011-02-08 20:36:24

标签: ruby-on-rails-3 activerecord

尝试构造一个查询,以便我有多个语句指定连接,每个连接都有一个链接到它们的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条件。我也打印哈希,只是为了确保我不会失去理智,那里有价值,而且确实有。

那么,我想要的是什么让我按照我的期望去做?

1 个答案:

答案 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来处理相关值的条件。希望这在某些方面对某人有所帮助。