Il modo più efficiente per farlo è nella prossima versione di MongoDB al momento della stesura di questo articolo utilizzando $split
operatore per dividere la nostra stringa come mostrato qui
quindi assegna l'ultimo elemento dell'array a una variabile utilizzando $let
operatore variabile e $arrayElemAt
operatori.
Successivamente, utilizziamo il $switch
operatore per eseguire un'elaborazione di una condizione logica o un'istruzione case rispetto a quella variabile.
La condizione qui è $gt
che restituisce true se il valore contiene "test"
, e in tal caso in in espressione dividiamo quella stringa e restituiamo semplicemente $concat
valore enato del primo elemento nell'array appena calcolato e il -
. Se la condizione restituisce false, restituiamo semplicemente la variabile.
Ovviamente nella nostra istruzione case, utilizziamo il $indexOfCP
che restituisce -1
se non ci sono occorrenze di "test"
.
let cursor = db.collection.aggregate(
[
{ "$project": {
"data": 1,
"version": {
"$let": {
"vars": {
"v": {
"$arrayElemAt": [
{ "$split": [ "$version", "." ] },
-1
]
}
},
"in": {
"$switch": {
"branches": [
{
"case": {
"$gt": [
{ "$indexOfCP": [ "$$v", "test" ] },
-1
]
},
"then": {
"$concat": [
"-",
"",
{ "$arrayElemAt": [
{ "$split": [ "$$v", "-" ] },
0
]}
]
}
}
],
"default": "$$v"
}
}
}
}
}}
]
)
La query di aggregazione produce qualcosa del genere:
{ "_id" : ObjectId("57a98773cbbd42a2156260d8"), "data" : 11, "version" : "32" }
{ "_id" : ObjectId("57a98773cbbd42a2156260d9"), "data" : 55, "version" : "-42" }
Come puoi vedere, i dati del campo "versione" sono stringa. Se il tipo di dati per quel campo non ha importanza, puoi semplicemente usare $out
operatore della fase della pipeline di aggregazione per scrivere il risultato in una nuova raccolta o sostituire la raccolta.
{ "out": "collection" }
Se hai bisogno di convertire i tuoi dati in un numero in virgola mobile, l'unico modo per farlo, semplicemente perché MongoDB non fornisce un modo per eseguire la conversione del tipo fuori dagli schemi tranne che da intero a stringa, è iterare il cursore di aggregazione oggetto e converti il tuo valore utilizzando parseFloat
o Number
quindi aggiorna i tuoi documenti utilizzando $set
operatore e bulkWrite()
metodo per la massima efficienza.
let requests = [];
cursor.forEach(doc => {
requests.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": {
"data": doc.data,
"version": parseFloat(doc.version)
},
"$unset": { "person": " " }
}
}
});
if ( requests.length === 1000 ) {
// Execute per 1000 ops and re-init
db.collection.bulkWrite(requests);
requests = [];
}}
);
// Clean up queues
if(requests.length > 0) {
db.coll.bulkWrite(requests);
}
Sebbene la query di aggregazione funzioni perfettamente in MongoDB 3.4 o versioni successive, la soluzione migliore da MongoDB 3.2 al contrario è mapReduce
con bulkWrite()
metodo.
var results = db.collection.mapReduce(
function() {
var v = this.version.split(".")[2];
emit(this._id, v.indexOf("-") > -1 ? "-"+v.replace(/\D+/g, '') : v)
},
function(key, value) {},
{ "out": { "inline": 1 } }
)["results"];
results
assomiglia a questo:
[
{
"_id" : ObjectId("57a98773cbbd42a2156260d8"),
"value" : "32"
},
{
"_id" : ObjectId("57a98773cbbd42a2156260d9"),
"value" : "-42"
}
]
Da qui usi il precedente .forEach
loop per aggiornare i tuoi documenti.
Da MongoDB 2.6 a 3.0 sarà necessario utilizzare il deprecato Bulk()
API e metodo associato come mostrato nella mia risposta qui.