Bu aslında, http://jira.mongodb.org/browse/SERVER-1243 adresindeki uzun süredir devam eden sorunla ilgilidir. Burada, çoklu dizi eşleşmelerinin olduğu "tüm durumlar" ı destekleyen açık bir sözdiziminde bir takım zorluklar vardır. bulundu. Aslında, bu sorunun orijinal çözümlerinden sonra uygulanan Toplu İşlemler gibi, bu soruna yönelik çözümlere "yardımcı" olan yöntemler mevcuttur .
Tek bir güncelleme deyiminde tek bir eşleşen dizi öğesinden daha fazlasını güncellemek hala mümkün değildir, bu nedenle "çoklu" bir güncellemeyle bile güncelleyebileceğiniz tek şey, bu tek belgedeki her belge için dizideki yalnızca tek bir matematik öğesidir Beyan.
Şu anda mümkün olan en iyi çözüm, eşleşen tüm belgeleri bulmak ve döngüye sokmak ve en azından birçok işlemin tek bir yanıtla tek bir istekle gönderilmesine izin verecek Toplu güncellemeleri işlemektir. İsteğe bağlı .aggregate()
olarak, arama sonucunda döndürülen dizi içeriğini yalnızca güncelleme seçimiyle eşleşen koşullara azaltmak için kullanabilirsiniz :
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$setDifference": [
{ "$map": {
"input": "$events",
"as": "event",
"in": {
"$cond": [
{ "$eq": [ "$$event.handled", 1 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
]).forEach(function(doc) {
doc.events.forEach(function(event) {
bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
"$set": { "events.$.handled": 0 }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
}
});
});
if ( count % 1000 != 0 )
bulk.execute();
.aggregate()
Dizi ya da her bir elemanı için tüm içerik için bir "eşsiz" tanımlayıcı "benzersiz" bir elemanının kendisi meydana olduğunda kısmı vardır çalışacaktır. Bunun nedeni , diziyi eşleşmeler için işlemek için kullanılan işlemden döndürülen $setDifference
tüm false
değerleri filtrelemek amacıyla kullanılan "set" operatörünün olmasıdır $map
.
Dizi içeriğinizin benzersiz öğeleri yoksa aşağıdakilerle alternatif bir yaklaşım deneyebilirsiniz $redact
:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
Sınırlaması burada "ele" gerçekten başka bir belge düzeyinde bir alan olması gerekiyorsa, o zaman muhtemelen beklenmedik sonuçlar elde edeceksiniz, ancak bu alanın sadece tek bir belge konumunda göründüğü ve eşitlik eşleşmesi olduğu durumlarda sorun yoktur.
Yazılı olarak gelecekteki sürümler (Post MongoDB sonrası) $filter
daha basit bir işlem yapacak :
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$filter": {
"input": "$events",
"as": "event",
"cond": { "$eq": [ "$$event.handled", 1 ] }
}
}
}}
])
Ve destek olan tüm sürümler .aggregate()
aşağıdaki yaklaşımı kullanabilir $unwind
, ancak bu operatörün kullanımı, boru hattındaki dizi genişlemesi nedeniyle onu en az verimli yaklaşım yapar:
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"events": { "$push": "$events" }
}}
])
MongoDB sürümünün toplu çıktıdan bir "imleci" desteklediği tüm durumlarda, bu sadece bir yaklaşım seçme ve sonuçları Toplu güncelleme deyimlerini işlemek için gösterilen aynı kod bloğuyla yineleme meselesidir. Toplu Çıktıdan Toplu İşlemler ve "imleçler" aynı sürümde tanıtılır (MongoDB 2.6) ve bu nedenle genellikle işleme için el ele çalışırlar.
Daha eski sürümlerde bile .find()
, imleci döndürmek ve ifadelerin yürütülmesini dizi öğesinin .update()
yineleme için kaç kez eşleştiğini filtrelemek için kullanmak en iyisidir :
db.collection.find({ "events.handled": 1 }).forEach(function(doc){
doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
});
});
Kesinlikle "çoklu" güncellemeler yapmaya kararlıysanız veya eşleşen her belge için birden fazla güncellemeyi işlemekten daha verimli olduğunu düşünüyorsanız, her zaman olası dizi eşleşmelerinin maksimum sayısını belirleyebilir ve yalnızca bu "çoklu" güncellemeyi yürütebilirsiniz. güncellenecek başka belge kalmayıncaya kadar.
MongoDB 2.4 ve 2.2 sürümleri için geçerli bir yaklaşım da .aggregate()
bu değeri bulmak için kullanılabilir :
var result = db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"count": { "$max": "$count" }
}}
]);
var max = result.result[0].count;
while ( max-- ) {
db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}
Durum ne olursa olsun, sen do bazı şeyler vardır değil güncelleme dahilinde yapmak istiyorum:
Diziyi "tek adımda" güncellemeyin: Burada, koddaki tüm dizi içeriğini ve ardından $set
her belgedeki tüm diziyi güncellemenin daha verimli olabileceğini düşünüyorsanız . Bu işlemek daha hızlı görünebilir, ancak dizi içeriğinin okunduğundan ve güncelleme yapıldığından beri değişmediğine dair bir garanti yoktur. $set
Hala bir atom operatörü olmasına rağmen , diziyi sadece "doğru" veri olduğunu düşündüğü şeyle güncelleyecektir ve bu nedenle okuma ve yazma arasında meydana gelen değişikliklerin üzerine yazması muhtemeldir.
Güncellenecek endeks değerlerini hesaplamayın: "Tek çekim" yaklaşımına benzer şekilde, sadece bu pozisyonu 0
ve pozisyonu 2
(vb.) Güncelleyecek ve kodlayacak öğelerdir ve aşağıdaki gibi bir deyim vardır:
{ "$set": {
"events.0.handled": 0,
"events.2.handled": 0
}}
Yine buradaki sorun, belge okunduğunda bulunan bu indeks değerlerinin güncelleme sırasındaki dizideki aynı indeks değerleri olduğu "varsayımıdır". Diziye siparişi değiştirecek şekilde yeni öğeler eklenirse, bu konumlar artık geçerli olmaz ve yanlış öğeler aslında güncellenir.
Bu nedenle, birden çok eşleşen dizi öğesinin tek güncelleme deyiminde işlenmesine izin vermek için belirlenen makul bir sözdizimi olana kadar, temel yaklaşım, eşleşen her dizi öğesini tek tek bir deyimde (ideal olarak Toplu olarak) güncellemek veya esasen maksimum dizi öğelerini çalışmaktır. değiştirilen sonuç döndürülmeyene kadar güncelleme veya güncellemeye devam etmek için Her durumda, her ifade için yalnızca bir öğe güncelleniyor olsa bile, eşleşen dizi öğesindeki konum$
güncellemelerini "her zaman" işlemelisiniz.
Toplu İşlemler aslında "çoklu işlem" olarak çalışan herhangi bir işlemi işlemek için "genelleştirilmiş" çözümdür ve bunun için sadece aynı değerde birden fazla dizi elemanını güncellemekten daha fazla uygulama olduğundan, elbette uygulanmıştır. ve bu sorunu çözmek için şu anda en iyi yaklaşım.