Така че arrayFilters опция с филтриран по позиция $[<identifier>] всъщност работи правилно със серията издания за разработка след MongoDB 3.5.12, а също и в текущите кандидати за версия за серията MongoDB 3.6, където това всъщност ще бъде официално пуснато. Единственият проблем, разбира се, е, че използваните "драйвери" всъщност все още не са настигнали това.
Повторение на същото съдържание, което вече поставих в Актуализиране на вложен масив с MongoDB:
ЗАБЕЛЕЖКА Донякъде иронично, тъй като това е посочено в аргумента "опции" за
.update()и подобно на методите, синтаксисът обикновено е съвместим с всички последни версии на драйвери.Това обаче не е вярно за
mongoshell, тъй като начина, по който методът е внедрен там ( "по ирония на съдбата за обратна съвместимост" ),arrayFiltersаргументът не се разпознава и премахва от вътрешен метод, който анализира опциите, за да осигури "обратна съвместимост" с предишни версии на сървъра на MongoDB и "наследен".update()Синтаксис на извикване на API.Така че, ако искате да използвате командата в
mongoshell или други „базирани на обвивка“ продукти (по-специално Robo 3T) имате нужда от най-новата версия от клона за разработка или от производствената версия от 3.6 или по-нова версия.
Всичко това означава, че текущата реализация на "драйвер" на .update() всъщност "премахва" необходимите аргументи с дефиницията на arrayFilters . За NodeJS това ще бъде разгледано в серията версии 3.x на драйвера и, разбира се, "mongoose" вероятно ще отнеме известно време след това издание, за да внедри собствените си зависимости от актуализирания драйвер, който след това вече няма да "отстранява" такива действия.
Все пак можете да стартирате това на поддържан сървърен екземпляр, като се върнете обратно към основното използване на синтаксиса на "команда за актуализиране", тъй като това заобикаля внедрения метод на драйвер:
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.Types.ObjectId;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const contactSchema = new Schema({
data: String,
type: String,
priority: String,
retries: String
});
const personSchema = new Schema({
name: String,
level: String,
priority: String,
enabled: Boolean,
contacts: [contactSchema]
});
const groupSchema = new Schema({
name: String,
people: [personSchema],
workingHours: { start: String, end: String },
workingDays: { type: [Number], default: undefined },
contactTypes: {
workingHours: { type: [String], default: undefined },
contactTypes: { type: [String], default: undefined }
}
});
const Group = mongoose.model('Group', groupSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Clean data
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.remove() )
);
// Create sample
await Group.create({
name: "support",
people: [
{
"_id": ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
"enabled": true,
"level": "1",
"name": "Someone",
"contacts": [
{
"type": "email",
"data": "example@sqldat.com"
},
{
"_id": ObjectId("5a05a8dee0ce3444f8ec5bda"),
"retries": "1",
"priority": "1",
"type": "email",
"data": "example@sqldat.com"
}
]
}
]
});
let result = await conn.db.command({
"update": Group.collection.name,
"updates": [
{
"q": {},
"u": { "$set": { "people.$[i].contacts.$[j].data": "new data" } },
"multi": true,
"arrayFilters": [
{ "i._id": ObjectId("5a05a8c3e0ce3444f8ec5bd8") },
{ "j._id": ObjectId("5a05a8dee0ce3444f8ec5bda") }
]
}
]
});
log(result);
let group = await Group.findOne();
log(group);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
Тъй като това изпраща "командата" директно до сървъра, виждаме, че очакваната актуализация всъщност се осъществява:
Mongoose: groups.remove({}, {})
Mongoose: groups.insert({ name: 'support', _id: ObjectId("5a06557fb568aa0ad793c5e4"), people: [ { _id: ObjectId("5a05a8c3e0ce3444f8ec5bd8"), enabled: true, level: '1', name: 'Someone', contacts: [ { type: 'email', data: 'example@sqldat.com', _id: ObjectId("5a06557fb568aa0ad793c5e5") }, { _id: ObjectId("5a05a8dee0ce3444f8ec5bda"), retries: '1', priority: '1', type: 'email', data: 'example@sqldat.com' } ] } ], __v: 0 })
{ n: 1,
nModified: 1,
opTime:
{ ts: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
t: 24 },
electionId: 7fffffff0000000000000018,
ok: 1,
operationTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
'$clusterTime':
{ clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
signature: { hash: [Object], keyId: 0 } } }
Mongoose: groups.findOne({}, { fields: {} })
{
"_id": "5a06557fb568aa0ad793c5e4",
"name": "support",
"__v": 0,
"people": [
{
"_id": "5a05a8c3e0ce3444f8ec5bd8",
"enabled": true,
"level": "1",
"name": "Someone",
"contacts": [
{
"type": "email",
"data": "example@sqldat.com",
"_id": "5a06557fb568aa0ad793c5e5"
},
{
"_id": "5a05a8dee0ce3444f8ec5bda",
"retries": "1",
"priority": "1",
"type": "email",
"data": "new data" // <-- updated here
}
]
}
]
}
Така че точно „сега“ драйверите, които са налични "извън рафта", всъщност не имплементират .update() или е други внедряващи аналози по начин, който е съвместим с действителното преминаване през необходимите arrayFilters аргумент. Така че, ако "играете с" серия за разработка или пускате кандидат-сървър, тогава наистина трябва да сте готови да работите и с "кървавия край" и с неиздадени драйвери.
Но всъщност можете да направите това, както е показано във всеки драйвер, в правилната форма, при която командата, която се издава, няма да бъде променена.
Към момента на писане на 11 ноември 2017 г. няма "официален" пускане на MongoDB или поддържаните драйвери, които действително реализират това. Производственото използване трябва да се основава само на официалните версии на сървъра и поддържаните драйвери.