Quindi gli arrayFilters
opzione con filtro posizionale $[<identifier>]
funziona effettivamente correttamente con la serie di rilasci di sviluppo a partire da MongoDB 3.5.12 e anche nelle attuali versioni candidate per la serie MongoDB 3.6, dove verrà effettivamente rilasciata. L'unico problema, ovviamente, è che i "driver" in uso non hanno ancora raggiunto questo obiettivo.
Ripeti lo stesso contenuto che ho già inserito nell'aggiornamento di un array nidificato con MongoDB:
NOTA Un po' ironicamente, dal momento che questo è specificato nell'argomento "opzioni" per .update()
e come i metodi, la sintassi è generalmente compatibile con tutte le versioni dei driver di rilascio recenti.
Tuttavia questo non è vero per il mongo
shell, dal momento che il metodo è implementato lì ("ironicamente per compatibilità con le versioni precedenti") gli arrayFilters
argomento non viene riconosciuto e rimosso da un metodo interno che analizza le opzioni al fine di fornire "compatibilità con le versioni precedenti" con le versioni precedenti del server MongoDB e un "legacy" .update()
Sintassi delle chiamate API.
Quindi, se vuoi usare il comando nel mongo
shell o altri prodotti "shell based" (in particolare Robo 3T) è necessaria una versione più recente dal ramo di sviluppo o dalla versione di produzione a partire dalla 3.6 o successiva.
Tutto ciò significa che l'attuale implementazione del "driver" di .update()
in realtà "rimuove" gli argomenti necessari con la definizione di arrayFilters
. Per NodeJS questo verrà affrontato nella serie di versioni 3.x del driver e, ovviamente, "mangusta" impiegherà probabilmente del tempo dopo quella versione per implementare le proprie dipendenze dal driver aggiornato, che quindi non si "spoglierebbe" più tali azioni.
Tuttavia, puoi ancora eseguirlo su un supportato istanza del server, tornando all'utilizzo della sintassi di base del "comando di aggiornamento", poiché ciò ha ignorato il metodo del driver implementato:
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();
}
})()
Poiché ciò invia il "comando" direttamente al server, vediamo che l'aggiornamento previsto ha effettivamente luogo:
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
}
]
}
]
}
Quindi giusto "adesso" i driver disponibili "pronti all'uso" non implementano effettivamente .update()
oppure sono altre controparti di implementazione in un modo compatibile con il passaggio effettivo degli arrayFilters
necessari discussione. Quindi, se stai "giocando con" una serie di sviluppo o un server candidato al rilascio, allora dovresti essere davvero preparato a lavorare con il "bordo sanguinante" e anche i driver non rilasciati.
Ma puoi effettivamente farlo come dimostrato in qualsiasi driver, nella forma corretta in cui il comando emesso non verrà modificato.