Czy podczas korzystania z MongoDB istnieją jakieś specjalne wzorce tworzenia np. Widoku stronicowanego? powiedz blog, który zawiera listę 10 najnowszych postów, na którym możesz przejść wstecz do starszych postów.
Czy można rozwiązać to za pomocą indeksu np. Blogpost.publishdate i po prostu pominąć i ograniczyć wynik?
Odpowiedzi:
Używanie skip + limit nie jest dobrym sposobem na stronicowanie, gdy problemem jest wydajność lub w przypadku dużych kolekcji; będzie działać coraz wolniej w miarę zwiększania liczby stron. Użycie pomijania wymaga, aby serwer przeszedł przez wszystkie dokumenty (lub wartości indeksu) od 0 do wartości przesunięcia (pominięcia).
O wiele lepiej jest użyć zapytania zakresowego (+ limit), w którym podaje się wartość zakresu ostatniej strony. Na przykład, jeśli sortujesz według „opublikowania”, możesz po prostu przekazać ostatnią wartość „opublikujdatę” jako kryterium zapytania, aby uzyskać następną stronę danych.
źródło
Możliwe rozwiązanie: spróbuj uprościć projekt, zastanawiając się, czy możemy sortować tylko według identyfikatora lub jakiejś unikalnej wartości?
Jeśli możemy, można zastosować stronicowanie oparte na zakresie.
Powszechnym sposobem jest użycie sort (), skip () i limit () w celu zaimplementowania stronicowania, co opisano powyżej.
źródło
{ _id: { $gt: ... } }
... po prostu nie działa przy niestandardowym zamówieniu - np.sort(...)
.To jest rozwiązanie, którego użyłem, gdy moja kolekcja stała się zbyt duża, aby można ją było zwrócić w pojedynczym zapytaniu. Wykorzystuje nieodłączną kolejność
_id
pola i umożliwia zapętlenie kolekcji według określonego rozmiaru partii.Tutaj jest to moduł npm, mongoose-paging , pełny kod jest poniżej:
function promiseWhile(condition, action) { return new Promise(function(resolve, reject) { process.nextTick(function loop() { if(!condition()) { resolve(); } else { action().then(loop).catch(reject); } }); }); } function findPaged(query, fields, options, iterator, cb) { var Model = this, step = options.step, cursor = null, length = null; promiseWhile(function() { return ( length===null || length > 0 ); }, function() { return new Promise(function(resolve, reject) { if(cursor) query['_id'] = { $gt: cursor }; Model.find(query, fields, options).sort({_id: 1}).limit(step).exec(function(err, items) { if(err) { reject(err); } else { length = items.length; if(length > 0) { cursor = items[length - 1]._id; iterator(items, function(err) { if(err) { reject(err); } else { resolve(); } }); } else { resolve(); } } }); }); }).then(cb).catch(cb); } module.exports = function(schema) { schema.statics.findPaged = findPaged; };
Dołącz go do swojego modelu w następujący sposób:
Następnie zapytaj w ten sposób:
MyModel.findPaged( // mongoose query object, leave blank for all {source: 'email'}, // fields to return, leave blank for all ['subject', 'message'], // number of results per page {step: 100}, // iterator to call on each set of results function(results, cb) { console.log(results); // this is called repeatedly while until there are no more results. // results is an array of maximum length 100 containing the // results of your query // if all goes well cb(); // if your async stuff has an error cb(err); }, // function to call when finished looping function(err) { throw err; // this is called once there are no more results (err is null), // or if there is an error (then err is set) } );
źródło
Stronicowanie na podstawie zakresu jest wykonalne, ale musisz być sprytny, jeśli chodzi o min / max zapytania.
Jeśli możesz sobie na to pozwolić, spróbuj buforować wyniki zapytania w tymczasowym pliku lub kolekcji. Dzięki kolekcjom TTL w MongoDB możesz wstawić swoje wyniki do dwóch kolekcji.
Używając obu zapewnień, nie uzyskasz częściowych wyników, gdy TTL będzie zbliżone do aktualnego czasu. Podczas przechowywania wyników możesz skorzystać z prostego licznika, aby w tym momencie wykonać BARDZO proste zapytanie o zakres.
źródło
Oto przykład pobierania listy
User
dokumentów w kolejnościCreatedDate
(gdziepageIndex
jest od zera) przy użyciu oficjalnego sterownika C #.public void List<User> GetUsers() { var connectionString = "<a connection string>"; var client = new MongoClient(connectionString); var server = client.GetServer(); var database = server.GetDatabase("<a database name>"); var sortBy = SortBy<User>.Descending(u => u.CreatedDate); var collection = database.GetCollection<User>("Users"); var cursor = collection.FindAll(); cursor.SetSortOrder(sortBy); cursor.Skip = pageIndex * pageSize; cursor.Limit = pageSize; return cursor.ToList(); }
Wszystkie operacje sortowania i stronicowania są wykonywane po stronie serwera. Chociaż jest to przykład w C #, myślę, że to samo można zastosować do portów innych języków.
Zobacz http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it .
źródło
// file:ad-hoc.js // an example of using the less binary as pager in the bash shell // // call on the shell by: // mongo localhost:27017/mydb ad-hoc.js | less // // note ad-hoc.js must be in your current directory // replace the 27017 wit the port of your mongodb instance // replace the mydb with the name of the db you want to query // // create the connection obj conn = new Mongo(); // set the db of the connection // replace the mydb with the name of the db you want to query db = conn.getDB("mydb"); // replace the products with the name of the collection // populate my the products collection // this is just for demo purposes - you will probably have your data already for (var i=0;i<1000;i++ ) { db.products.insert( [ { _id: i, item: "lamp", qty: 50, type: "desk" }, ], { ordered: true } ) } // replace the products with the name of the collection cursor = db.products.find(); // print the collection contents while ( cursor.hasNext() ) { printjson( cursor.next() ); } // eof file: ad-hoc.js
źródło