حذف المنشورات المحذوفة بشكل دائم دفعة واحدة؟

إذا لم أكن مخطئًا، هناك حقل يُسمى deleted_at.

إذا تم الحذف، فيجب أن يكون هناك طابع زمني (date time stamp).

وإذا لم يتم الحذف، فإن القيمة تكون null.

ربما يمكنك البحث عن الإدخال الذي لا يساوي null وحذفه.

شكرًا لك يا جاف، لكن تلك التي أستهدفها ليست معلمة كمحذوفة. بل إن موضوعها قد حُذف ثم تم تدميرها بالكامل باستخدام destroy_all.

لقد جربت هذا وألاحظ أن جزءًا من استعلامك لم يعيد الاستجابة التي تحتاجها.

SELECT topic_id from posts لا يُرجع عددًا صحيحًا، بل يُرجع نصًا

قد يكون هذا هو السبب في أن قاعدة بياناتك لا تزال تحتوي على منشورات غير مرتبطة.

أعتقد أنك ترى مستكشف البيانات يقوم تلقائيًا بتحويل العدد الصحيح إلى عنوان URL، وهو ما يفعله عندما يكون التسمية topic_id.

عندما أقوم بتشغيل هذا الاستعلام في مستكشف البيانات، يتم التقاط جميع المشاركات التي أحاول تحديدها (أكثر من 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>,
إلخ.

ومع ذلك، لا يمكنني معرفة كيفية تطبيق 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: You are passing an instance of ActiveRecord::Base to find. Please pass the id of the object by calling .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 قد تغير منذ ذلك الحين:

ماذا فعل ذلك؟ لا أستطيع أن أتخيل أن القضبان قد تغيرت.

هل يعطيك هذا المشاركات التي تريد حذفها؟

لقد قرأت في مكان ما على الإنترنت عند البحث عن تحويل مصفوفة Active Record إلى علاقة Active Record أن الأمور تغيرت بين Rails 3.x و Rails 4 وأن الصيغة تحتاج إلى أن تكون مختلفة، لكنها مرت فوق رأسي حقًا.

يبدو أن هذا قد التقط القليل عندما قمت بتشغيله لأول مرة، وقمت بتدميرها جميعًا. لكن لم يكن هناك الكثير. الآن لا يلتقط أي شيء على الإطلاق، بينما يلتقط SQL الآلاف عند تشغيله في مستكشف البيانات.

هذه منشورات يتيمة، حيث تم تدمير الموضوع بالكامل.

هل يمكنك استخدام 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 إلى AR عبر 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

ملاحظة جانبية: من باب الفضول، هل جربت destroy_all بدون limit(1000) وواجهت مشاكل، أم أنك لم تجربه بدون الحد؟

جربته بدون الحد وحدث شيء جنوني بعض الشيء - لكنني نسيت التفاصيل للأسف.

هل يمكننا وضع علامة على مشاركتك كحل أو استخدام هذا من المنشور الأصلي؟

image

لا تقلق. إذا لم تكن قد جربتها، لكنت أضفت ملاحظة بأن الحد قد لا يكون ضروريًا، ولكن بما أنك واجهت مشاكل، يمكنني تركها كما هي.