Как получить последние N записей в mongodb?


524

Я не могу найти где-нибудь это было задокументировано. По умолчанию операция find () будет получать записи с начала. Как я могу получить последние N записей в mongodb?

Изменить: также я хочу, чтобы возвращаемый результат упорядочивался от менее недавнего к самому последнему, а не наоборот.


7
@ Хаим, будь конкретен, чтобы ответить, какая часть веб-страницы решает мой вопрос?
Бен Чен

Привет @BinChen, у меня недавно та же проблема, она решена?
Хантон,

Ответы:


737

Если я понимаю ваш вопрос, вам нужно отсортировать в порядке возрастания.

Предполагая, что у вас есть поле идентификатора или даты с именем "x", вы бы сделали ...

.Сортировать()


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

1 будет сортировать по возрастанию ( от старого к новому) и -1 будет сортировать по убыванию ( от новых к старому.)

Если вы используете автоматически созданное поле _id, в него встроена дата ... так что вы можете использовать это для упорядочения по ...

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

Это вернет все ваши документы, отсортированные от самых старых до самых новых.

Естественный порядок


Вы также можете использовать Природный Орден, упомянутый выше ...

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

Опять же, используя 1 или -1 в зависимости от порядка, который вы хотите.

Используйте .limit ()


И наконец, хорошей практикой является добавление ограничения при выполнении такого широко открытого запроса, чтобы вы могли сделать либо ...

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

или

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

11
@MortezaM. Я почти уверен, что ваш порядок запросов перепутан ... ваш sort () должен выполняться последним, а не первым (во многом как SQL ORDER BY) .find ({}). Skip (1) .limit ( 50) .sort ({"date": - 1})
Джастин Дженкинс

6
Что бы это ни было, порядок вызова функций не должен иметь ничего общего с конечным результатом.
Мортеза Милани

7
@MortezaM. Конечно, порядок имеет значение. item = 3; item.add(3).divide(3) == 2; item.divide(3).add(3) == 4;Без порядка, каков будет результат ??? Я согласен с вами, что этот обратный порядок не является интуитивным. Это не SQL, в конце концов он должен следовать нормальным парадигмам OO.
RickyA

38
и все же порядок не имеет значения. Все, что вам нужно сделать, это попробовать, чтобы увидеть, что это не так. все они вызываются курсором и передаются на сервер, поэтому сервер ограничивает результаты сортировки (top N), так как все остальное не имеет смысла.
Ася Камская

10
Документы подтверждают, что порядок не имеет значения: ссылка
JohnnyHK

120

Последние N добавленных записей, от менее недавних до самых последних, можно увидеть с помощью этого запроса:

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

Если вы хотите их в обратном порядке:

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

Если вы устанавливаете Mongo-Hacker, вы также можете использовать:

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

Если вам надоело постоянно писать эти команды, вы можете создавать собственные функции в вашем ~ / .mongorc.js. Например

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

затем из оболочки монго просто введите last(N)


1
db.collection.find().reverse().limit(1)выдает ошибку... has no method reverse
Catfish

@ Catfish, вы правы, я только что заметил, что reverse () был добавлен [Mongo-Hacker] ( tylerbrock.github.com/mongo-hacker ), я обновлю свой ответ. Спасибо.
Trasplazio Garzuglio

1
db.getCollection ('COLLECTION_NAME'). find (). skip (db.getCollection ('COLLECTION_NAME'). count () - N) отлично работает для меня :)
Spl2nky

Это должен быть ответ на вопрос, а не ответ Джастина Дженкинса.
Джадьель де Армас

Естественный порядок должен не полагаться; если вы используете набор реплик (и вы должны это делать), на разных узлах вполне могут храниться одни и те же документы в другом порядке на диске.
Винс Боудрен

14

Чтобы получить последние N записей, вы можете выполнить следующий запрос:

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

если вы хотите только одну последнюю запись:

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

Примечание: вместо $ natural вы можете использовать один из столбцов из вашей коллекции.


это работает для меня db.yourcollectionname.findOne ({$ query: {}, $ orderby: {$ natural: -1}}). Я думаю, что последний паратензис отсутствует в ответе
Ajay

Естественный порядок должен не полагаться; если вы используете набор реплик (и вы должны это делать), на разных узлах вполне могут храниться одни и те же документы в другом порядке на диске.
Винс Боудрен

6

вы можете использовать sort(), limit(), skip()чтобы получить последний N начала записи из любого пропущенного значения

db.collections.find().sort(key:value).limit(int value).skip(some int value);

6

Вы можете попробовать этот метод:

Получить общее количество записей в коллекции с

db.dbcollection.count() 

Тогда используйте skip:

db.dbcollection.find().skip(db.dbcollection.count() - 1).pretty()

5

См. Раздел «Запросы: сортировка и естественный порядок», http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order, а также sort () в разделе «Методы курсора» http://www.mongodb.org/display. / DOCS / Advanced + Запросы


1
Спасибо за ваш ответ, это близко, но я хочу восстановить записи, упорядоченные от менее недавних до самых последних, возможно ли это?
Бен Чен

Естественный порядок должен не полагаться; если вы используете набор реплик (и вы должны это делать), на разных узлах вполне могут храниться одни и те же документы в другом порядке на диске.
Винс Боудрен

Если вы хотите получить самые последние записи, вам придется полагаться на поле даты в документе.
Винс Боудрен

5

Вы не можете «пропустить» в зависимости от размера коллекции, потому что она не будет учитывать условия запроса.

Правильное решение состоит в том, чтобы отсортировать от желаемой конечной точки, ограничить размер набора результатов, а затем при необходимости скорректировать порядок результатов.

Вот пример, основанный на реальном коде.

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

Хороший обходной путь! Было бы неплохо сделать то же самое на уровне запросов. Нечто подобное var query = collection.find( { conditions } ).sort({$natural : -1}).reverse().limit(N).
inwpitrust

4

@ Бен-чен,

Вы можете использовать агрегацию для последних n записей подмножества документов в собрании. Вот упрощенный пример без группировки (в данном случае вы будете делать это между этапами 4 и 5).

Возвращает последние 20 записей (на основе поля с именем timestamp), отсортированные по возрастанию. Затем он проецирует каждый документ _id, timestamp и what_field_you_want_to_show в результаты.

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

итак, результат будет выглядеть примерно так:

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

Надеюсь это поможет.


1
СПАСИБО @ lauri108! Из всех ответов на этот и смежные вопросы, это ЕДИНСТВЕННОЕ работающее и надежное решение «как получить ПОСЛЕДНИЕ ДОКУМЕНТЫ». И достаточно просто сделать в одном запросе. Работа выполнена.
randomsock

@randomsock, если вам это нравится, проголосуйте :)
lauri108

4

Сортировка, пропуск и т. Д. Могут быть довольно медленными в зависимости от размера вашей коллекции.

Лучшая производительность будет достигнута, если ваша коллекция проиндексирована по некоторым критериям; и тогда вы можете использовать курсор min ():

Во-первых, индексируйте свою коллекцию с помощью db.collectionName.setIndex( yourIndex ) Вы можете использовать восходящий или нисходящий порядок, что круто, потому что вы всегда хотите, чтобы «N последних элементов» ... так что если вы индексируете в порядке убывания, это то же самое, что получить «первые N элементов» ,

Затем вы находите первый элемент вашей коллекции и используете значения его индексного поля в качестве минимальных критериев поиска:

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

Вот ссылка на курсор min (): https://docs.mongodb.com/manual/reference/method/cursor.min/


Единственный ответ, который учитывает производительность, отличное дополнение :)
zardilior

Этот ответ гарантирует порядок?
zardilior

да, это гарантирует, на основе вашего индекса
Жоао Отеро

1
Очевидно, вы можете просто забыть мин и использовать: db.collectionName.find().hint(yourIndex).limit(N) Если индекс находится в порядке убывания, вы получите N минимальных значений.
Haltux


0
db.collection.find().hint( { $natural : -1 } ).sort(field: 1/-1).limit(n)

согласно документации mongoDB :

Вы можете указать {$ natural: 1}, чтобы запрос выполнял прямое сканирование коллекции.

Вы также можете указать {$ natural: -1}, чтобы запрос выполнял обратное сканирование коллекции.


0

используйте оператор $ slice для ограничения элементов массива

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

где геолокация - это массив данных, из которого мы получаем последние 5 записей.


-5

Последняя функция должна быть sort, а не limit.

Пример:

db.testcollection.find().limit(3).sort({timestamp:-1}); 

2
Это кажется неверным. Почему бы sortпосле limit?
Acumenus
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.