Немного странно в отношении производительности запроса ... Мне нужно запустить запрос, который выполняет общее количество документов, а также может возвращать набор результатов, который может быть ограничен и смещен.
Итак, у меня 57 документов, и пользователь хочет, чтобы 10 документов были компенсированы на 20.
Я могу придумать два способа сделать это: сначала запросить все 57 документов (возвращенных в виде массива), а затем с помощью array.slice вернуть нужные документы. Второй вариант - запустить 2 запроса, первый - с использованием собственного метода «count» mongo, а затем запустить второй запрос с использованием собственных агрегаторов mongo $ limit и $ skip.
Как вы думаете, что лучше масштабируется? Выполнять все это одним запросом или запускать два отдельных запроса?
Редактировать:
// 1 query
var limit = 10;
var offset = 20;
Animals.find({}, function (err, animals) {
if (err) {
return next(err);
}
res.send({count: animals.length, animals: animals.slice(offset, limit + offset)});
});
// 2 queries
Animals.find({}, {limit:10, skip:20} function (err, animals) {
if (err) {
return next(err);
}
Animals.count({}, function (err, count) {
if (err) {
return next(err);
}
res.send({count: count, animals: animals});
});
});
count()
функция по умолчанию в PHP не принимаетlimit
и неskip
учитывает, если только не сказано, поэтому просто выполнение одного запроса лимита и пропуска, а затем получение счетчика должно дать, вероятно, наиболее эффективное решение. Однако как вы узнаете, что существует 57 документов, если вы не выполните два запроса, чтобы подсчитать, что там сейчас? У вас есть статический номер, который никогда не меняется? В противном случае вам нужно будет выполнить как пропуск, так и ограничение, а затем подсчет.db.collection.find(<query>).count();
count()
функция MongoDB.count()
Функция в MongoDB является относительно медленной , но он по - прежнему в значительной степени так же быстро , как и большинства клиентских вариаций на больших наборах , и это может быть быстрее , чем на стороне клиента подсчет здесь возможно. Но эта часть субъективна для вашего собственного тестирования. Имейте в виду, что раньше я легко считал массивы длиной 10 тыс., Так что это может быть быстрее на стороне клиента, очень сложно сказать, что на 10 тыс. Элементов.Ответы:
Предлагаю вам использовать 2 запроса:
db.collection.count()
вернет общее количество элементов. Это значение хранится где-то в Mongo и не вычисляется.db.collection.find().skip(20).limit(10)
здесь я предполагаю, что вы можете использовать сортировку по какому-либо полю, поэтому не забудьте добавить индекс в это поле. Этот запрос тоже будет быстрым.Я думаю, что вам не следует запрашивать все элементы, а затем выполнять пропуск и прием, потому что позже, когда у вас будут большие данные, у вас возникнут проблемы с передачей и обработкой данных.
источник
.skip()
инструкция тяжелая для ЦП, потому что она идет в начало коллекции и достигает значения, указанного в параметре.skip()
. Это может реально повлиять на большую коллекцию! Но я не знаю, какой из них самый тяжелый между использованием в.skip()
любом случае или получением всей коллекции и обрезкой с помощью JS ... Как вы думаете?.skip()
. Этот ответ затрагивает его и советует использовать фильтр в поле даты. Это можно использовать с помощью методов.skip()
&.take()
. Это кажется хорошей идеей. Однако у меня возникли проблемы с вопросом этого OP о том, как подсчитать общее количество документов. Если фильтр используется для борьбы с последствиями для производительности.skip()
, как мы можем получить точный подсчет? Счетчик, хранящийся в базе данных, не будет отражать наш отфильтрованный набор данных.cursor.count()
для возврата количества отфильтрованных документов (он не будет выполнять запрос, он вернет вам количество сопоставленных документов). Убедитесь, что свойства фильтрации и упорядочения проиндексированы, и все будет в порядке.cursor.count()
должно работать, как указал @ user854301. Однако в итоге я добавил к моему API (/api/my-colllection/stats
) конечную точку, которую я использовал для возврата различной статистики по моим коллекциям с помощью функции Mongoose db.collection.stats . Поскольку мне это действительно было нужно только для моего интерфейса, я просто запросил конечную точку, чтобы вернуть эту информацию независимо от моей разбивки на страницы на стороне сервера.Вместо использования двух отдельных запросов вы можете использовать
aggregate()
в одном запросе:Совокупный "$ фасет" можно получить быстрее, общее количество и данные с пропуском и ограничением
db.collection.aggregate([ //{$sort: {...}} //{$match:{...}} {$facet:{ "stage1" : [ {"$group": {_id:null, count:{$sum:1}}} ], "stage2" : [ { "$skip": 0}, {"$limit": 2} ] }}, {$unwind: "$stage1"}, //output projection {$project:{ count: "$stage1.count", data: "$stage2" }} ]);
вывод следующим образом: -
[{ count: 50, data: [ {...}, {...} ] }]
Также посмотрите https://docs.mongodb.com/manual/reference/operator/aggregation/facet/
источник
После того, как мне пришлось решить эту проблему самостоятельно, я хотел бы опираться на ответ user854301.
Mongoose ^ 4.13.8 Я смог использовать вызываемую функцию,
toConstructor()
которая позволила мне избежать многократного построения запроса при применении фильтров. Я знаю, что эта функция доступна и в более старых версиях, но вам нужно будет проверить документацию Mongoose, чтобы подтвердить это.Далее используются обещания Bluebird:
let schema = Query.find({ name: 'bloggs', age: { $gt: 30 } }); // save the query as a 'template' let query = schema.toConstructor(); return Promise.join( schema.count().exec(), query().limit(limit).skip(skip).exec(), function (total, data) { return { data: data, total: total } } );
Теперь запрос на счетчик вернет общее количество сопоставленных записей, а возвращенные данные будут подмножеством общих записей.
Обратите внимание на () вокруг query (), который создает запрос.
источник
Есть библиотека, которая сделает все это за вас, посмотрите mongoose-paginate-v2
источник
db.collection_name.aggregate([ { '$match' : { } }, { '$sort' : { '_id' : -1 } }, { '$facet' : { metadata: [ { $count: "total" } ], data: [ { $skip: 1 }, { $limit: 10 },{ '$project' : {"_id":0} } ] // add projection here wish you re-shape the docs } } ] )
Вместо использования двух запросов для нахождения общего количества и пропуска совпадающей записи.
$ facet - лучший и оптимизированный способ.
источник