В MongoDB - пейджинг
при использовании MongoDB существуют ли какие-либо специальные шаблоны для создания, например, paged view? скажем, блог, в котором перечислены 10 последних сообщений, где вы можете перейти назад к более старым сообщениям.
или решить его с помощью индекса, например, blogpost.publishdate и просто пропустить и ограничить результат?
6 ответов:
использование skip + limit не является хорошим способом сделать подкачку, когда производительность является проблемой или с большими коллекциями; он будет становиться все медленнее и медленнее по мере увеличения номера страницы. Использование skip требует, чтобы сервер прошел через все документы (или значения Индекса) от 0 до значения смещения (пропуска).
гораздо лучше использовать запрос диапазона (+limit), где вы передаете значение диапазона последней страницы. Например, если вы сортируете по "publishdate" , вы просто передадите последний значение "publishdate" в качестве критерия запроса для получения следующей страницы данных.
- диапазон на основе подкачки трудно реализовать, если вам нужно сортировать элементы во многих отношениях.
- помните, что если значение поля параметра сортировки не является уникальным , то подкачка на основе диапазона станет невозможной.
возможное решение: попробуйте упростить desgin, думая о том, можем ли мы сортировать только по идентификатору или некоторому уникальному значению?
и если мы можем, то диапазон на основе разбиения на страницы могут быть использованы.
распространенным способом является использование сортировки() , пропустить() и limit () для реализации подкачки то, что описано выше.
это решение я использовал, когда моя коллекция стала слишком большой, чтобы вернуть в одном запросе. Он использует преимущества присущего упорядочения
_id
поле и позволяет вам перебирать коллекцию по заданному размеру пакета.вот он как модуль npm,Мангуст-пейджинг, полный код ниже:
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; };
прикрепите его к вашей модели следующим образом:
MySchema.plugin(findPaged);
тогда запрос такой:
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) } );
диапазон на основе подкачки выполним, но вы должны быть умны о том, как вы мин/макс запрос.
Если вы можете себе это позволить, вы должны попробовать кэшировать результаты запроса во временном файле или коллекции. Благодаря TTL коллекциям в MongoDB вы можете вставить свои результаты в две коллекции.
- Search + User + Parameters Query (TTL whatever)
- результаты запроса (TTL whatever + интервал очистки + 1)
использование обоих гарантирует вам не будет получать частичные результаты, когда TTL находится рядом с текущим временем. Вы можете использовать простой счетчик, когда вы храните результаты, чтобы сделать очень простой запрос диапазона в этот момент.
вот пример получения списка
User
документы заказаCreatedDate
(гдеpageIndex
на основе нуля) с использованием официального драйвера 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(); }
все операции сортировки и разбиения на страницы выполняются на стороне сервера. Хотя это пример в C#, я думаю, что то же самое можно применить к другим языковым портам.
// 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