использование array_length внутри инструкции WHERE в ActiveRecord

#sql #ruby-on-rails #postgresql #ruby-on-rails-4 #rails-activerecord

#sql #ruby-on-rails #postgresql #ruby-on-rails-4 #rails-activerecord

Вопрос:

Мне удобнее использовать activerecord в отличие от ванильного SQL, поэтому у меня возникли небольшие проблемы.

У меня есть таблица связей со свойством, называемым sequence, которое имеет тип array. Массив либо пуст, либо содержит серию чисел.

Чего я пытаюсь достичь, так это написать область AR, которая позволяет мне возвращать только записи, размер массива последовательности которых равен num . Я представляю, что это выглядит примерно так:

 def self.of_order(num)
   where("array_length(sequence, 1) = ?", num)
end
  

Если бы это было доступно в active record, я бы представил, что это выглядит как:

 def self.of_order(num)
   where(sequence.size == num)
end
  

Редактировать:
Вот как выглядит миграция:

 class CreateRelationships < ActiveRecord::Migration
  def change
    create_table :relationships do |t|
      t.integer :root_id
      t.integer :destination_id
      t.boolean :first_order?
      t.text :sequence, array: true, default: []

      t.timestamps null: false
    end
  end
end
  

Пример данных:

 [
    [ 0] #<Relationship:0x007f8d5a5c82c8> {
                    :id => 73,
               :root_id => 51,
             :target_id => 52,
          :first_order? => true,
              :sequence => [],
            :created_at => Thu, 20 Oct 2016 19:05:22 UTC  00:00,
            :updated_at => Thu, 20 Oct 2016 19:05:22 UTC  00:00,
        :primitive_type => "spouse"
    },
    [ 1] #<Relationship:0x007f8d5a5c8188> {
                    :id => 74,
               :root_id => 52,
             :target_id => 51,
          :first_order? => true,
              :sequence => [22,43,90],
            :created_at => Thu, 20 Oct 2016 19:05:22 UTC  00:00,
            :updated_at => Thu, 20 Oct 2016 19:05:22 UTC  00:00,
        :primitive_type => "spouse"
    }
]
  

Я бы хотел Relationship.all вернуть обе записи,
Relationship.of_order(0) чтобы вернуть первую запись,
Relationship.of_order(3) чтобы вернуть вторую запись,
и Relationship.of_order(2) для возврата none.

Комментарии:

1. покажите более подробную информацию о вашей модели и миграциях, а также некоторые примеры данных…

2. добавлен @johnnynemonic

3. @muistoosh, который выдает SELECT "relationships".* FROM "relationships" WHERE (array_length(sequence, 1) = 0) и возвращает пустой

Ответ №1:

Я думаю, что корень вашей проблемы заключается в том, что array_length это null когда массив пуст:

 => select array_length(array[]::text[], 1);
 array_length 
--------------

(1 row)

=> select coalesce(array_length(array[]::text[], 1), 0);
 coalesce 
----------
        0
(1 row)
  

Это не совсем четко задокументировано, так что не расстраивайтесь, если вы это пропустили.

Итак, учитывая:

 def self.of_order(num)
  where("array_length(sequence, 1) = ?", num)
end
  

Relationship.of_order(6) будет работать просто отлично, но Relationship.of_order(0) в конечном итоге вы попытаетесь сделать это null = 0 внутри базы данных, а это никогда не бывает правдой, поэтому вы не найдете свои пустые массивы.

На ум приходят два простых решения:

  1. Вы можете явно обработать of_order(0) случай в своей области:

     def self.of_order(num)
      if(num == 0)
        where('array_length(sequence, 1) is null')
      else
        where('array_length(sequence, 1) = ?', num)
      end
    end
      
  2. Выполните coalesce вызов в запросе для преобразования нулевых значений в нули и предоставьте базе данных беспокоиться об этом:

     def self.of_order(num)
      where('coalesce(array_length(sequence, 1), 0) = ?', num)
    end
      

Комментарии:

1. Герой! избавило меня от головной боли