Така че arrayFilters
опция с филтриран по позиция $[<identifier>]
всъщност работи правилно със серията издания за разработка след MongoDB 3.5.12, а също и в текущите кандидати за версия за серията MongoDB 3.6, където това всъщност ще бъде официално пуснато. Единственият проблем, разбира се, е, че използваните "драйвери" всъщност все още не са настигнали това.
Повторение на същото съдържание, което вече поставих в Актуализиране на вложен масив с MongoDB:
ЗАБЕЛЕЖКА Донякъде иронично, тъй като това е посочено в аргумента "опции" за
.update()
и подобно на методите, синтаксисът обикновено е съвместим с всички последни версии на драйвери.Това обаче не е вярно за
mongo
shell, тъй като начина, по който методът е внедрен там ( "по ирония на съдбата за обратна съвместимост" ),arrayFilters
аргументът не се разпознава и премахва от вътрешен метод, който анализира опциите, за да осигури "обратна съвместимост" с предишни версии на сървъра на MongoDB и "наследен".update()
Синтаксис на извикване на API.Така че, ако искате да използвате командата в
mongo
shell или други „базирани на обвивка“ продукти (по-специално 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": "[email protected]"
},
{
"_id": ObjectId("5a05a8dee0ce3444f8ec5bda"),
"retries": "1",
"priority": "1",
"type": "email",
"data": "[email protected]"
}
]
}
]
});
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: '[email protected]', _id: ObjectId("5a06557fb568aa0ad793c5e5") }, { _id: ObjectId("5a05a8dee0ce3444f8ec5bda"), retries: '1', priority: '1', type: 'email', data: '[email protected]' } ] } ], __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": "[email protected]",
"_id": "5a06557fb568aa0ad793c5e5"
},
{
"_id": "5a05a8dee0ce3444f8ec5bda",
"retries": "1",
"priority": "1",
"type": "email",
"data": "new data" // <-- updated here
}
]
}
]
}
Така че точно „сега“ драйверите, които са налични "извън рафта", всъщност не имплементират .update()
или е други внедряващи аналози по начин, който е съвместим с действителното преминаване през необходимите arrayFilters
аргумент. Така че, ако "играете с" серия за разработка или пускате кандидат-сървър, тогава наистина трябва да сте готови да работите и с "кървавия край" и с неиздадени драйвери.
Но всъщност можете да направите това, както е показано във всеки драйвер, в правилната форма, при която командата, която се издава, няма да бъде променена.
Към момента на писане на 11 ноември 2017 г. няма "официален" пускане на MongoDB или поддържаните драйвери, които действително реализират това. Производственото използване трябва да се основава само на официалните версии на сървъра и поддържаните драйвери.