sql >> Databáze >  >> NoSQL >> MongoDB

Jak uložit objednanou sadu dokumentů v MongoDB bez použití omezené kolekce

Na základě vašeho požadavku by jedním z přístupů mohlo být navrhnout schéma tak, aby každý dokument měl možnosti pojme více než jeden dokument a sám o sobě funguje jako uzavřený kontejner .

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

Každý dokument v kolekci bude fungovat jako uzavřený kontejner a dokumenty budou uloženy jako pole v doc pole. doc pole je pole, zachová pořadí vkládání. Počet dokumentů můžete omezit na n . Tedy _id pole každého kontejnerového dokumentu bude přírůstkové o n , uvádějící počet dokumentů, které kontejnerový dokument pojme.

Tím se vyhnete přidání extra fields do dokumentu extra indices , unnecessary sorts .

Vložení úplně prvního záznamu

tj. když je kolekce prázdná.

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

Vkládání následných záznamů

  • Určete _id posledního kontejnerového dokumentu a number dokumentů, které má.
  • Pokud je počet dokumentů, které obsahuje, menší než n a poté aktualizovat kontejnerový dokument s novým dokumentem, jinak vytvořte nový kontejnerový dokument.

Řekněme, že každý container document může obsahovat 5 dokumentů a my chceme vložit nový 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 ]
        });
    }
})

Všimněte si, že aggregation , běží na straně serveru a je velmi efektivní, všimněte si také aggregation vám vrátí dokument spíše než kurzor ve verzích previous to 2.6 . Takže byste museli upravit výše uvedený kód tak, abyste vybírali pouze z jednoho dokumentu a ne iterovali kurzor.

Vložení nového dokumentu mezi dokumenty

Nyní, pokud chcete vložit nový dokument mezi dokumenty 1 a 2 , víme, že dokument by měl spadat do kontejneru s _id=0 a měl by být umístěn v second pozici v doc pole tohoto kontejneru.

takže používáme $each a $position operátory pro vkládání do konkrétních pozic.

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

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

Manipulace s přetečením

Nyní se musíme postarat o to, aby dokumenty overflowing v každém container , řekněme, že mezi to vložíme nový dokument do kontejneru s _id=0 . Pokud kontejner již obsahuje 5 dokumenty, musíme move the last document to the next container a udělejte tak, dokud všechny kontejnery neobsahují dokumenty v rámci jejich kapacity, pokud to bude potřeba, musíme konečně vytvořit kontejner, do kterého se vejdou přetékající dokumenty.

Tato složitá operace by měla provést na straně serveru . Abychom to zvládli, můžeme vytvořit skript, jako je ten níže, a register to s 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);
}
}

Takže after every insertion in between , můžeme vyvolat tuto function předáním ID kontejneru, handleOverFlow(containerId) .

Načítání všech záznamů v pořadí

Stačí použít $unwind operátor v aggregate pipeline .

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

Znovu objednání dokumentů

Každý dokument můžete uložit do uzavřeného kontejneru s polem "_id":

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

Získejte pole „doc“ uzavřeného kontejneru, v němž chcete změnit pořadí položek.

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

Aktualizujte jejich ID, aby se po seřazení změnilo pořadí položky.

Seřaďte pole podle jejich _id.

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

aktualizujte uzavřený kontejner zpět pomocí nového pole dokumentů.

Ale zase se vše scvrkává na to, který přístup je proveditelný a nejlépe vyhovuje vašim požadavkům.

K vašim otázkám:

Dokumenty jako pole.

použijte $each a $position operátory v db.collection.update() funkce, jak je znázorněno v mé odpovědi.

Ano. Ovlivnilo by to výkon, ledaže by kolekce obsahovala méně dat.

Ano. S omezenými kolekcemi můžete přijít o data.



  1. res.redirect zobrazující staré informace po odeslání formuláře?

  2. jak přidám hodnotu do horní části pole v mongodb?

  3. Jak funguje unhideIndex() v MongoDB

  4. Mongodb, sharding a služby pro více oken