113 votes

Requête Mongoose de limite/excentration et de comptage

Une question un peu étrange sur les performances des requêtes... J'ai besoin d'exécuter une requête qui fait un comptage total des documents, et qui peut également retourner un ensemble de résultats qui peut être limité et décalé.

Ainsi, j'ai 57 documents au total, et l'utilisateur veut 10 documents décalés de 20.

Je vois deux façons de procéder : la première consiste à demander l'ensemble des 57 documents (renvoyés sous forme de tableau), puis à utiliser array.slice pour renvoyer les documents souhaités. La deuxième option consiste à lancer deux requêtes, la première utilisant la méthode native "count" de Mongo, puis une deuxième requête utilisant les agrégateurs natifs $limit et $skip de Mongo.

Lequel, selon vous, serait le plus adapté ? Tout faire en une seule requête, ou en exécuter deux séparément ?

Edita:

// 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});
    });
});

176voto

user854301 Points 1510

Je vous suggère d'utiliser 2 requêtes :

  1. db.collection.count() retournera le nombre total d'éléments. Cette valeur est stockée quelque part dans Mongo et n'est pas calculée.

  2. db.collection.find().skip(20).limit(10) Ici, je suppose que vous pourriez utiliser un tri par un certain champ, donc n'oubliez pas d'ajouter un index sur ce champ. Cette requête sera également rapide.

Je pense que vous ne devriez pas interroger tous les éléments et ensuite effectuer un saut et une reprise, car plus tard, lorsque vous aurez de grandes données, vous aurez des problèmes de transfert et de traitement des données.

31voto

DhineshYes Points 761

Au lieu d'utiliser 2 requêtes distinctes, vous pouvez utiliser aggregate() en une seule requête :

Agrégat "$facet" peut être récupéré plus rapidement, le Nombre total y el Données avec saut et limite

    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"
     }}

 ]);

Le résultat est le suivant

[{
     count: 50,
     data: [
        {...},
        {...}
      ]
 }]

Jetez également un coup d'œil à https://docs.mongodb.com/manual/reference/operator/aggregation/facet/

6voto

SANJEEV RAVI Points 139
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
    } }
] )

Au lieu d'utiliser deux requêtes pour trouver le nombre total et ignorer l'enregistrement correspondant.
$facet est le meilleur moyen et le plus optimisé.

  1. Faire correspondre l'enregistrement

  2. Trouver total_count

  3. sauter l'enregistrement

  4. Nous pouvons également remodeler les données en fonction de nos besoins dans la requête.

4voto

WootWoot Points 423

Il existe une bibliothèque qui peut faire tout cela pour vous. mongoose-paginate-v2

3voto

oli_taz Points 167

Après avoir été confronté à ce problème moi-même, je voudrais m'appuyer sur la réponse de l'utilisateur 854301.

Mongoose ^4.13.8 J'ai pu utiliser une fonction appelée toConstructor() ce qui m'a permis d'éviter de construire la requête plusieurs fois lorsque des filtres sont appliqués. Je sais que cette fonction est également disponible dans les versions antérieures, mais vous devrez consulter la documentation de Mongoose pour le confirmer.

Ce qui suit utilise les promesses 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 }
    }
);

Maintenant, la requête de comptage renverra le total des enregistrements qu'elle a fait correspondre et les données renvoyées seront un sous-ensemble du total des enregistrements.

Veuillez noter que le () autour de query() qui construit la requête.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X