Så arrayFilters
mulighed med positionsfiltreret $[<identifier>]
fungerer faktisk korrekt med udviklingsudgivelsesserien siden MongoDB 3.5.12 og også i de nuværende udgivelseskandidater for MongoDB 3.6-serien, hvor denne faktisk vil blive frigivet officielt. Det eneste problem er selvfølgelig, at de "drivere", der er i brug, faktisk ikke har indhentet dette endnu.
Gentagelse af det samme indhold, som jeg allerede har placeret på Opdatering af et indlejret array med MongoDB:
BEMÆRK Lidt ironisk, da dette er angivet i "options"-argumentet for .update()
og ligesom metoder er syntaksen generelt kompatibel med alle nyere udgivelsesdriverversioner.
Dette er dog ikke sandt for mongo
shell, da måden metoden er implementeret der ("ironisk nok for bagudkompatibilitet") arrayFilters
argument genkendes og fjernes ikke af en intern metode, der analyserer mulighederne for at levere "bagudkompatibilitet" med tidligere MongoDB-serverversioner og en "legacy" .update()
API-opkaldssyntaks.
Så hvis du vil bruge kommandoen i mongo
shell eller andre "skalbaserede" produkter (især Robo 3T), skal du bruge en seneste version fra enten udviklingsgrenen eller produktionsudgivelsen fra 3.6 eller nyere.
Alt dette betyder, at den aktuelle "driver"-implementering af .update()
"fjerner" faktisk de nødvendige argumenter med definitionen af arrayFilters
. For NodeJS vil dette blive behandlet i driverens 3.x-udgivelsesserie, og selvfølgelig vil "mongoose" sandsynligvis tage noget tid efter den udgivelse at implementere sine egne afhængigheder af den opdaterede driver, som så ikke længere ville "strippe" sådanne handlinger.
Du kan dog stadig køre dette på en understøttet serverforekomst, ved at gå tilbage til den grundlæggende "update command" syntaksbrug, da dette omgik den implementerede drivermetode:
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();
}
})()
Da det sender "kommandoen" direkte til serveren, ser vi, at den forventede opdatering faktisk finder sted:
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
}
]
}
]
}
Så lige "nu" de tilgængelige drivere "fra hylden" implementerer faktisk ikke .update()
eller det er andre implementerende modparter på en måde, der er kompatibel med faktisk at passere gennem de nødvendige arrayFilters
argument. Så hvis du "leger med" en udviklingsserie eller udgivelseskandidatserver, så bør du virkelig også være forberedt på at arbejde med "bleeding edge" og ikke-udgivne drivere.
Men du kan faktisk gøre dette som vist i enhver driver, i den korrekte form, hvor kommandoen, der udstedes, ikke vil blive ændret.
I skrivende stund den 11. november 2017 er der ingen "officiel" udgivelse af MongoDB eller de understøttede drivere, der rent faktisk implementerer dette. Produktionsbrug bør kun være baseret på officielle udgivelser af serveren og understøttede drivere.