Если я не ошибаюсь, существует поле с названием deleted_at.
Если запись удалена, там должен быть временной штамп.
Если запись не удалена, то значение null.
Возможно, стоит найти запись, которая <> Null, и удалить её.
Если я не ошибаюсь, существует поле с названием deleted_at.
Если запись удалена, там должен быть временной штамп.
Если запись не удалена, то значение null.
Возможно, стоит найти запись, которая <> Null, и удалить её.
Спасибо, Гав, но те, на которые я нацелен, не помечены как удалённые. Скорее, их тема была удалена, а затем выполнена команда destroy_all.
Думаю, вы видите, как 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 methoddestroy_all' for #<Array:0x000055fe7bc7fc98> from (pry):3:inpry’
О. Тогда, возможно, проверьте, является ли
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, не могли бы вы помочь?
Я даже попытался преобразовать 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 }
Отлично, рад слышать, что это помогло. Таким образом, для объединения ответов можно использовать следующее в консоли 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) и столкнулись с проблемами, или вы вообще не пробовали это без ограничения?
Я пробовал без ограничения, и что-то пошло немного не так — но я забыл детали, извините.
Можем ли мы пометить ваш пост как решение или использовать это из первого сообщения темы?

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