Ruby on rails 3 使用rails3中的位置链接多个连接

Ruby on rails 3 使用rails3中的位置链接多个连接,ruby-on-rails-3,activerecord,Ruby On Rails 3,Activerecord,试图构造一个查询,使我有多个语句指定联接,每个语句都有一个where消息链接到它们上。当查询运行时,我得到所有的连接,但只有第一次调用的where。下面是执行查询的方法体: observations_joins = Observation.joins(:obs_session => :project).where(:obs_sessions=>{:project_id=>self.project.id}) descriptor_hash = descriptor_where_h

试图构造一个查询,使我有多个语句指定联接,每个语句都有一个where消息链接到它们上。当查询运行时,我得到所有的连接,但只有第一次调用的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
我有另一个从第二个joins语句内部调用的方法,它迭代潜在的匹配值并生成字符串和使用的值;正文如下:

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}
并且不包括第二组where条件。我还打印了散列,只是为了确保我没有失去理智,里面有值,而且确实有


那么,我错过了什么才能让这一切如我所期待的那样继续下去呢

在这里回答我自己的问题。我发现最优雅、最简洁的方法就是直接去阿雷尔。另外,发布的原始代码也存在一些问题,但即使如此,我仍然需要使用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。注意Distinct;我已经有了一个完全使用arel的版本,它看起来很有效,但实际上返回了副本。我发现了使用group(some_属性)来伪造东西的引用,但可以说,这会导致整个链条出现问题。所以我转而使用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
最终,我发现了最好的解决方案,即利用ActiveRelationship链接来提供distinct和include,以及直接使用arel来满足相关值的条件。希望这能在某些方面帮助到某人

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