Удалить удалённые посты навсегда массово?

Если я не ошибаюсь, существует поле с названием deleted_at.

Если запись удалена, там должен быть временной штамп.

Если запись не удалена, то значение null.

Возможно, стоит найти запись, которая <> Null, и удалить её.

Спасибо, Гав, но те, на которые я нацелен, не помечены как удалённые. Скорее, их тема была удалена, а затем выполнена команда destroy_all.

Я немного поигрался с этим и заметил, что часть вашего запроса не возвращает нужный ответ.

SELECT topic_id from posts не возвращает целое число, а возвращает строку

Это может быть причиной того, что в вашей базе данных всё ещё содержатся «сиротские» записи.

Думаю, вы видите, как Data Explorer автоматически преобразует целое число в URL, что он делает, когда меткой является topic_id.

Когда я запускаю этот запрос в Data Explorer, все посты, которые я пытаюсь идентифицировать, захватываются (более 5000):

SELECT id, topic_id
FROM posts
WHERE topic_id not in (select id from topics)
ORDER by id

Очевидно, я что-то неправильно делаю с синтаксисом Rails, потому что получаю следующее:

[1] pry(main)> Target = Post.where('topic_id not in (select id from topics)')
=> []

Может ли кто-нибудь сказать, что я делаю не так?

Что ж, благодаря @pfaffman я нашел соответствующие посты, используя это:

Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")

Вот что я получаю:

[1] pry(main)> Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”)
=> [#<Post:0x000055df30d4ee90 id: 150>,
#<Post:0x000055df2e538ff0 id: 51097>,
#<Post:0x000055df2e50ba28 id: 83>,
#<Post:0x000055df2e4ee8b0 id: 40636>,
#<Post:0x000055df2e4a92d8 id: 62562>,
#<Post:0x000055df2e4b7978 id: 13522>,
etc

Однако я не могу понять, как затем применить destroy_all к этому набору данных.

Это может помочь (для моего собственного понимания):

Есть какие-либо предложения?

Думаю, этого будет достаточно

posts =
Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")


posts.destroy_all

Или вы можете добавить .destroy_all к вашему find_by_sql.

Я пробовал это. Данные, похоже, возвращаются в виде массива с идентификатором поста и ID (см. Delete deleted-posts permanently in bulk? - #45 by nathank).

Вот ошибка, которую я получаю, когда добавляю .destroy_all или использую posts=, как вы предлагаете:

[2] pry(main)> posts.destroy_all
NoMethodError: undefined method destroy_all' for #<Array:0x000055fe7bc7fc98> from (pry):3:in pry

О. Тогда, возможно, проверьте, является ли

 p=posts.first

идентификатором поста. Если да, то вы можете

x=Post.find(p)
x.destroy

Затем вы можете перебрать их.

Кажется, вам нужно обернуть ваш запрос в нечто такое, чтобы он возвращал массив постов, а не массив идентификаторов постов.

Спасибо, Джей. Вот что я получаю:

[3] pry(main)> p=
[3] pry(main)* posts.first
=> #<Post:0x0000563a24cab908 id: 150>
[4] pry(main)> x=Post.find(p)
ArgumentError: Вы передаёте экземпляр ActiveRecord::Base в find. Пожалуйста, передайте ID объекта, вызвав .id.
from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/relation/finder_methods.rb:467:in `find_one’

Теперь расскажи мне об этих «обёртках». Это так раздражает, потому что я знаю, что команда Rails от @Sam должна была работать, но, кажется, с тех пор Rails изменился:

Что это сделало? Не могу представить, что Rails изменились.

Это даст вам посты, которые вы хотите удалить?

Я где-то в интернете читал, что при поиске информации о преобразовании массива Active Record в отношение Active Record между Rails 3.x и Rails 4 произошли изменения, и синтаксис должен быть другим, но я это не до конца понял.

Кажется, при первом запуске он нашел несколько, и я сразу же вызвал destroy_all для них. Но их было немного. Сейчас он вообще ничего не находит, тогда как SQL-запрос в Data Explorer находит тысячи.

Это сиротские посты, у которых тема была удалена через destroy_all.

Можно ли использовать each{} для перебора элементов этого массива, вызывая destroy для каждого поста по отдельности?

Post.find_by_sql("select id from posts where topic_id not in (select id from topics)").each { |p| p.destroy }

Что ж, я попробовал. Сначала не понравилось, что SQL-запрос обернут таким образом, да и синтаксис не подошел:

Post.find_by_sql(“select id from posts where topic_id not in (select id from topics)”).each { |p| p.destroy_all }
SyntaxError: unexpected `in’, expecting ‘(’
…rom posts where topic_id not in (select id from topics)”)…
… ^~
SyntaxError: unexpected local variable or method, expecting end-of-input
…t in (select id from topics)”).each { |p| p.destroy_all }

Тогда я попробовал другой, более «костыльный» вариант, разбив запрос на части:

posts=Post.find_by_sql("select id from posts where topic_id not in (select id from topics)")
posts.each do |p|
p.destroy
end

Казалось, что это сработало, но добавление p.destroy вызвало следующую ошибку:

ActiveModel::MissingAttributeError: missing attribute: user_id
from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activemodel-6.1.4.1/lib/active_model/attribute.rb:222:in `value’

Я попробовал несколько способов включить это, но потом сдался. @sam, не могли бы вы помочь?

P.S.

Я даже попытался преобразовать SQL в ActiveRecord через scuttle.io, как указано здесь:

Post.select([:id, :topic_id]).where(Topic.select(:id))

Увы, получил эту ошибку:

ArgumentError: Unsupported argument type: #Topic::ActiveRecord_Relation:0x000055c67a7131d0 (Topic::ActiveRecord_Relation)

Когда я скопировал и вставил эту часть из предыдущего сообщения, похоже, что кавычки где-то превратились в фигурные кавычки. Думаю, это и есть настоящая ошибка. Извините за это.

Метод find_by_sql описывается как возвращающий объект со значениями, указанными в SQL-запросе, что, по всей видимости, означает, что вы получаете объект Post, у которого установлен только свойство id, а user_id и всё остальное отсутствуют.

... find_by_sql("select * ... решит эту проблему. Вероятно, существует подмножество значений, которые можно выбрать для выполнения удаления, вместо выбора всего, но я не знаю, что это за подмножество.

Итак, всё вместе: (на этот раз без фигурных кавычек…)

Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }

:partying_face: Аллилуйя!!! :partying_face:

Спасибо, Саймон — сработало просто отлично. Все «сиротские» посты удалены, и я с нетерпением жду, как мои загрузки уменьшатся почти до нуля в течение следующих 24 часов.

позже
И они уменьшились! С 3,5 ГБ до 0,7 ГБ. Замечательно!!

Отлично, рад слышать, что это помогло. Таким образом, для объединения ответов можно использовать следующее в консоли Rails, чтобы уничтожить все темы, удаленные более 90 дней назад, повторяя команду столько раз, сколько необходимо, если тем больше 1000:

Topic.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all

После завершения этого процесса можно использовать следующее, чтобы уничтожить все сообщения, ставшие сиротами из-за уничтоженных тем:

Post.find_by_sql("select * from posts where topic_id not in (select id from topics)").each { |p| p.destroy }

Стоит отметить, что вышеуказанные команды не уничтожат удалённые сообщения, только удалённые темы и их сиротские сообщения. Чтобы также уничтожить удалённые сообщения старше 90 дней, используйте следующее, снова повторяя при необходимости:

Post.with_deleted.where(deleted_at: ...90.days.ago).limit(1000).destroy_all

P.S. Из любопытства: вы пробовали использовать destroy_all без limit(1000) и столкнулись с проблемами, или вы вообще не пробовали это без ограничения?

Я пробовал без ограничения, и что-то пошло немного не так — но я забыл детали, извините.

Можем ли мы пометить ваш пост как решение или использовать это из первого сообщения темы?

image

Не переживайте. Если бы вы не пробовали, я бы добавил примечание о том, что ограничение может быть не нужно, но раз у вас возникли проблемы, я оставлю всё как есть.