Эффективно повторять и изменять ассоциацию - PullRequest
0 голосов
/ 19 сентября 2018

У нас есть код, который эффективно делает это.

obj.things.each |thing|
    ... do some stuff ...

    obj.things.destroy(thing)

    ... do some more stuff...
end

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

obj.things.to_a.each |thing|
    ...
end

Есть ли способ перебора и изменения в коллекции без вытягивания всей ассоциации в память?

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

ОБНОВЛЕНИЕ : Я написал о более крупной проблеме .

Ответы [ 2 ]

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

Ваш исходный each уже вытягивает всю ассоциацию в память.

Вызов to_a для создания дополнительной копии массива (который затем не содержит мутаций) действительно является очень разумным подходом.И не особенно дорого: массив дублируется, но фактические объекты - нет.

Вы также можете использовать ActiveRecord::Base#destroy вместо:

obj.things.each |thing|
    ... do some stuff ...

    thing.destroy

    ... do some more stuff...
end

В качестве *Коллекция 1013 * больше не знает об уничтожении, она все равно будет содержать полный набор вещей, и, следовательно, итерация не будет затронута.(Это может быть проблематично, если в любом блоке stuff используется текущее содержимое obj.things.)

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

Уничтожение объекта внутри внешнего вида на самом деле плохая практика.

Я могу подумать о двух методах: вы можете сохранить идентификаторы объектов, которые вы хотите уничтожить, в массиве или пометить их в базе данных, чтобыбыть удаленным (добавив новый логический столбец по умолчанию в false и сделайте update_column :to_destroy, true).

Таким образом, вы можете сделать Thing.where (id: ids_to_destroy) .destroy_all (или что-то вроде Thing.where (to_destroy: true)) .destroy_all, если вы отметили их) после цикла.

Какой метод использовать, зависит от ваших потребностей.Я бы пошел с сохранением идентификаторов в массиве, так как он требует меньше изменений, но, возможно, вы обрабатываете действительно большой объем данных, и сохранение действительно очень большого массива в памяти - это слишком много (не обычный случай, так как вы просто храните идентификаторы,но это возможно, хотя).

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