Пошаговая фильтрация ActiveRecord с использованием функции where в блоке - PullRequest
0 голосов
/ 25 апреля 2019

Я пытаюсь реализовать бесплатные запросы к лазерной базе данных.Например: дать все кадры с энергией> 20 Дж и продолжительностью <150 фс и ... </p>

Мой подход заключается в том, чтобы сначала найти все кадры с энергией> 20 Дж, а затем применить условие длительности <150 фс к этим кадрам.И так далее, пока все параметры поиска не будут обработаны. </p>

ss.ssps.each do |ssp|
    selectedShots = 
      selectedShots.where("instancevalues.name = '#{ssp.instancevalue_name}'")
                   .where("instancevalues.data_numeric #{ssp.operator} #{ssp.value}")
end

Это работает, как и ожидалось, для одного параметра поиска.Оператор SQL выглядит следующим образом:

SELECT  `shots`.* FROM `shots` WHERE (instancevalues.name = 'Energy') 
                                 AND (instancevalues.data_numeric > 20.0) 

С двумя параметрами поиска я бы ожидал, что Rails сначала обработает первый параметр и создаст подмножество снимков, как описано выше.После этого применить второй параметр.

Вместо этого Rails генерирует один SQL-оператор вида

SELECT  `shots`.* FROM `shots` WHERE 
           (instancevalues.name = 'Energy') 
       AND (instancevalues.data_numeric > 20.0) 
       AND (instancevalues.name = 'Duration') 
       AND (instancevalues.data_numeric < 150.0) 

Результат, конечно, пуст.Как я могу реализовать пошаговую фильтрацию в рельсах?

ОБНОВЛЕНИЕ:

Я пытался объяснить суть проблемы, поэтому не использовал деталимодель.Но, возможно, это упрощение препятствует альтернативному решению.

У меня есть модели:

class Shot < ActiveRecord::Base
  belongs_to :experement
  has_many :instancevaluesets
  has_many :instances, :through => :instancevaluesets
  has_many :instancevalues, :through => :instancevaluesets
class Instancevalueset < ActiveRecord::Base
  belongs_to :shot
  belongs_to :instance
  has_many :instancevalues
class Instancevalue < ActiveRecord::Base
 #  instancevalueset_id :integer(38)     not null
 #  name                :string(256)     not null
 #  data_numeric        :decimal(, )
  belongs_to :instancevalueset

После каждого лазерного выстрела будет создаваться новый выстрел в дБ.Модель Instancevalueset подключает снимок к устройству (экземпляру).Физическое устройство создает запись для себя после выстрела.Устройства записывают результаты измерения в значениях instance с помощью instancevaluesset_id als FK.Моя задача - найти кадры, которые соответствуют параметрам поиска, примененным к значениям intance (результатам измерений).

Мой оригинальный код:

  ss.ssps.each do |ssp|
          selectedShots = **selectedShots.joins(:instancevalues)**
            .where("instancevalues.name = '#{ssp.instancevalue_name}'")
            .where("instancevalues.data_numeric #{ssp.operator} #{ssp.value}")
      end

Предположим, набор снимков [a, b, c, d, e, f].Снимки [a, c, e] имеют энергию> 20 Дж, поэтому после первого ssp я хочу получить [a, c, e].Теперь я хочу отфильтровать короткие импульсы из этого набора.Выстрел e на длинный T = 200fs.Итак, во 2-й итерации я хочу применить ssp к [a, c, e] и получить [a, c].

Идея @Mark хороша, но она возвращает массив запросов, примененных к БД, а не к предыдущему набору.

 selectedShots = ss.ssps.map do |ssp|
          selectedShots.joins(:instancevalues)
            .where("instancevalues.name = '#{ssp.instancevalue_name}'")
            .where("instancevalues.data_numeric #{ssp.operator} #{ssp.value}")
      end 

Если было бы возможно выполнить слияние ANDмассива, это решило бы мою проблему.Но если я проверю:

  selectedShots.last.merge(selectionShots.first)

, я получу снова:

 ....WHERE (instancevalues.name = 'Energy') 
       AND (instancevalues.data_numeric > 20.0) 
       AND (instancevalues.name = 'Duration') 
       AND (instancevalues.data_numeric < 150.0) 

Ответы [ 2 ]

0 голосов
/ 26 апреля 2019

По предложению @Mark:

#2D array, one row contain ids of one query
      selectedShotsIDs = ss.ssps.map do |ssp|
          selectedShots.joins(:instancevalues)
            .where("instancevalues.name = '#{ssp.instancevalue_name}'")
            .where("instancevalues.data_numeric #{ssp.operator} #{ssp.value}").select(:id).map{|shot| shot.id}
      end

# Filter IDs that occur in all queries using array intersections
      filteredIDs = selectedShotsIDs.first
      selectedShotsIDs.each do |ss|
        filteredIDs = filteredIDs & ss
      end

      # get the shots
      selectedShots = Shot.where(id: filteredIDs )

Может быть, это неэффективно, но работает!

0 голосов
/ 25 апреля 2019

Я думаю, это может сработать:

selectedShots = ss.ssps.map do |ssp|
  selectedShots.where("instancevalues.name = '#{ssp.instancevalue_name}'")
               .where("instancevalues.data_numeric #{ssp.operator} #{ssp.value}")
end

Это создаст массив с отдельной записью для каждого ssp в коллекции ssps и выполнит каждый запрос отдельно.

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