Связывание нескольких соединений с шинами в рельсах3 - PullRequest
4 голосов
/ 08 февраля 2011

Попытка построить запрос таким образом, чтобы у меня было несколько операторов, указывающих объединения, каждое из которых было связано с сообщением «где». Когда запрос запущен, я получаю все соединения, но только откуда мой первый вызов. Вот тело метода, которое выполняет запрос:

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}

Итак, генерируемый 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)

и не включает второй набор условий. Я также печатаю хеш, просто чтобы убедиться, что я не схожу с ума, и там есть ценности, которые есть.

Итак, что мне не хватает, чтобы заставить это пойти так, как я ожидал?

1 Ответ

5 голосов
/ 06 марта 2011

Отвечая на мой собственный вопрос здесь. Самый элегантный и лаконичный способ, который я нашел для этой работы, заключался в том, чтобы перейти прямо в Арель. Кроме того, были некоторые проблемы с отправленным исходным кодом, но даже все же мне нужно было использовать 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

Приведенный выше метод необязательно вызывает остальные методы, которые возвращают арель, используемый в вызовах where при сцеплении объекта 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 для условий связанных значений. Надеюсь, что в какой-то момент это кому-нибудь поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...