sql >> Database teknologi >  >> NoSQL >> MongoDB

mongodb samlet forespørgsel returnerer ikke den rigtige sum ved brug af $sum

Dit nuværende skema har marks feltdatatype som streng, og du skal bruge en heltalsdatatype til din aggregeringsramme for at beregne summen. På den anden side kan du bruge MapReduce at beregne summen, da det tillader brugen af ​​native JavaScript-metoder såsom parseInt() på dine objektegenskaber i dets kortfunktioner. Så overordnet har du to valg.

Mulighed 1:Opdater skema (Skift datatype)

Den første ville være at ændre skemaet eller tilføje et andet felt i dit dokument, der har den faktiske numeriske værdi, ikke strengrepræsentationen. Hvis dit samlingsdokument er relativt lille, kan du bruge en kombination af mongodbs markør find() , forEach() og update() metoder til at ændre dit mærkeskema:

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

For relativt store samlingsstørrelser vil din db-ydelse være langsom, og det anbefales at bruge mongo-masseopdateringer til dette:

MongoDB-versioner>=2.6 og <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 og nyere:

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);

Mulighed 2:Kør MapReduce

Den anden tilgang ville være at omskrive din forespørgsel med MapReduce hvor du kan bruge JavaScript-funktionen parseInt() .

I din MapReduce operation, definere kortfunktionen, der behandler hvert inputdokument. Denne funktion kortlægger de konverterede marks strengværdi til subject for hvert dokument og udsender subject og konverterede marks par. Det er her den oprindelige JavaScript-funktion parseInt() kan anvendes. Bemærk:i funktionen this refererer til det dokument, som kortreduceringsoperationen behandler:

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

Dernæst skal du definere den tilsvarende reduktionsfunktion med to argumenter keySubject og valuesMarks . valuesMarks er et array, hvis elementer er heltal marks værdier udsendt af kortfunktionen og grupperet efter keySubject . Funktionen reducerer valuesMarks array til summen af ​​dets elementer.

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

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

Med din samling vil ovenstående placere dit MapReduce-aggregeringsresultat i en ny samling db.example_results . Således db.example_results.find() vil udsende:

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


  1. Opret og find GeoLocation i mongoose

  2. Caching af gentagne forespørgsler i MongoDB

  3. Er det muligt at få felterne i projektionsrækkefølgen i Aggregation Frameworks mongo

  4. Hvordan laver man en forespørgsel i denne indlejrede dokumentstruktur (MongoDB)?