Jak zdobyć ostatnie N rekordów w mongodb?

523

Nigdzie nie mogę tego znaleźć. Domyślnie operacja find () pobierze rekordy od początku. Jak mogę uzyskać ostatnie N rekordów w mongodb?

Edycja: chcę również, aby zwracany wynik był uporządkowany od mniej do najnowszego, a nie odwrotnie.

Bin Chen
źródło
7
@Haim, proszę podać konkretną odpowiedź, która część strony rozwiązuje moje pytanie?
Bin Chen,
Cześć @BinChen, mam ostatnio ten sam problem, czy został rozwiązany?
Hanton

Odpowiedzi:

736

Jeśli rozumiem twoje pytanie, musisz sortować rosnąco.

Zakładając, że masz jakieś pole identyfikatora lub daty o nazwie „x”, zrobiłbyś ...

.sortować()


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

1 sortuje rosnąco (od najstarszych do najnowszych) i -1 będzie sortowanie malejące (od najnowszych do najstarszych.)

Jeśli korzystasz z automatycznie utworzonego pola _id , ma on osadzoną datę ... więc możesz użyć go do zamówienia przez ...

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

Spowoduje to powrót wszystkich dokumentów posortowanych od najstarszego do najnowszego.

Naturalny porządek


Możesz także użyć wspomnianego wyżej Naturalnego Porządku ...

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

Ponownie, używając 1 lub -1 w zależności od żądanej kolejności.

Użyj .limit ()


Wreszcie, dobrą praktyką jest dodanie limitu podczas wykonywania tego rodzaju szeroko otwartych zapytań, aby można było wykonać ...

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

lub

db.foo.find().sort({$natural:1}).limit(50);
Justin Jenkins
źródło
11
@MortezaM. Jestem prawie pewien, że masz pomieszaną kolejność zapytań ... twoje sort () powinno być uruchamiane jako ostatnie, a nie pierwsze (podobnie jak SQL ORDER BY) .find ({}). Skip (1) .limit ( 50) .sort ({„date”: - 1})
Justin Jenkins
6
Cokolwiek to jest, kolejność wywoływania funkcji nie powinna mieć nic wspólnego z wynikiem końcowym.
Morteza Milani,
7
@MortezaM. Oczywiście kolejność ma znaczenie. item = 3; item.add(3).divide(3) == 2; item.divide(3).add(3) == 4;Bez zamówienia, jaki byłby wynik? Zgadzam się z tobą, że ta odwrócona kolejność nie jest intuicyjna. To nie jest SQL, w końcu powinien przestrzegać normalnych paradygmatów OO.
RickyA
37
a jednak kolejność nie ma znaczenia. Wszystko, co musisz zrobić, to spróbować, aby zobaczyć, że tak nie jest. wszystkie z nich są wywoływane na kursorze i przekazywane do serwera, więc serwer ogranicza wyniki sortowania (górne N), ponieważ wszystko inne nie miałoby sensu.
Asya Kamsky
9
Dokumenty potwierdzają, że kolejność nie ma znaczenia: link
JohnnyHK,
120

Ostatnie N dodanych rekordów, od najnowszych do najnowszych, można zobaczyć za pomocą tego zapytania:

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

Jeśli chcesz je w odwrotnej kolejności:

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

Jeśli zainstalujesz Mongo-Hacker , możesz również użyć:

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

Jeśli masz dość pisania tych poleceń, możesz tworzyć własne funkcje w pliku ~ / .mongorc.js. Na przykład

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

następnie z muszli mangowej po prostu wpisz last(N)

Trasplazio Garzuglio
źródło
1
db.collection.find().reverse().limit(1)daje mi błąd... has no method reverse
Catfish
@Catfish masz rację, właśnie zauważyłem, że [Mongo-Hacker] ( tylerbrock.github.com/mongo-hacker ) dodał ( ) aktualizację mojej odpowiedzi. Dzięki.
Trasplazio Garzuglio
1
db.getCollection ('COLLECTION_NAME'). find (). skip (db.getCollection ('COLLECTION_NAME'). count () - N) działa świetnie dla mnie :)
Spl2nky 24.07.2016
To powinna być odpowiedź na pytanie, a nie odpowiedź Justina Jenkinsa.
Jadiel de Armas,
Nie należy polegać na porządku naturalnym ; jeśli używasz zestawu replik (i powinieneś tak być), różne węzły mogą mieć te same dokumenty przechowywane w innej kolejności na dysku.
Vince Bowdren,
14

Aby uzyskać ostatnie N rekordów, możesz wykonać poniższe zapytanie:

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

jeśli chcesz tylko jeden ostatni rekord:

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

Uwaga: Zamiast $ natural możesz użyć jednej z kolumn ze swojej kolekcji.

Ashwini
źródło
to działa dla mnie db.yourcollectionname.findOne ({$ query: {}, $ orderby: {$ natural: -1}}). Myślę, że w odpowiedzi brakuje ostatniego parathensis
Ajay
Nie należy polegać na porządku naturalnym ; jeśli używasz zestawu replik (i powinieneś tak być), różne węzły mogą mieć te same dokumenty przechowywane w innej kolejności na dysku.
Vince Bowdren,
6

można użyć sort(), limit(), skip()aby dostać ostatni N Start rekordu z dowolnej wartości pominiętego

db.collections.find().sort(key:value).limit(int value).skip(some int value);
pkp
źródło
6

Możesz wypróbować tę metodę:

Uzyskaj całkowitą liczbę rekordów w kolekcji za pomocą

db.dbcollection.count() 

Następnie użyj pominięcia:

db.dbcollection.find().skip(db.dbcollection.count() - 1).pretty()
bello hargbola
źródło
5

Zajrzyj do zapytania: Sortowanie i porządek naturalny, http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order, a także sort () w Cursor Methods http://www.mongodb.org/display / DOCS / Advanced + Queries

Steve Wilhelm
źródło
1
Dzięki za odpowiedź, jest blisko, ale chcę zachować zapisy uporządkowane od mniej do najnowszego, czy to możliwe?
Bin Chen,
Nie należy polegać na porządku naturalnym ; jeśli używasz zestawu replik (i powinieneś tak być), różne węzły mogą mieć te same dokumenty przechowywane w innej kolejności na dysku.
Vince Bowdren,
Jeśli chcesz uzyskać najnowsze rekordy, będziesz musiał polegać na polu daty w dokumencie.
Vince Bowdren,
5

Nie można „pominąć” na podstawie wielkości kolekcji, ponieważ nie uwzględni ona warunków zapytania.

Prawidłowym rozwiązaniem jest sortowanie od pożądanego punktu końcowego, ograniczenie rozmiaru zestawu wyników, a następnie, w razie potrzeby, dostosowanie kolejności wyników.

Oto przykład oparty na kodzie rzeczywistym.

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
źródło
Niezłe obejście! Byłoby miło móc zrobić to samo na poziomie zapytania. Coś jak var query = collection.find( { conditions } ).sort({$natural : -1}).reverse().limit(N).
inwpitrust
4

@ bin-chen,

Możesz użyć agregacji dla ostatnich n wpisów podzbioru dokumentów w kolekcji. Oto uproszczony przykład bez grupowania (co w tym przypadku można zrobić między etapami 4 i 5).

Zwraca 20 ostatnich wpisów (na podstawie pola o nazwie „znacznik czasu”), posortowanych rosnąco. Następnie wyświetla w wynikach każdy dokument _id, znacznik czasu i cokolwiek_pole_na_na_na_wyświetleniu.

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

więc wynik wyglądałby mniej więcej tak:

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

Mam nadzieję że to pomoże.

lauri108
źródło
1
DZIĘKUJEMY @ lauri108! Spośród wszystkich odpowiedzi na to i powiązane pytania, jest to JEDYNIE działające i niezawodne rozwiązanie „jak zdobyć OSTATNIO N DOCS”. I wystarczająco proste do zrobienia w jednym zapytaniu. Zadanie wykonane.
randomsock
@randomsock, jeśli Ci się spodoba, zagłosuj :)
lauri108
4

Sortowanie, pomijanie itd. Może być dość powolne, w zależności od wielkości Twojej kolekcji.

Lepszą wydajność można osiągnąć, jeśli masz kolekcję indeksowaną według niektórych kryteriów; a następnie możesz użyć kursora min ():

Po pierwsze, indeksuj kolekcję za pomocą db.collectionName.setIndex( yourIndex ) Możesz użyć rosnącej lub malejącej kolejności, co jest fajne, ponieważ chcesz zawsze mieć „N ostatnich pozycji” ... więc jeśli indeksujesz według malejącej kolejności, to to samo, co uzyskanie „pierwszych N pozycji” .

Następnie znajdujesz pierwszy element swojej kolekcji i używasz jego wartości pola indeksu jako minimalnych kryteriów wyszukiwania, takich jak:

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

Oto odwołanie do kursora min (): https://docs.mongodb.com/manual/reference/method/cursor.min/

João Otero
źródło
Tylko odpowiedź, która uwzględnia wydajność, świetny dodatek :)
zardilior
Czy ta odpowiedź gwarantuje porządek?
zardilior
tak, to gwarantuje, na podstawie twojego indeksu
João Otero
Najwyraźniej możesz po prostu zapomnieć o min i użyć: db.collectionName.find().hint(yourIndex).limit(N) Jeśli indeks jest w porządku malejącym, otrzymasz N minimalnych wartości.
haltux
0
db.collection.find().hint( { $natural : -1 } ).sort(field: 1/-1).limit(n)

zgodnie z dokumentacją mongoDB :

Możesz określić {$ natural: 1}, aby zmusić zapytanie do wykonania skanowania kolekcji do przodu.

Możesz także określić {$ natural: -1}, aby wymusić wykonanie przez kwerendę skanowania kolekcji wstecznej.

Gili.il
źródło
0

użyj operatora $ slice do ograniczenia elementów tablicy

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

gdzie geolokalizacja to tablica danych, z których otrzymujemy ostatnie 5 rekordów.

KARTHIKEYAN.A
źródło
-5

Ostatnią funkcją powinno być sort, nie limit.

Przykład:

db.testcollection.find().limit(3).sort({timestamp:-1}); 
Ramesh Kasi
źródło
2
To wydaje się niepoprawne. Dlaczego miałby sortbyć później limit?
Acumenus,