MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Jak przechowywać zamówiony zestaw dokumentów w MongoDB bez używania ograniczonej kolekcji?

W zależności od wymagań jednym z podejść może być zaprojektowanie schematu w taki sposób, aby każdy dokument posiadał odpowiednie możliwości do przechowywania więcej niż jednego dokumentu i sam w sobie działa jak zamknięty pojemnik .

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

Każdy dokument w kolekcji będzie działał jak zamknięty kontener , a dokumenty będą przechowywane jako tablica w doc pole. doc pole będące tablicą, zachowa kolejność wstawiania. Możesz ograniczyć liczbę dokumentów do n . Więc _id pole każdego dokumentu kontenera będzie zwiększane o n , wskazując liczbę dokumentów, które może przechowywać dokument kontenera.

Robiąc to, unikasz dodanie extra fields do dokumentu, extra indices , unnecessary sorts .

Wstawianie pierwszego rekordu

tj. gdy kolekcja jest pusta.

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

Wstawianie kolejnych rekordów

  • Zidentyfikuj _id ostatniego dokumentu kontenera i number posiadanych dokumentów.
  • Jeśli liczba przechowywanych dokumentów jest mniejsza niż n , a następnie zaktualizuj dokument kontenera z nowym dokumentem, w przeciwnym razie utwórz nowy dokument kontenera.

Powiedz, że każdy container document może pomieścić 5 co najwyżej dokumentów i chcemy wstawić nowy 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 ]
        });
    }
})

Zauważ, że aggregation , działa po stronie serwera i jest bardzo wydajny, należy również pamiętać, że aggregation zwróci Ci dokument zamiast kursora w wersjach previous to 2.6 . Więc musiałbyś zmodyfikować powyższy kod, aby po prostu wybierać z jednego dokumentu, zamiast iterować kursor.

Wstawianie nowego dokumentu między dokumentami

Teraz, jeśli chcesz wstawić nowy dokument między dokumentami 1 i 2 , wiemy, że dokument powinien znajdować się w kontenerze z _id=0 i powinien być umieszczony w second pozycja w doc tablica tego kontenera.

więc używamy $each i $position operatorów do wstawiania w określone pozycje.

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

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

Obsługa przepływu

Teraz musimy zająć się dokumentami overflowing w każdym container , powiedzmy, że wstawiamy nowy dokument pomiędzy, w kontenerze z _id=0 . Jeśli kontener ma już 5 dokumenty, musimy move the last document to the next container i rób to, dopóki wszystkie pojemniki nie pomieszczą dokumentów w swoim zakresie, w razie potrzeby w końcu musimy utworzyć pojemnik do przechowywania przepełnionych dokumentów.

Ta złożona operacja powinna być zrobione po po stronie serwera . Aby sobie z tym poradzić, możemy stworzyć skrypt taki jak ten poniżej i register to z 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 więc after every insertion in between , możemy wywołać tę function przekazując identyfikator kontenera, handleOverFlow(containerId) .

Pobieranie wszystkich rekordów w kolejności

Po prostu użyj $unwind operator w aggregate pipeline .

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

Ponowne zamawianie dokumentów

Każdy dokument możesz przechowywać w zamkniętym kontenerze z polem „_id”:

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

Zdobądź tablicę „doc” zamkniętego kontenera, którego elementy chcesz zmienić.

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

Zaktualizuj ich identyfikatory, aby po sortowaniu zmieniła się kolejność elementów.

Sortuj tablicę na podstawie ich identyfikatorów.

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

zaktualizuj zamknięty kontener z powrotem za pomocą nowej tablicy dokumentów.

Ale z drugiej strony wszystko sprowadza się do tego, które podejście jest wykonalne i najlepiej odpowiada Twoim wymaganiom.

Przechodząc do twoich pytań:

Dokumenty jako tablice.

użyj $each i $position operatory w db.collection.update() działają zgodnie z opisem w mojej odpowiedzi.

TAk. Miałoby to wpływ na wydajność, chyba że kolekcja zawiera bardzo mniej danych.

TAk. W przypadku ograniczonych kolekcji możesz utracić dane.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Jak naprawić {document}.Id nie jest obsługiwany błąd

  2. Agreguj i aktualizuj MongoDB

  3. Jaka jest zaleta używania ObjectId zamiast zwykłego String?

  4. Wartość domyślna nie jest ustawiona podczas używania aktualizacji z Upsert jako true

  5. Paginacja po stronie serwera z pojedynczym polem tablicy ciągów dokumentu