sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man lagrar en beställd uppsättning dokument i MongoDB utan att använda en begränsad samling

Baserat på dina krav kan ett av tillvägagångssätten vara att utforma ditt schema på ett sådant sätt att varje dokument har kapaciteten att hålla mer än ett dokument och i sig själv fungera som en försedd behållare .

{
  "_id":Number,
  "doc":Array
}

Varje dokument i samlingen fungerar som en försedd behållare , och dokumenten kommer att lagras som array i doc fält. doc fältet är en array, kommer att bibehålla insättningsordningen. Du kan begränsa antalet dokument till n . Så _id fältet för varje containerdokument kommer att vara inkrementellt med n , som anger antalet dokument som ett containerdokument kan innehålla.

Genom att göra dessa undviker lägga till extra fields till dokumentet, extra indices , unnecessary sorts .

Infoga den allra första posten

dvs när samlingen är tom.

var record = {"name" : "first"};
db.col.insert({"_id":0,"doc":[record]});

Infoga efterföljande poster

  • Identifiera det sista behållardokumentets _id och number av dokument som den innehåller.
  • Om antalet dokument som den innehåller är mindre än n , sedan uppdatera behållardokumentet med det nya dokumentet, annars skapa ett nytt containerdokument.

Säg att varje container document kan hålla 5 dokument som mest, och vi vill infoga ett nytt dokument.

var record = {"name" : "newlyAdded"};

// using aggregation, get the _id of the last inserted container, and the 
// number of record it currently holds.
db.col.aggregate( [ {
    $group : {
        "_id" : null,
        "max" : {
            $max : "$_id"
        },
        "lastDocSize" : {
            $last : "$doc"
        }
    }
}, {
    $project : {
        "currentMaxId" : "$max",
        "capSize" : {
            $size : "$lastDocSize"
        },
        "_id" : 0
    }
// once obtained, check if you need to update the last container or 
// create a new container and insert the document in it.
} ]).forEach( function(check) {
    if (check.capSize < 5) {
        print("updating");
        // UPDATE
        db.col.update( {
            "_id" : check.currentMaxId
        }, {
            $push : {
                "doc" : record
            }
        });
    } else {
        print("inserting");
        //insert
        db.col.insert( {
            "_id" : check.currentMaxId + 5,
            "doc" : [ record ]
        });
    }
})

Observera att aggregation , körs på serversidan och är mycket effektiv, notera också att aggregation skulle returnera dig ett dokument snarare än en markör i versioner previous to 2.6 . Så du skulle behöva modifiera ovanstående kod för att bara välja från ett enda dokument istället för att iterera en markör.

Infoga ett nytt dokument mellan dokument

Om du nu vill infoga ett nytt dokument mellan dokument 1 och 2 , vi vet att dokumentet ska hamna i behållaren med _id=0 och bör placeras i second position i doc array av den behållaren.

så vi använder oss av $each och $position operatörer för att infoga i specifika positioner.

var record = {"name" : "insertInMiddle"};

db.col.update(
{
    "_id" : 0
}, {
    $push : {
        "doc" : {
            $each : [record],
            $position : 1
        }
    }
}
);

Hantera överflöde

Nu måste vi ta hand om dokument som overflowing i varje container , säg att vi infogar ett nytt dokument emellan, i behållare med _id=0 . Om behållaren redan har 5 dokument måste vi move the last document to the next container och gör så tills alla containrar innehåller dokument inom sin kapacitet, om så krävs måste vi äntligen skapa en container för att hålla de överfulla dokumenten.

Denna komplexa operation bör göras på serversidan . För att hantera detta kan vi skapa ett skript som det nedan och register det med mongodb.

db.system.js.save( {
    "_id" : "handleOverFlow",
    "value" : function handleOverFlow(id) {
        var currDocArr = db.col.find( {
            "_id" : id
        })[0].doc;
        print(currDocArr);
        var count = currDocArr.length;
        var nextColId = id + 5;
        // check if the collection size has exceeded
    if (count <= 5)
        return;
    else {
        // need to take the last doc and push it to the next capped 
    // container's array
    print("updating collection: " + id);
    var record = currDocArr.splice(currDocArr.length - 1, 1);
    // update the next collection
    db.col.update( {
        "_id" : nextColId
    }, {
        $push : {
            "doc" : {
                $each : record,
                $position : 0
            }
        }
    });
    // remove from original collection
    db.col.update( {
        "_id" : id
    }, {
        "doc" : currDocArr
    });
    // check overflow for the subsequent containers, recursively.
    handleOverFlow(nextColId);
}
}

Så att after every insertion in between , kan vi anropa denna function genom att skicka container-id, handleOverFlow(containerId) .

Hämtar alla poster i ordning

Använd bara $unwind operatör i aggregate pipeline .

db.col.aggregate([{$unwind:"$doc"},{$project:{"_id":0,"doc":1}}]);

Beställa om dokument

Du kan lagra varje dokument i en kapslad behållare med ett "_id"-fält:

.."doc":[{"_id":0,","name":"xyz",...}..]..

Få tag i "doc"-arrayen i den förseglade behållaren som du vill beställa om artiklar.

var docArray = db.col.find({"_id":0})[0];

Uppdatera deras ID så att ordningen på objektet ändras efter sortering.

Sortera arrayen baserat på deras _id.

docArray.sort( function(a, b) {
    return a._id - b._id;
});

uppdatera tillbaka den täckta behållaren med den nya dokumentmatrisen.

Men återigen, allt handlar om vilket tillvägagångssätt som är genomförbart och som passar dina krav bäst.

Kommer till dina frågor:

Dokument som matriser.

använd $each och $position operatorer i db.collection.update() fungerar som avbildat i mitt svar.

Ja. Det skulle påverka prestandan, om inte samlingen har mycket mindre data.

Ja. Med Capped Collections kan du förlora data.



  1. Mongoexport-fel vid analys av fråga

  2. MongoDB Number Field kommer inte att infogas eller uppdateras med numret som jag matat in

  3. Hur skapar man en egen databas i Redis?

  4. Mongodb-fråga med fält i samma dokument