Comment obtenir les derniers enregistrements N dans mongodb?

524

Je ne trouve nulle part où cela a été documenté. Par défaut, l'opération find () récupère les enregistrements depuis le début. Comment puis-je obtenir les derniers N enregistrements dans mongodb?

Edit: je veux aussi que le résultat retourné soit ordonné du moins récent au plus récent, pas l'inverse.

Bin Chen
la source
7
@Haim, veuillez être précis pour répondre, quelle partie de la page Web résout ma question?
Bin Chen
Salut @BinChen, j'ai récemment rencontré le même problème, est-il résolu?
Hanton

Réponses:

737

Si je comprends votre question, vous devez trier par ordre croissant.

En supposant que vous ayez un champ id ou date appelé "x", vous feriez ...

.Trier()


db.foo.find().sort({x:1});

Le 1 triera par ordre croissant (du plus ancien au plus récent) et -1 par ordre décroissant (du plus récent au plus ancien).

Si vous utilisez le champ _id créé automatiquement, il contient une date intégrée ... vous pouvez donc l'utiliser pour commander avant ...

db.foo.find().sort({_id:1});

Cela ramènera tous vos documents triés du plus ancien au plus récent.

Ordre naturel


Vous pouvez également utiliser un ordre naturel mentionné ci-dessus ...

db.foo.find().sort({$natural:1});

Encore une fois, utilisez 1 ou -1 selon l'ordre que vous souhaitez.

Utilisez .limit ()


Enfin, il est recommandé d'ajouter une limite lors de ce type de requête grand ouvert afin que vous puissiez faire l'une ou l'autre ...

db.foo.find().sort({_id:1}).limit(50);

ou

db.foo.find().sort({$natural:1}).limit(50);
Justin Jenkins
la source
11
@MortezaM. Je suis sûr que vous avez votre commande de requête mélangé ... votre sort () doit être exécuté dernier, pas d' abord (comme une PAR SQL ORDER) .Find ({}). Saut (1) .limit ( 50) .sort ({"date": - 1})
Justin Jenkins
6
Quoi qu'il en soit, l'ordre des fonctions d'appel ne devrait avoir rien à voir avec le résultat final.
Morteza Milani
7
@MortezaM. Bien sûr, l'ordre est important. item = 3; item.add(3).divide(3) == 2; item.divide(3).add(3) == 4;Sans ordre, quel serait le résultat ??? Je suis d'accord avec vous que cet ordre inversé n'est pas intuitif. Ce n'est pas du SQL après tout, il doit suivre les paradigmes OO normaux.
RickyA
38
et pourtant l'ordre n'a pas d'importance. Tout ce que vous avez à faire est de l'essayer pour voir que ce n'est pas le cas. tous ces éléments sont appelés sur le curseur et transmis au serveur afin que le serveur limite les résultats du tri (N supérieur) car rien d'autre n'aurait de sens.
Asya Kamsky
10
Les documents confirment que la commande n'a pas d'importance: lien
JohnnyHK
120

Les N derniers enregistrements ajoutés, du moins récent au plus récent, peuvent être vus avec cette requête:

db.collection.find().skip(db.collection.count() - N)

Si vous les voulez dans l'ordre inverse:

db.collection.find().sort({ $natural: -1 }).limit(N)

Si vous installez Mongo-Hacker, vous pouvez également utiliser:

db.collection.find().reverse().limit(N)

Si vous en avez assez d'écrire ces commandes tout le temps, vous pouvez créer des fonctions personnalisées dans votre ~ / .mongorc.js. Par exemple

function last(N) {
    return db.collection.find().skip(db.collection.count() - N);
}

puis à partir d'un shell mongo il suffit de taper last(N)

Trasplazio Garzuglio
la source
1
db.collection.find().reverse().limit(1)me donne l'erreur... has no method reverse
Catfish
@Catfish vous avez raison, je viens de remarquer que reverse () a été ajouté par [Mongo-Hacker] ( tylerbrock.github.com/mongo-hacker ), je mettrai à jour ma réponse. Merci.
Trasplazio Garzuglio
1
db.getCollection ('COLLECTION_NAME'). find (). skip (db.getCollection ('COLLECTION_NAME'). count () - N) fonctionne très bien pour moi :)
Spl2nky
Ce devrait être la réponse à la question, et non la réponse de Justin Jenkins.
Jadiel de Armas
Il ne faut pas se fier à l' ordre naturel ; si vous utilisez un jeu de réplicas (et vous devriez l'être), différents nœuds peuvent très bien avoir les mêmes documents stockés dans un ordre différent sur le disque.
Vince Bowdren
14

Afin d'obtenir les N derniers enregistrements, vous pouvez exécuter la requête ci-dessous:

db.yourcollectionname.find({$query: {}, $orderby: {$natural : -1}}).limit(yournumber)

si vous ne voulez qu'un dernier enregistrement:

db.yourcollectionname.findOne({$query: {}, $orderby: {$natural : -1}})

Remarque: au lieu de $ natural, vous pouvez utiliser l'une des colonnes de votre collection.

Ashwini
la source
cela fonctionne pour moi db.yourcollectionname.findOne ({$ query: {}, $ orderby: {$ natural: -1}}). Je pense que le dernier parathensis manque dans la réponse
Ajay
Il ne faut pas se fier à l' ordre naturel ; si vous utilisez un jeu de réplicas (et vous devriez l'être), différents nœuds peuvent très bien avoir les mêmes documents stockés dans un ordre différent sur le disque.
Vince Bowdren
6

vous pouvez utiliser sort(), limit(), skip()pour obtenir début d'enregistrement dernier N de toute valeur sautée

db.collections.find().sort(key:value).limit(int value).skip(some int value);
pkp
la source
6

Vous pouvez essayer cette méthode:

Obtenez le nombre total d'enregistrements dans la collection avec

db.dbcollection.count() 

Utilisez ensuite skip:

db.dbcollection.find().skip(db.dbcollection.count() - 1).pretty()
bello hargbola
la source
5

Regardez sous Querying: Sorting and Natural Order, http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order ainsi que sort () sous Cursor Methods http://www.mongodb.org/display / DOCS / Advanced + Requêtes

Steve Wilhelm
la source
1
Merci pour votre réponse, elle est proche, mais je souhaite récupérer des enregistrements classés du moins récent au plus récent, est-ce possible?
Bin Chen
Il ne faut pas se fier à l' ordre naturel ; si vous utilisez un jeu de réplicas (et vous devriez l'être), différents nœuds peuvent très bien avoir les mêmes documents stockés dans un ordre différent sur le disque.
Vince Bowdren
Si vous souhaitez obtenir les enregistrements les plus récents, vous devrez vous fier à un champ de date dans le document.
Vince Bowdren
5

Vous ne pouvez pas "ignorer" en fonction de la taille de la collection, car elle ne tiendra pas compte des conditions de requête.

La bonne solution consiste à trier à partir du point final souhaité, à limiter la taille de l'ensemble de résultats, puis à ajuster l'ordre des résultats si nécessaire.

Voici un exemple, basé sur du code du monde réel.

var query = collection.find( { conditions } ).sort({$natural : -1}).limit(N);

query.exec(function(err, results) {
    if (err) { 
    }
    else if (results.length == 0) {
    }
    else {
        results.reverse(); // put the results into the desired order
        results.forEach(function(result) {
            // do something with each result
        });
    }
});
Marty Hirsch
la source
Belle solution de contournement! Ce serait bien de pouvoir faire la même chose au niveau de la requête. Quelque chose comme var query = collection.find( { conditions } ).sort({$natural : -1}).reverse().limit(N).
inwpitrust
4

@ bin-chen,

Vous pouvez utiliser une agrégation pour les n dernières entrées d'un sous-ensemble de documents dans une collection. Voici un exemple simplifié sans regroupement (ce que vous feriez entre les étapes 4 et 5 dans ce cas).

Cela renvoie les 20 dernières entrées (basées sur un champ appelé "horodatage"), triées par ordre croissant. Il projette ensuite chaque document _id, horodatage et tout_field_you_want_to_show dans les résultats.

var pipeline = [
        {
            "$match": { //stage 1: filter out a subset
                "first_field": "needs to have this value",
                "second_field": "needs to be this"
            }
        },
        {
            "$sort": { //stage 2: sort the remainder last-first
                "timestamp": -1
            }
        },
        {
            "$limit": 20 //stage 3: keep only 20 of the descending order subset
        },
        {
            "$sort": {
                "rt": 1 //stage 4: sort back to ascending order
            }
        },
        {
            "$project": { //stage 5: add any fields you want to show in your results
                "_id": 1,
                "timestamp" : 1,
                "whatever_field_you_want_to_show": 1
            }
        }
    ]

yourcollection.aggregate(pipeline, function resultCallBack(err, result) {
  // account for (err)
  // do something with (result)
}

ainsi, le résultat ressemblerait à quelque chose comme:

{ 
    "_id" : ObjectId("5ac5b878a1deg18asdafb060"),
    "timestamp" : "2018-04-05T05:47:37.045Z",
    "whatever_field_you_want_to_show" : -3.46000003814697
}
{ 
    "_id" : ObjectId("5ac5b878a1de1adsweafb05f"),
    "timestamp" : "2018-04-05T05:47:38.187Z",
    "whatever_field_you_want_to_show" : -4.13000011444092
}

J'espère que cela t'aides.

lauri108
la source
1
MERCI @ lauri108! De toutes les réponses à cette question et aux questions connexes, c'est LA SEULE solution efficace et fiable pour "comment obtenir les DERNIERS DOCS". Et assez simple à faire en une seule requête. Travail accompli.
randomsock
@randomsock si vous l'aimez, votez-le :)
lauri108
4

Le tri, le saut, etc. peuvent être assez lents en fonction de la taille de votre collection.

Une meilleure performance serait obtenue si votre collection était indexée selon certains critères; puis vous pouvez utiliser le curseur min ():

Tout d'abord, indexez votre collection avec db.collectionName.setIndex( yourIndex ) Vous pouvez utiliser l'ordre croissant ou décroissant, ce qui est cool, car vous voulez toujours les "N derniers éléments" ... donc si vous indexez par ordre décroissant, c'est la même chose que d'obtenir les "premiers N éléments" .

Ensuite, vous trouvez le premier élément de votre collection et utilisez ses valeurs de champ d'index comme critères minimum dans une recherche comme:

db.collectionName.find().min(minCriteria).hint(yourIndex).limit(N)

Voici la référence pour le curseur min (): https://docs.mongodb.com/manual/reference/method/cursor.min/

João Otero
la source
Seule réponse qui tient compte des performances, excellent ajout :)
zardilior
Cette réponse garantit-elle l'ordre?
zardilior
oui, cela garantit, en fonction de votre indice
João Otero
1
Apparemment, vous pouvez simplement oublier le min et utiliser: db.collectionName.find().hint(yourIndex).limit(N) Si l'index est dans l'ordre décroissant, vous obtenez les N valeurs minimales.
haltux
0
db.collection.find().hint( { $natural : -1 } ).sort(field: 1/-1).limit(n)

selon la documentation de mongoDB :

Vous pouvez spécifier {$ natural: 1} pour forcer la requête à effectuer une analyse de collecte vers l'avant.

Vous pouvez également spécifier {$ natural: -1} pour forcer la requête à effectuer une analyse de collection inverse.

Gili.il
la source
0

utiliser l' opérateur $ slice pour limiter les éléments du tableau

GeoLocation.find({},{name: 1, geolocation:{$slice: -5}})
    .then((result) => {
      res.json(result);
    })
    .catch((err) => {
      res.status(500).json({ success: false, msg: `Something went wrong. ${err}` });
});

où la géolocalisation est un tableau de données, d'où nous obtenons les 5 derniers enregistrements.

KARTHIKEYAN.A
la source
-5

La dernière fonction devrait être sort, non limit.

Exemple:

db.testcollection.find().limit(3).sort({timestamp:-1}); 
Ramesh Kasi
la source
2
Cela semble incorrect. Pourquoi serait sortaprès limit?
Acumenus