sql >> Database >  >> NoSQL >> MongoDB

Een geordende set documenten opslaan in MongoDB zonder een gelimiteerde verzameling te gebruiken?

Op basis van uw vereisten zou een van de benaderingen kunnen zijn om uw schema zo te ontwerpen dat elk document de mogelijkheid heeft om meer dan één document te bevatten en op zichzelf te fungeren als een afgedekte container .

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

Elk document in de verzameling fungeert als een afgedekte container , en de documenten worden opgeslagen als een array in de doc veld. De doc veld een array is, behoudt de volgorde van invoeging. U kunt het aantal documenten beperken tot n . Dus de _id veld van elk containerdocument zal toenemen met n , waarmee wordt aangegeven hoeveel documenten een containerdocument kan bevatten.

Door dit te doen vermijdt u toevoegen van extra fields naar het document, extra indices , unnecessary sorts .

Het allereerste record invoegen

d.w.z. wanneer de verzameling leeg is.

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

Volgende records invoegen

  • Identificeer de _id van het laatste containerdocument , en het number documenten die het bevat.
  • Als het aantal documenten dat het bevat kleiner is dan n , dan bijwerken thecontainer document met het nieuwe document, anders maak een nieuw containerdocument.

Stel dat elk container document kan 5 bevatten hoogstens documenten en we willen een nieuw document invoegen.

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

Merk op dat de aggregation , draait op de server en is zeer efficiënt, merk ook op dat de aggregation zou u een document terugsturen in plaats van een cursor in versies previous to 2.6 . U zou dus de bovenstaande code moeten wijzigen om slechts uit een enkel document te selecteren in plaats van een cursor te herhalen.

Een nieuw document tussen documenten invoegen

Als u nu een nieuw document tussen documenten wilt invoegen 1 en 2 , weten we dat het document in de container met _id=0 . moet vallen en moet in de second . worden geplaatst positie in het doc array van die container.

dus maken we gebruik van de $each en $position operators voor het invoegen in specifieke posities.

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

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

Overstroming afhandelen

Nu moeten we zorgen voor documenten die overflowing in elke container , stel dat we er een nieuw document tussen invoegen, in container met _id=0 . Als de container al 5 . heeft documenten, moeten we move the last document to the next container en doe dit totdat alle containers de documenten binnen hun capaciteit bevatten, indien nodig moeten we eindelijk een container maken om de overvolle documenten te bewaren.

Deze complexe operatie moet worden gedaan aan de serverkant . Om dit aan te pakken, kunnen we een script maken zoals hieronder en register het met 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);
}
}

Zodat after every insertion in between , kunnen we deze function aanroepen door de container-id door te geven, handleOverFlow(containerId) .

Alle records in volgorde ophalen

Gebruik gewoon de $unwind operator in de aggregate pipeline .

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

Documenten opnieuw bestellen

U kunt elk document opslaan in een afgesloten container met een "_id"-veld:

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

Pak de "doc"-array van de afgedekte container waarvan u items opnieuw wilt ordenen.

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

Werk hun ID bij zodat na het sorteren de volgorde van het item zal veranderen.

Sorteer de array op basis van hun _id's.

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

update de afgedekte container terug, met de nieuwe doc-array.

Maar nogmaals, alles komt neer op welke aanpak haalbaar is en het beste aansluit bij uw behoefte.

Komend op uw vragen:

Documenten als arrays.

gebruik de $each en $position operators in de db.collection.update() functioneren zoals afgebeeld in mijn antwoord.

Ja. Het zou de prestaties beïnvloeden, tenzij de verzameling zeer weinig gegevens bevat.

Ja. Met Capped Collections kunt u gegevens verliezen.



  1. Mongo - Negeer eigendom van persistentie

  2. MongoDB+Azure+Android:com.mongodb.WriteConcernException err:geen hoofdcode:10058

  3. Express node.js-controller wacht niet op gegevensmanipulatie en retourneert oude gegevens

  4. Waar is AccountKey voor CosmosDB in Azure Portal