mongodb: यदि मौजूद नहीं है तो सम्मिलित करें


146

हर दिन, मुझे दस्तावेजों का एक स्टॉक (एक अद्यतन) प्राप्त होता है। मैं जो करना चाहता हूं वह प्रत्येक आइटम को सम्मिलित करता है जो पहले से मौजूद नहीं है।

  • मैं पहली बार उन पर नज़र रखना चाहता हूं, और आखिरी बार मैंने उन्हें अपडेट में देखा था।
  • मुझे डुप्लिकेट दस्तावेज़ नहीं चाहिए।
  • मैं एक दस्तावेज को हटाना नहीं चाहता, जो पहले सहेजा जा चुका है, लेकिन मेरे अपडेट में नहीं है।
  • रिकॉर्ड का 95% (अनुमानित) दिन-प्रतिदिन अनमोड किया जाता है।

मैं पायथन ड्राइवर (पाइमोंगो) का उपयोग कर रहा हूं।

वर्तमान में मैं क्या कर रहा हूँ (छद्म कोड):

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)

मेरी समस्या यह है कि यह बहुत धीमी है (100 000 से कम रिकॉर्ड के लिए 40 मिनट, और मेरे पास अपडेट में लाखों हैं)। मुझे पूरा यकीन है कि ऐसा करने के लिए कुछ बनाया गया है, लेकिन अद्यतन के लिए दस्तावेज़ () mmmhhh है .... थोड़ा सा .... ( http://www.mongodb.org/display/DOCS/Updating )

क्या कोई इसे तेजी से करने की सलाह दे सकता है?

जवाबों:


153

लगता है कि आप एक "तेज" करना चाहते हैं। 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 block को पूरी तरह से बदल देता है। यदि कुंजी मौजूद नहीं है, तो यह सम्मिलित करेगा और यदि यह करता है तो अद्यतन करेगा।

इससे पहले:

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

उपरांत:

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

आप यह भी निर्दिष्ट कर सकते हैं कि आप कौन सा डेटा लिखना चाहते हैं:

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

अब आपका चयनित दस्तावेज़ केवल "की 2" के मूल्य को अपडेट करेगा और बाकी सब को अछूता छोड़ देगा।


5
यह लगभग वही है जो मैं चाहता हूं! यदि ऑब्जेक्ट पहले से मौजूद है तो मैं सम्मिलन_डेट फ़ील्ड को कैसे नहीं छू सकता हूं?
LeMiz

24
क्या आप कृपया पहली प्रविष्टि पर फ़ील्ड सेट करने का उदाहरण दे सकते हैं और मौजूद होने पर उसे अपडेट नहीं कर सकते हैं? @VanNguyen
अली शाकिबा

7
आपके उत्तर का पहला हिस्सा गलत है, मुझे लगता है। जब तक आप $ सेट का उपयोग नहीं करेंगे तब coll.update डेटा को बदल देगा । तो इसके बाद वास्तव में हो जाएगा: {'key2': 'value2', 'key3': 'value3'}
जेम्स ब्लैकबर्न

9
-1 यह जवाब खतरनाक है। आप "कुंजी" के मूल्य से पाते हैं और फिर आप "कुंजी" को मिटा देते हैं, ताकि बाद में आप इसे फिर से नहीं ढूंढ पाएंगे। यह एक बहुत ही कम उपयोग का मामला है।
मार्क ई। हासे

23
आपको $ setOnInsert ऑपरेटर का उपयोग करना चाहिए! यदि क्वेरी मिली, तो अपग्रेड भी दस्तावेज़ को अपडेट करेगा।
यूलचेनी

65

MongoDB 2.4 के अनुसार, आप $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ ) का उपयोग कर सकते हैं

अपनी प्रविष्टि कमांड में $ सेट का उपयोग करके $ setOnInsert और 'last_update_date' का उपयोग करके 'सम्मिलन_डेट' सेट करें।

अपने छद्मकोड को कार्यशील उदाहरण में बदलने के लिए:

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

3
यह सही है, आप एक फ़िल्टर से मेल खाते दस्तावेज़ की जाँच कर सकते हैं, और अगर $ setOnInsert का उपयोग करके नहीं मिला तो कुछ डालें। ध्यान दें कि वहाँ एक बग था जहाँ आप $ setOnInsert नहीं कर सकते थे _id फ़ील्ड के साथ - यह कुछ ऐसा कहेगा जैसे "" मॉड द _आईडी फ़ील्ड नहीं कर सकता "। यह एक बग था, जो v2.5.4 में तय किया गया था या इसके बारे में था। यदि आप यह संदेश या समस्या देखते हैं, तो बस नवीनतम संस्करण प्राप्त करें।
कीरेन जॉनस्टोन

19

आप हमेशा एक अद्वितीय सूचकांक बना सकते हैं, जो 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 }

12

आप $ setOnInsert ऑपरेटर के साथ उपर का उपयोग कर सकते हैं।

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


11
किसी के लिए तीसरे परम के साथ क्वेरी करने के लिए सिर्फ सही होना चाहिए या सही होना चाहिए या सही नहीं होना चाहिए
एस ..

6

1. अपडेट का उपयोग करें।

ऊपर वैन गुयेन के उत्तर से आकर्षित, बचाने के बजाय अद्यतन का उपयोग करें। यह आपको मुखर विकल्प तक पहुंच प्रदान करता है।

नोट : यह विधि पूरे दस्तावेज़ को ओवरराइड करता है जब मिला ( डॉक्स से )

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. $ सेट का उपयोग करें

यदि आप दस्तावेज़ के चयन को अपडेट करना चाहते हैं, लेकिन पूरी बात नहीं है, तो आप अपडेट के साथ $ सेट विधि का उपयोग कर सकते हैं। (फिर, डॉक्स से ) ... इसलिए, यदि आप सेट करना चाहते हैं ...

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

इसे इस रूप में भेजें ...

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

यह आपके सभी दस्तावेज़ों के साथ गलती से ओवरराइटिंग को रोकने में मदद करता है { name: 'jason borne' }


6

सारांश

  • आपके पास अभिलेखों का एक मौजूदा संग्रह है।
  • आपके पास एक सेट रिकॉर्ड है जिसमें मौजूदा रिकॉर्ड के अपडेट हैं।
  • कुछ अपडेट वास्तव में कुछ भी अपडेट नहीं करते हैं, वे नकल करते हैं जो आपके पास पहले से हैं।
  • सभी अद्यतनों में वही फ़ील्ड होते हैं जो पहले से ही हैं, बस संभवतः अलग-अलग मान हैं।
  • जब कोई रिकॉर्ड पिछली बार बदला गया था, तो आप ट्रैक करना चाहते हैं, जहां वास्तव में एक मूल्य बदल गया है।

ध्यान दें, मैं PyMongo को मान रहा हूं, अपनी पसंद की भाषा में बदलाव करें।

निर्देश:

  1. यूनिक = ट्रू के साथ इंडेक्स के साथ कलेक्शन बनाएं ताकि आपको डुप्लिकेट रिकॉर्ड न मिले।

  2. अपने इनपुट रिकॉर्ड्स में फेरबदल करते हुए, उनमें से 15,000 रिकॉर्ड या तो बैच बनाते हैं। बैच में प्रत्येक रिकॉर्ड के लिए, आप जिस डेटा को सम्मिलित करना चाहते हैं, उससे मिलकर एक तानाशाह बनाएं, प्रत्येक को एक नया रिकॉर्ड बनाने जा रहा है। इनमें 'बनाया' और 'अपडेटेड' टाइमस्टैम्प जोड़ें। इसे 'ContinueOnError' ध्वज = सही के साथ एक बैच इन्सर्ट कमांड के रूप में जारी करें, इसलिए बाकी सभी चीज़ों की प्रविष्टि तब भी होती है, जब वहाँ कोई डुप्लिकेट कुंजी हो (जिसमें लगता है कि यह वहां होगा)। यह बहुत जल्दी होगा। बल्क आवेषण रॉक, मैंने 15k / सेकंड प्रदर्शन स्तर प्राप्त किया है। ContinueOnError पर आगे के नोट्स, http://docs.mongodb.org/manual/core/write-operations/ देखें

    रिकॉर्ड आवेषण बहुत जल्दी होते हैं, इसलिए आप कुछ ही समय में उन आवेषण के साथ किया जाएगा। अब, प्रासंगिक रिकॉर्ड अपडेट करने का समय आ गया है। एक बैच पुनर्प्राप्ति के साथ ऐसा करें, एक समय में एक से अधिक तेजी से।

  3. 15K या तो के बैच बनाने, फिर से अपने सभी इनपुट रिकॉर्ड्स पर Iterate। कुंजियाँ बाहर निकालें (यदि एक कुंजी है तो सबसे अच्छा है, लेकिन अगर वहाँ नहीं है तो मदद नहीं की जा सकती)। Db.collectionNameBlah.find ({फ़ील्ड: {$ in: [1, 2,3 ...}) क्वेरी के साथ मोंगो के रिकॉर्ड के इस समूह को पुनः प्राप्त करें। इनमें से प्रत्येक रिकॉर्ड के लिए, निर्धारित करें कि क्या कोई अपडेट है, और यदि ऐसा है, तो अपडेट को जारी करें, जिसमें 'अपडेटेड' टाइमस्टैम्प को अपडेट करना भी शामिल है।

    दुर्भाग्य से, हम ध्यान दें, MongoDB 2.4 और नीचे एक बल्क अपडेट ऑपरेशन शामिल नहीं है। वे उस पर काम कर रहे हैं।

मुख्य अनुकूलन अंक:

  • आवेषण काफी हद तक थोक में अपने संचालन को गति देगा।
  • अभिलेखों को पुनः दर्ज करने से भी चीज़ों की गति बढ़ेगी।
  • व्यक्तिगत अपडेट अब एकमात्र संभव मार्ग है, लेकिन 10Gen इस पर काम कर रहा है। मुमकिन है, यह 2.6 में होगा, हालांकि मुझे यकीन नहीं है कि यह तब तक खत्म हो जाएगा, करने के लिए बहुत कुछ है (मैं उनके जीरा सिस्टम का पालन कर रहा हूं)।

5

मुझे नहीं लगता कि मोंगोडब इस प्रकार के चयनात्मक उत्थान का समर्थन करता है। मुझे LeMiz जैसी ही समस्या है, और 'बनाया' और 'अपडेटेड' टाइमस्टैम्प दोनों के साथ काम करते समय अपडेट (मानदंड, न्यूऑब्ज, अपग्रेड, मल्टी) का उपयोग करना सही नहीं है। निम्नलिखित उक्त कथन को देखते हुए:

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

परिदृश्य # 1 - 'एबीसी' के 'नाम' के साथ दस्तावेज़ मौजूद नहीं है: नया दस्तावेज़ 'नाम' = 'एबीसी', 'निर्मित' = 2010-07-14 11:11:11, और 'अद्यतन' = के साथ बनाया गया है। 2010-07-14 11:11:11।

परिदृश्य # 2 - 'एबीसी' के 'नाम' के साथ दस्तावेज़ पहले से ही मौजूद है: 'नाम' = 'एबीसी', 'निर्मित' = 2010-07-12 09:09:09, और 'अपडेटेड' = 2010-07 -13 10:10:10। उत्थान के बाद, दस्तावेज़ अब परिदृश्य # 1 में परिणाम के समान होगा। उखाड़ने में निर्दिष्ट करने का कोई तरीका नहीं है कि सम्मिलित करते समय कौन से फ़ील्ड सेट किए जाएं, और अपडेट करते समय किन फ़ील्ड को अकेला छोड़ दिया जाए।

मेरा समाधान समीरा क्षेत्रों पर एक अद्वितीय सूचकांक बनाना , एक प्रदर्शन करना, और तुरंत बाद में केवल 'अद्यतन' फ़ील्ड पर एक अद्यतन करना था।


4

सामान्य तौर पर, अपडेट का उपयोग करना MongoDB में बेहतर है क्योंकि यह अभी दस्तावेज़ बनाएगा यदि यह अभी तक मौजूद नहीं है, हालांकि मुझे यकीन नहीं है कि आपके अजगर एडाप्टर के साथ कैसे काम करना है।

दूसरा, यदि आपको केवल यह जानना है कि दस्तावेज़ मौजूद है या नहीं, तो गणना करें () जो केवल एक संख्या देता है, वह find_one की तुलना में एक बेहतर विकल्प होगा, जो आपके MongoDB से पूरे दस्तावेज़ को अनावश्यक रूप से ट्रैफ़िक के कारण हस्तांतरित करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.