Оптимизация запроса ActiveRecord для исключения определенных элементов с учетом двух связанных отношений HABTM и has_many - PullRequest
0 голосов
/ 28 января 2020

Мне нужно отображать только определенные записи из одной таблицы в ActiveAdmin с помощью фильтра, который я реализую как выбор (выпадающий список) и чей код находится в области видимости Rails. У меня проблемы с поиском чистого SQL заменителя в области Rails для следующего условия:

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

# app/models/score.rb

class Score < ApplicationRecord
  has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
end

# app/models/export_order.rb

class ExportOrder < ApplicationRecord
  has_and_belongs_to_many :scores, join_table: :scores_export_orders
  belongs_to :shop
end

# app/models/shop.rb

class Shop < ApplicationRecord
  has_many :export_orders
end

Фильтр позволяет выбрать магазин поэтому относительный код должен возвращать Score::ActiveRecord_Relation в области видимости.

Условие таково: возвращать все оценки, которые не были экспортированы в выбранный магазин, т.е. все оценки, не имеющие ни одного из них. экспортировать заказы с shop, равным выбранному магазину .

В качестве примера представьте следующий сценарий:

[608, []]
[607, []]
[606, []]
[593, [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1, 8, 8, 8, nil, nil, nil, nil, 8, 8]]
[586, []]
[585, []]
[372, [nil, nil, 2, nil, nil, nil, 8, 8, 8, nil]]
[157, [nil, 2, nil, nil, nil, 1]]

, где слева у вас есть идентификаторы счета, и справа - массив идентификаторов магазинов их экспортных заказов. Чтобы было понятно, выше приведен результат Score.all.map { |s| [s.id, s.export_orders.map{|o|o.shop_id}] }.

Так, например, результат Score.not_exported_to(2).ids должен быть

[608, 607, 606, 593, 586, 585]

, поскольку ни один из порядков экспорта оценок ( с этими идентификаторами) имеет 2 в своих идентификаторах магазинов.


Прямо сейчас, я действительно не могу придумать SQL способ написать это. До сих пор в моей области видимости есть:

# app/models/score.rb

  scope :not_exported_to, -> (input) {
    shop = Shop.find(input)
    export_orders = shop.export_orders
    already_exported_score_ids = export_orders.map { |o| o.scores.ids } .flatten.uniq
    where.not(id: already_exported_score_ids)
  }

Это дает правильный результат, но, очевидно, очень неэффективно, так как использует map в зависимой ассоциации (Score), которая выполняет выборку в таблице результатов, повторенную количество раз, равное export_orders.count. Представьте, что он работает с 1000+ экспортными заказами магазина!

То, что я пробовал до сих пор:

joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").distinct.where("export_orders.shop_id <> #{input}")

, но это возвращает неправильный результат.

Я не очень опытен в SQL, и я боюсь, что мне придется использовать какие-то вложенные запросы, с которыми я не очень знаком.

...