sql >> Databasteknik >  >> NoSQL >> MongoDB

mongodb aggregerad fråga returnerar inte korrekt summa vid användning av $summa

Ditt nuvarande schema har marks fältdatatyp som sträng och du behöver en heltalsdatatyp för ditt aggregeringsramverk för att räkna ut summan. Å andra sidan kan du använda MapReduce för att beräkna summan eftersom det tillåter användning av inbyggda JavaScript-metoder som parseInt() på dina objektegenskaper i dess kartfunktioner. Så totalt sett har du två val.

Alternativ 1:Uppdatera schema (Ändra datatyp)

Det första skulle vara att ändra schemat eller lägga till ett annat fält i ditt dokument som har det faktiska numeriska värdet inte strängrepresentationen. Om ditt samlingsdokument är relativt litet kan du använda en kombination av mongodbs markör find() , forEach() och update() metoder för att ändra ditt märkesschema:

db.student.find({ "marks": { "$type": 2 } }).snapshot().forEach(function(doc) {
    db.student.update(
        { "_id": doc._id, "marks": { "$type": 2 } }, 
        { "$set": { "marks": parseInt(doc.marks) } }
    );
});

För relativt stora samlingsstorlekar kommer din db-prestanda att vara långsam och det rekommenderas att använda mongo massuppdateringar för detta:

MongoDB versioner>=2.6 och <3.2:

var bulk = db.student.initializeUnorderedBulkOp(),
    counter = 0;

db.student.find({"marks": {"$exists": true, "$type": 2 }}).forEach(function (doc) {    
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "marks": parseInt(doc.marks) } 
    });

    counter++;
    if (counter % 1000 === 0) {
        // Execute per 1000 operations 
        bulk.execute(); 

        // re-initialize every 1000 update statements
        bulk = db.student.initializeUnorderedBulkOp();
    }
})

// Clean up remaining operations in queue
if (counter % 1000 !== 0) bulk.execute(); 

MongoDB version 3.2 och senare:

var ops = [],
    cursor = db.student.find({"marks": {"$exists": true, "$type": 2 }});

cursor.forEach(function (doc) {     
    ops.push({ 
        "updateOne": { 
            "filter": { "_id": doc._id } ,              
            "update": { "$set": { "marks": parseInt(doc.marks) } } 
        }         
    });

    if (ops.length === 1000) {
        db.student.bulkWrite(ops);
        ops = [];
    }     
});

if (ops.length > 0) db.student.bulkWrite(ops);

Alternativ 2:Kör MapReduce

Den andra metoden skulle vara att skriva om din fråga med MapReduce där du kan använda JavaScript-funktionen parseInt() .

I din MapReduce operation, definiera kartfunktionen som bearbetar varje inmatat dokument. Denna funktion mappar de konverterade marks strängvärde till subject för varje dokument och avger subject och konverterade marks par. Det är här den inbyggda JavaScript-funktionen parseInt() Kan appliceras. Obs:i funktionen, this hänvisar till dokumentet som kartminskningsoperationen bearbetar:

var mapper = function () {
    var x = parseInt(this.marks);
    emit(this.subject, x);
};

Därefter definierar du motsvarande reduceringsfunktion med två argument keySubject och valuesMarks . valuesMarks är en array vars element är heltal marks värden som avges av kartfunktionen och grupperade efter keySubject . Funktionen minskar valuesMarks array till summan av dess element.

var reducer = function(keySubject, valuesMarks) {
    return Array.sum(valuesMarks);
};

db.student.mapReduce(
    mapper,
    reducer,
    {
        out : "example_results",
        query: { subject : "maths" }       
    }
 );

Med din samling kommer ovanstående att placera ditt MapReduce-aggregationsresultat i en ny samling db.example_results . Alltså db.example_results.find() kommer att mata ut:

/* 0 */
{
    "_id" : "maths",
    "value" : 163
}


  1. Hur man konverterar BigDecimal till Double i spring-data-mongodb ramverk

  2. Flter mongodb databas med mongoose nodejs

  3. Redis och frågevärden

  4. .updateOne på MongoDB fungerar inte i Node.js