mongodb: yoksa ekle


146

Her gün bir belge stoğu (güncelleme) alıyorum. Ne yapmak istiyorum zaten mevcut olmayan her öğeyi eklemek.

  • Ayrıca ilk taktığımda ve en son ne zaman bir güncellemede gördüğümü takip etmek istiyorum.
  • Yinelenen belgelere sahip olmak istemiyorum.
  • Daha önce kaydedilmiş, ancak güncellememde olmayan bir belgeyi kaldırmak istemiyorum.
  • Kayıtların% 95'i (tahmini) günden güne değiştirilmez.

Python sürücüsünü (pymongo) kullanıyorum.

Şu anda ne (sahte kod):

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)

Benim sorunum çok yavaş (100 000'den az kayıt için 40 dakika ve güncellemede milyonlarca var). Bunu yapmak için yerleşik bir şey olduğundan eminim, ama update () için belge mmmhhh .... biraz kısa .... ( http://www.mongodb.org/display/DOCS/Updating )

Birisi nasıl daha hızlı yapılacağını önerebilir mi?

Yanıtlar:


153

Kulağa "yukarı" yapmak istiyor gibisin. MongoDB bunun için yerleşik bir desteğe sahiptir. Update () çağrınıza fazladan bir parametre iletin: {upsert: true}. Örneğin:

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

Bu, eğer bul - else-update bloğunuzun yerini alır. Anahtar yoksa eklenir ve varsa güncellenir.

Önce:

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

Sonra:

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

Hangi verileri yazmak istediğinizi de belirtebilirsiniz:

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

Şimdi seçtiğiniz doküman yalnızca "key2" değerini güncelleyecek ve diğer her şeye dokunulmayacak.


5
Neredeyse istediğim bu! Nesne zaten mevcutsa insertion_date alanına nasıl dokunamam?
LeMiz

24
lütfen sadece ilk kesici uçta bir alan belirleme örneğini verebilir ve varsa güncelleme yapamaz mısınız? @VanNguyen
Ali Shakiba

7
Cevabınızın ilk kısmı yanlış bence. $ set kullanmazsanız coll.update verilerin yerini alır . Yani After aslında: {'key2': 'value2', 'key3': 'value3'}
James Blackburn

9
-1 Bu cevap tehlikelidir. "Anahtar" değerine göre bulursunuz ve daha sonra "anahtar" ı silersiniz, böylece daha sonra tekrar bulamazsınız. Bu çok olası bir durum değildir.
Mark E. Haase

23
$ SetOnInsert operatörünü kullanmalısınız! Sorgu bulunursa, Upsert belgeyi bile güncelleyecektir.
YulCheney

65

MongoDB 2.4'ten itibaren $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ kullanabilirsiniz. )

$ SetOnInsert kullanarak 'insertion_date' ve upsert komutunuzda $ set kullanarak 'last_update_date' ayarlayın.

Sahte kodunuzu çalışan bir örneğe dönüştürmek için:

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
Bu doğrudur, $ setOnInsert kullanarak bir filtreyle eşleşen belgeyi kontrol edebilir ve bulunamazsa bir şeyler ekleyebilirsiniz. _İd alanı ile $ setOnInsert yapamayacağınız bir hata olduğunu unutmayın - "_id alanını modlandıramaz" gibi bir şey söyleyecektir. Bu bir hata, v2.5.4 veya orada düzeltildi. Bu mesajı veya sorunu görürseniz, en son sürümü edinin.
Kieren Johnstone

19

Her zaman benzersiz bir dizin oluşturabilirsiniz, bu da MongoDB'nin çakışan bir kaydetmeyi reddetmesine neden olur. Mongodb kabuğunu kullanarak aşağıdakileri düşünün:

> 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 }


6

1. Güncelle seçeneğini kullanın.

Yukarıdaki Van Nguyen'ın cevabından yola çıkarak kaydetmek yerine güncellemeyi kullanın. Bu size upsert seçeneğine erişmenizi sağlar.

NOT : Bu yöntem, bulunduğunda dokümanın tamamını geçersiz kılar ( Dokümanlardan )

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 kullan

Belgenin bir seçimini güncellemek istiyorsanız, ancak her şeyi güncellemiyorsanız, $ set yöntemini update ile kullanabilirsiniz. (yine, Dokümanlar'dan ) ... Yani, ayarlamak isterseniz ...

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

Farklı gönder ...

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

Bu, tüm belgelerinizin yanlışlıkla üzerine yazılmasını önlemeye yardımcı olur { name: 'jason borne' }.


6

özet

  • Mevcut bir kayıt koleksiyonunuz var.
  • Varolan kayıtların güncelleştirmelerini içeren bir küme kaydınız var.
  • Bazı güncellemeler gerçekten hiçbir şeyi güncellemez, zaten sahip olduklarınızı çoğaltır.
  • Tüm güncellemeler zaten var olan alanlarla aynıdır, sadece farklı değerler olabilir.
  • Bir kaydın en son ne zaman değiştirildiğini, bir değerin gerçekte nerede değiştiğini izlemek istersiniz.

Not, PyMongo'yu seçiyorum, tercih ettiğiniz dile göre değiştirin.

Talimatlar:

  1. Yinelenen kayıtları almamak için unique = true dizinine sahip koleksiyon oluşturun.

  2. Girdi kayıtlarınızı yineleyin, 15.000 kayıttan oluşan gruplar oluşturun. Toplu işteki her kayıt için, her birinin yeni bir kayıt olacağı varsayılarak, eklemek istediğiniz verilerden oluşan bir diksiyon oluşturun. Bunlara 'oluşturulmuş' ve 'güncellenmiş' zaman damgalarını ekleyin. Bunu 'ContinueOnError' flag = true ile bir toplu insert komutu olarak verin, böylece orada yinelenen bir anahtar olsa bile (her şey var gibi görünecektir) her şeyin eklenmesi gerçekleşir. BU ÇOK HIZLI OLACAK. Toplu kaya ekler, 15k / saniye performans seviyelerine ulaştım. ContinueOnError ile ilgili diğer notlar, bkz. Http://docs.mongodb.org/manual/core/write-operations/

    Kayıt ekleri ÇOK hızlı olur, bu nedenle bu eklerle hemen ilgilenirsiniz. Şimdi, ilgili kayıtları güncelleme zamanı. Bunu bir kerede birden daha hızlı bir toplu alım ile yapın.

  3. 15K veya daha fazla toplu iş oluşturarak tüm giriş kayıtlarınızı tekrarlayın. Anahtarları çıkarın (bir anahtar varsa en iyisi, ancak yoksa yardım edilemez). Bu kayıtlar grubunu bir db.collectionNameBlah.find ({field: {$ in: [1, 2,3 ...}) sorgusuyla alın. Bu kayıtların her biri için bir güncelleme olup olmadığını belirleyin ve eğer varsa, 'güncellenmiş' zaman damgasının güncellenmesi dahil olmak üzere güncellemeyi yayınlayın.

    Ne yazık ki, MongoDB 2.4 ve aşağısında toplu güncelleme işlemi İÇERMEZ. Bunun üzerinde çalışıyorlar.

Temel Optimizasyon Noktaları:

  • Kesici uçlar işlemlerinizi büyük oranda hızlandıracaktır.
  • Kayıtları toplu olarak almak da işleri hızlandıracaktır.
  • Bireysel güncellemeler şu anda tek olası rota, ancak 10Gen üzerinde çalışıyor. Muhtemelen, bu 2.6'da olacak, ancak o zamana kadar bitip bitmeyeceğinden emin değilim, yapacak çok şey var (Jira sistemlerini takip ediyorum).

5

Mongodb'un bu tür seçici desteklemeyi desteklediğini sanmıyorum. LeMiz ile aynı sorun var ve hem 'oluşturulan' hem de 'güncellenmiş' zaman damgası ile uğraşırken güncelleme (ölçütler, newObj, upsert, multi) kullanarak doğru çalışmıyor. Aşağıdaki upert bildirimi göz önüne alındığında:

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

Senaryo # 1 - 'abc' için 'ad' olan belge mevcut değil: Yeni ad 'ad' = 'abc', 'oluşturuldu' = 2010-07-14 11:11:11 ve 'güncellendi' = ile oluşturuldu 2010-07-14 11:11:11.

Senaryo # 2 - 'abc' nin 'adı' olan belge zaten aşağıdakilerle var: 'name' = 'abc', 'oluşturulan' = 2010-07-12 09:09:09 ve 'güncellendi' = 2010-07 -13 10:10:10. Upert'ten sonra, belge şimdi senaryo # 1'deki sonuçla aynı olacaktır. Bir ekte, ekleme yapılıyorsa hangi alanların ayarlanacağını ve güncelleme yapılıyorsa hangi alanların yalnız bırakılacağını belirtmenin bir yolu yoktur.

Benim çözümüm, ölçüt alanlarında benzersiz bir dizin oluşturmak , bir ekleme yapmak ve hemen ardından "güncellenen" alanda bir güncelleme yapmaktı.


4

Genel olarak, henüz mevcut değilse belgeyi oluşturacağından MongoDB'de güncelleme kullanmak daha iyidir, ancak python adaptörünüzle nasıl çalışacağından emin değilim.

İkinci olarak, yalnızca bu belgenin var olup olmadığını bilmeniz gerekiyorsa, yalnızca bir sayı döndüren count () işlevi, tüm belgeyi MongoDB'nizden gereksiz trafiğe neden olan find_one'dan daha iyi bir seçenek olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.