在rails3中使用wheres链接多个连接

wkh*_*tch 4 activerecord ruby-on-rails-3

尝试构造一个查询,以便我有多个语句指定连接,每个连接都有一个链接到它们的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
Run Code Online (Sandbox Code Playgroud)

我有另一个从第二个连接语句内部调用的方法,它迭代潜在的匹配值并生成字符串和使用的值; 身体在这里:

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}
Run Code Online (Sandbox Code Playgroud)

所以生成的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)
Run Code Online (Sandbox Code Playgroud)

并且不包括第二组where条件.我也打印哈希,只是为了确保我不会失去理智,那里有价值,而且确实有.

那么,我想要的是,我想要的是什么呢?

wkh*_*tch 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
Run Code Online (Sandbox Code Playgroud)

上述方法可选地调用其余方法,这些方法在构建最终查询时返回链接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
Run Code Online (Sandbox Code Playgroud)

最后是潜在连接实体值的其他谓词方法:

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
Run Code Online (Sandbox Code Playgroud)

最终,我发现最好的解决方案是利用ActiveRelation链接来提供distinct和includes,并直接使用arel来处理相关值的条件.希望这在某些方面对某人有所帮助.