ActiveRecord упорядочить отношение с нулевыми последними и ненулевыми значениями, отсортированными по вторичному атрибуту - PullRequest
0 голосов
/ 18 сентября 2018

У меня есть модель, Prospect.Я хочу отсортировать перспективы по некоторому значению (например, updated_at ASC), однако я также хочу разделить отсортированный список пополам, чтобы сначала отображались все перспективы, в которых сначала отображается lead_id IS NOT NULL, а затем updated_at ASC, а затем все перспективы, в которых lead_id IS NULLпоследний отсортированный по updated_at ASC.Меня не волнует, что такое lead_id, только то, что любые записи, где lead_id IS NOT NULL расположены в начале списка.Мне нужно иметь возможность разбивать набор данных на страницы, и запрос должен быть относительно эффективным (время обработки должно быть не более 500 мс, в идеале не более 100 мс).

Первый подход

Сначала я попытался выполнить это с помощью следующего запроса, однако это не работает (для моего варианта использования), поскольку потенциальные клиенты сортируются по lead_id (как и следовало ожидать из этого запроса), который уникален, поэтому эффективно выполняет вторичную сортировкубесполезно.

Prospect.order("lead_id ASC nulls last, updated_at ASC")

Второй подход

Я попробовал (слегка измененный) подход, предложенный Андрей Дейнеко .Это возвращает весь набор данных в правильном порядке, но нет способа объединить два отдельных отношения в одно отношение, которое можно разбить на страницы.В результате, чтобы разбить набор данных на страницы, мне нужно создать экземпляр каждой строки таблицы в памяти.Это было бы приемлемо для нескольких десятков записей максимум, но, конечно, не 20k +.

# prospect.rb
scope :with_leads,    -> { where.not(lead_id: nil) }
scope :without_leads, -> { where(lead_id: nil) }
scope :ordered,       -> { order(:total_clicks_count) } 

[Prospect.with_leads.ordered, Prospect.without_leads.ordered].flatten

Третий подход

Я понял, что могу получить отсортированный списокпроспект идентификаторы (как с, так и без lead_id) и использовать его, чтобы получить полный набор данных, упорядоченный по id.Это выполняет то, что мне нужно, и отлично работает для нескольких десятков или сотен записей, но не подходит для записей 20k +.

lead_ids = Prospect.where.not(lead_id: nil).pluck(:id)
prospect_ids = Prospect.where(lead_id: nil).pluck(:id)
prospects = Prospect.order_by_ids([lead_ids, prospect_ids].flatten)

Вот источник order_by_ids:

class ApplicationRecord < ActiveRecord::Base
    self.abstract_class = true

    def self.order_by_ids(ids)
        # https://stackoverflow.com/a/26777669/4139179
        order_by = ["CASE"]
        ids.each_with_index do |id, index|
            order_by << "WHEN #{self.name.underscore}s.id='#{id}' THEN #{index}"
        end

        order_by << "END"
        order(order_by.join(" "))
    end
end

Проблема с этим вторым подходом состоит в том, что для создания списка или упорядоченных идентификаторов для каждого подмножества (перспективы с и без lead_id) требуется до 1 секунды на 1000 записей, а затем используется для извлечения всего набора данных по порядку.

Есть ли лучший подход, который будет возвращать весь набор данных (таким образом, который может быть разбит на страницы), упорядоченный по некоторому атрибуту (например, updated_at ASC), где перспективы с lead_id находятся вверху списка, а те, у которых нетвнизу?

1 Ответ

0 голосов
/ 18 сентября 2018

При использовании ORDER BY «столбца» в PostgreSQL значения NULL по умолчанию будут последними.Так что

Prospect.order(:lead_id, :updated_at)

должно сработать.

Ваша фактическая потребность:

На практике эффект будет состоять в том, что когда пользователь просматривает список своих продажВ перспективе, те, которые были преобразованы в потенциальных клиентов, будут отображаться первыми в списке, даже если список отсортирован по другим атрибутам.

# prospect.rb
scope :with_leads,    -> { where.not(lead_id: nil) }
scope :without_leads, -> { where(lead_id: nil) }
scope :ordered,       -> { order(:total_clicks_count) } 

И затем используйте эти области для представления пользователю:

Prospect.with_leads.ordered.each do
  #...
end

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