Un poco extraño en el rendimiento de la consulta ... Necesito ejecutar una consulta que haga un recuento total de documentos, y también puedo devolver un conjunto de resultados que puede ser limitado y compensado.
Entonces, tengo 57 documentos en total y el usuario quiere 10 documentos compensados por 20.
Puedo pensar en 2 formas de hacer esto, primero es consultar los 57 documentos (devueltos como una matriz), luego usar array.slice devuelve los documentos que desean. La segunda opción es ejecutar 2 consultas, la primera usando el método nativo 'count' de mongo, luego ejecutar una segunda consulta usando los agregadores nativos $ limit y $ skip de mongo.
¿Cuál crees que escalaría mejor? ¿Hacerlo todo en una consulta o ejecutar dos independientes?
Editar:
// 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()
función en PHP no tomalimit
oskip
en cuenta a menos que se lo que sólo se ejecuta una consulta de límite y salte y luego conseguir el conteo debe dar la solución más probable performant aquí. Sin embargo, ¿cómo sabrá que hay 57 documentos si no hace dos consultas para contar lo que hay actualmente? ¿Tiene un número estático que nunca cambia? Si no es así, deberá realizar tanto el salto como el límite y luego el conteo.db.collection.find(<query>).count();
count()
función de MongoDB. Lacount()
función en MongoDB es relativamente lenta, pero sigue siendo bastante tan rápida como la mayoría de las variaciones del lado del cliente en conjuntos más grandes y podría ser más rápida que el conteo del lado del cliente aquí. Pero esa parte es subjetiva para sus propias pruebas. Tenga en cuenta que antes he contado matrices de 10k de longitud fácilmente, por lo que podría ser más rápido en el lado del cliente, es muy difícil decirlo en 10k elementos.Respuestas:
Le sugiero que utilice 2 consultas:
db.collection.count()
devolverá el número total de artículos. Este valor se almacena en algún lugar de Mongo y no se calcula.db.collection.find().skip(20).limit(10)
aquí supongo que podría usar una clasificación por algún campo, así que no olvide agregar un índice en este campo. Esta consulta también será rápida.Creo que no debe consultar todos los elementos y luego realizar la omisión y la toma, porque más adelante, cuando tenga big data, tendrá problemas con la transferencia y el procesamiento de datos.
fuente
.skip()
instrucción es pesada para la CPU porque va al comienzo de la colección y llega al valor especificado en el parámetro de.skip()
. ¡Puede tener un impacto real en una gran colección! Pero no sé cuál es el más pesado entre uso de.skip()
todos modos o obtener toda la colección y recortar con JS ... ¿Qué les parece?.skip()
. Esta respuesta lo retoca y aconseja usar un filtro en un campo de fecha. Se podría usar esto con los métodos.skip()
&.take()
. Esto parece una buena idea. Sin embargo, tengo problemas con la pregunta de este OP sobre cómo obtener un recuento del total de documentos. Si se utiliza un filtro para combatir las implicaciones de rendimiento de.skip()
, ¿cómo podemos tener un recuento preciso? El recuento almacenado en la base de datos no reflejará nuestro conjunto de datos filtrados.cursor.count()
para devolver el número de documentos filtrados (no ejecutará la consulta, le devolverá el número de documentos coincidentes). Asegúrese de que las propiedades de filtrado y orden estén indexadas y todo estará bien.cursor.count()
debería funcionar como señaló @ user854301. Sin embargo, lo que terminé haciendo fue agregar un punto final a mi API (/api/my-colllection/stats
) que usé para devolver varias estadísticas en mis colecciones usando la función db.collection.stats de Mongoose . Como realmente solo necesitaba esto para mi interfaz, solo consulté el punto final para devolver esa información independientemente de la paginación del lado del servidor.En lugar de usar 2 consultas separadas, puede usar
aggregate()
en una sola consulta:El agregado "$ facet" se puede recuperar más rápidamente, el recuento total y los datos con omisión y límite
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" }} ]);
salida de la siguiente manera: -
[{ count: 50, data: [ {...}, {...} ] }]
Además, eche un vistazo a https://docs.mongodb.com/manual/reference/operator/aggregation/facet/
fuente
Después de tener que abordar este problema yo mismo, me gustaría aprovechar la respuesta del usuario854301.
Mongoose ^ 4.13.8 Pude usar una función llamada
toConstructor()
que me permitió evitar generar la consulta varias veces cuando se aplican filtros. Sé que esta función también está disponible en versiones anteriores, pero tendrás que consultar los documentos de Mongoose para confirmarlo.Lo siguiente usa las promesas de 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 } } );
Ahora, la consulta de recuento devolverá el total de registros que coincidió y los datos devueltos serán un subconjunto del total de registros.
Tenga en cuenta el () alrededor de query () que construye la consulta.
fuente
Hay una biblioteca que hará todo esto por usted, consulte mongoose-paginate-v2
fuente
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 } } ] )
En lugar de utilizar dos consultas para encontrar el recuento total y omitir el registro coincidente.
$ facet es la mejor y más optimizada forma.
fuente