سؤال

كل يوم ، أتلقى مخزونًا من المستندات (تحديث). ما أريد القيام به هو إدراج كل عنصر غير موجود بالفعل.

  • أريد أيضًا تتبع المرة الأولى التي أدخلتها فيها ، وآخر مرة رأيتها في تحديث.
  • لا أريد أن يكون لدي مستندات مكررة.
  • لا أرغب في إزالة مستند تم حفظه مسبقًا ، لكن ليس في التحديث الخاص بي.
  • 95 ٪ (المقدرة) من السجلات غير معدلة من يوم لآخر.

أنا أستخدم برنامج تشغيل Python (Pymongo).

ما أقوم به حاليًا هو (الكود الزائف):

for each document in update:
      existing_document = collection.find_one(document)
      if not existing_document:
           document['insertion_date'] = now
      else:
           document = existing_document
      document['last_update_date'] = now
      my_collection.save(document)

مشكلتي هي أنها بطيئة للغاية (40 دقيقة لأقل من 100000 سجل ، ولدي ملايين منهم في التحديث). أنا متأكد تمامًا من وجود شيء مبني للقيام بذلك ، لكن المستند الخاص بـ Update () هو MMMHHH ....http://www.mongodb.org/display/docs/updating )

هل يمكن لأحد أن ينصح كيفية القيام بذلك بشكل أسرع؟

هل كانت مفيدة؟

المحلول

يبدو أنك تريد القيام "بالتراجع". MongoDB لديه دعم مدمج لهذا. تمرير معلمة إضافية إلى تحديثك () استدعاء: {Upsert: True}. علي سبيل المثال:

key = {'key':'value'}
data = {'key2':'value2', 'key3':'value3'};
coll.update(key, data, upsert=True); #In python upsert must be passed as a keyword argument

هذا يحل محل كتلة IF-Find-Else-update بالكامل. سيتم إدراجه إذا لم يكن المفتاح موجودًا وسيتم تحديثه إذا حدث ذلك.

قبل:

{"key":"value", "key2":"Ohai."}

بعد:

{"key":"value", "key2":"value2", "key3":"value3"}

يمكنك أيضًا تحديد البيانات التي تريد كتابتها:

data = {"$set":{"key2":"value2"}}

الآن سيقوم المستند الذي اخترته بتحديث قيمة "key2" فقط وترك كل شيء آخر دون مساس.

نصائح أخرى

اعتبارًا من MongoDB 2.4 ، يمكنك استخدام $ setoninsert (http://docs.mongodb.org/manual/reference/operator/setoninsert/)

قم بتعيين "insertion_date" باستخدام $ setoninsert و "last_update_date" باستخدام تعيين $ في أمر upsert الخاص بك.

لتحويل الرمز الكاذب إلى مثال عمل:

now = datetime.utcnow()
for document in update:
    collection.update_one(
        {"_id": document["_id"]},
        {
            "$setOnInsert": {"insertion_date": now},
            "$set": {"last_update_date": now},
        },
        upsert=True,
    )

يمكنك دائمًا جعل فهرس فريد ، مما يؤدي إلى رفض MongoDB حفظ المتضاربة. النظر في ما يلي باستخدام قذيفة mongodb:

> db.getCollection("test").insert ({a:1, b:2, c:3})
> db.getCollection("test").find()
{ "_id" : ObjectId("50c8e35adde18a44f284e7ac"), "a" : 1, "b" : 2, "c" : 3 }
> db.getCollection("test").ensureIndex ({"a" : 1}, {unique: true})
> db.getCollection("test").insert({a:2, b:12, c:13})      # This works
> db.getCollection("test").insert({a:1, b:12, c:13})      # This fails
E11000 duplicate key error index: foo.test.$a_1  dup key: { : 1.0 }

يمكنك استخدام Upsert مع مشغل $ setoninsert.

db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})

1. استخدم التحديث.

بالاعتماد على إجابة Van Nguyen أعلاه ، استخدم التحديث بدلاً من الحفظ. يمنحك هذا الوصول إلى خيار Upsert.

ملاحظة: هذه الطريقة تتجاوز المستند بأكمله عند العثور عليه (من المستندات)

var conditions = { name: 'borne' }   , update = { $inc: { visits: 1 }} , options = { multi: true };

Model.update(conditions, update, options, callback);

function callback (err, numAffected) {   // numAffected is the number of updated documents })

1.A. استخدم مجموعة $

إذا كنت ترغب في تحديث مجموعة مختارة من المستند ، ولكن ليس كل شيء ، يمكنك استخدام طريقة SET $ مع التحديث. (تكرارا، من المستندات) ... لذا ، إذا كنت تريد ضبط ...

var query = { name: 'borne' };  Model.update(query, ***{ name: 'jason borne' }***, options, callback)

أرسلها كما ...

Model.update(query, ***{ $set: { name: 'jason borne' }}***, options, callback)

هذا يساعد على منع الكتابة فوق كل المستندات (المستندات) عن طريق الخطأ { name: 'jason borne' }.

لا أعتقد أن MongoDB يدعم هذا النوع من الترشيح الانتقائي. لدي نفس المشكلة مثل Lemiz ، واستخدام تحديث (المعايير ، NewObj ، Upsert ، Multi) لا يعمل بشكل صحيح عند التعامل مع الطابع الزمني "الذي تم إنشاؤه" و "محدثة". بالنظر إلى البيان التالي:

update( { "name": "abc" }, 
        { $set: { "created": "2010-07-14 11:11:11", 
                  "updated": "2010-07-14 11:11:11" }},
        true, true ) 

السيناريو #1-مستند مع "اسم" من "ABC" غير موجود: يتم إنشاء مستند جديد باستخدام "name" = "ABC" ، "تم إنشاؤه" = 2010-07-14 11:11:11 ، و "التحديث" = 2010-07-14 11:11:11.

السيناريو رقم 2-مستند مع "اسم" من "ABC" موجود بالفعل مع ما يلي: "الاسم" = "ABC" ، "تم إنشاؤه" = 2010-07-12 09:09:09 ، و "التحديث" = 2010-07 -13 10:10:10. بعد الانتعاش ، سيكون المستند الآن هو نفس النتيجة في السيناريو رقم 1. لا توجد وسيلة لتحديدها في مرحلة التصميم التي يتم تعيين الحقول إذا تم إدخالها ، وأي الحقول تترك بمفردها إذا تم تحديثها.

كان الحل الخاص بي هو إنشاء فهرس فريد على Critera الحقول ، وإجراء إدراج ، وبعد ذلك مباشرة قم بإجراء تحديث فقط على حقل "التحديث".

ملخص

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

لاحظ ، أنا أفترض أن Pymongo ، تغيير لتناسب لغتك المفضلة.

تعليمات:

  1. قم بإنشاء المجموعة باستخدام فهرس مع فريد = صحيح حتى لا تحصل على سجلات مكررة.

  2. تكرار على سجلات الإدخال الخاصة بك ، وإنشاء دفعات منها من 15000 سجل أو نحو ذلك. لكل سجل في الدُفعة ، قم بإنشاء قولان يتكون من البيانات التي تريد إدراجها ، على افتراض أن كل منها سيكون سجلًا جديدًا. أضف الطابع الزمني "الذي تم إنشاؤه" و "محدثة" إلى هذه. قم بإصدار هذا كأمر إدراج دفعة مع علامة "ContereNerror" = صحيح ، وبالتالي فإن إدراج كل شيء آخر يحدث حتى لو كان هناك مفتاح مكرر هناك (يبدو أنه سيكون هناك). سيحدث هذا بسرعة كبيرة. سائبة إدراج الصخور ، لقد حصلت على مستويات الأداء 15K/ثانية. المزيد من الملاحظات على Conteronerror ، انظر http://docs.mongodb.org/manual/core/write-operations/

    تحدث إدراج السجل بسرعة كبيرة ، لذلك ستتم القيام به مع تلك الإدراج في أي وقت من الأوقات. الآن ، حان الوقت لتحديث السجلات ذات الصلة. افعل هذا مع استرجاع الدُفعة ، أسرع بكثير من واحد في وقت واحد.

  3. تكرار على جميع سجلات الإدخال الخاصة بك مرة أخرى ، وإنشاء دفعات من 15 كيلو أو نحو ذلك. استخراج المفاتيح (الأفضل إذا كان هناك مفتاح واحد ، ولكن لا يمكن مساعدته إذا لم يكن هناك). استرجاع هذه المجموعة من السجلات من Mongo باستخدام استعلام db.collectionNameblah.find ({field: {$ in: [1 ، 2،3 ...}). لكل من هذه السجلات ، حدد ما إذا كان هناك تحديث ، وإذا كان الأمر كذلك ، قم بإصدار التحديث ، بما في ذلك تحديث الطابع الزمني "المحدث".

    لسوء الحظ ، يجب أن نلاحظ أن MongoDB 2.4 وما يلي لا تتضمن عملية تحديث بالجملة. إنهم يعملون على ذلك.

نقاط التحسين الرئيسية:

  • ستعمل الإدراج على تسريع عملياتك بكميات كبيرة.
  • إن استرداد السجلات بشكل جماعي سيؤدي إلى تسريع الأمور أيضًا.
  • التحديثات الفردية هي المسار الوحيد الممكن الآن ، لكن 10Gen تعمل عليه. من المفترض أن يكون هذا في 2.6 ، على الرغم من أنني لست متأكدًا مما إذا كان سيتم الانتهاء من ذلك بحلول ذلك الوقت ، فهناك الكثير من الأشياء التي يجب القيام بها (كنت أتابع نظام JIRA الخاص بهم).

بشكل عام ، يكون استخدام التحديث أفضل في MongoDB لأنه سيقوم فقط بإنشاء المستند إذا لم يكن موجودًا بعد ، على الرغم من أنني لست متأكدًا من كيفية العمل مع محول Python الخاص بك.

ثانياً ، إذا كنت بحاجة فقط إلى معرفة ما إذا كان هذا المستند موجودًا أم لا ، فإن عد () الذي يعيد سوى رقم سيكون خيارًا أفضل من Find_one الذي من المفترض أن ينقل المستند بالكامل من MongoDB الذي يسبب حركة مرور غير ضرورية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top