Знайдіть об’єкти між двома датами MongoDB


406

Я граю навколо зберігання твітів всередині mongodb, кожен об'єкт виглядає приблизно так:

{
"_id" : ObjectId("4c02c58de500fe1be1000005"),
"contributors" : null,
"text" : "Hello world",
"user" : {
    "following" : null,
    "followers_count" : 5,
    "utc_offset" : null,
    "location" : "",
    "profile_text_color" : "000000",
    "friends_count" : 11,
    "profile_link_color" : "0000ff",
    "verified" : false,
    "protected" : false,
    "url" : null,
    "contributors_enabled" : false,
    "created_at" : "Sun May 30 18:47:06 +0000 2010",
    "geo_enabled" : false,
    "profile_sidebar_border_color" : "87bc44",
    "statuses_count" : 13,
    "favourites_count" : 0,
    "description" : "",
    "notifications" : null,
    "profile_background_tile" : false,
    "lang" : "en",
    "id" : 149978111,
    "time_zone" : null,
    "profile_sidebar_fill_color" : "e0ff92"
},
"geo" : null,
"coordinates" : null,
"in_reply_to_user_id" : 149183152,
"place" : null,
"created_at" : "Sun May 30 20:07:35 +0000 2010",
"source" : "web",
"in_reply_to_status_id" : {
    "floatApprox" : 15061797850
},
"truncated" : false,
"favorited" : false,
"id" : {
    "floatApprox" : 15061838001
}

Як я можу написати запит, який перевіряє створений_at та знаходить усі об'єкти між 18:47 та 19:00? Чи потрібно оновлювати документи, щоб дати зберігалися у певному форматі?


Ви не кажете, про яке поле ви хочете запитувати?
shingara

На жаль, я хочу здійснити запит на create_at та знайти між двома датами.
Том

Мені цікаво, чому б не використати часові позначки, будь-які переваги, використовуючи Date Obj?
Лев

4
@Leo Найбільша перевага об'єкта Date за мілісекунди з часу епохи чи що інше - читабельність людини. У цьому випадку встановити свій початковий діапазон 2010-04-29T00:00:00.000Zнабагато простіше, ніж обчислити ту саму дату / час у мілісекундах. Ви також можете легко перетворити часовий пояс. Також, Дати вже обробляють такі речі, як високосні дні, високосні секунди та інші дивацтва, з якими зазвичай не хочеться впоратися самостійно.
Грім

Відповіді:


619

Запит на діапазон дат (конкретний місяць або день) у кулінарній книзі MongoDB має дуже гарне пояснення з цього питання, але нижче я щось випробував сам, і, здається, це працює.

items.save({
    name: "example",
    created_at: ISODate("2010-04-30T00:00:00.000Z")
})
items.find({
    created_at: {
        $gte: ISODate("2010-04-29T00:00:00.000Z"),
        $lt: ISODate("2010-05-01T00:00:00.000Z")
    }
})
=> { "_id" : ObjectId("4c0791e2b9ec877893f3363b"), "name" : "example", "created_at" : "Sun May 30 2010 00:00:00 GMT+0300 (EEST)" }

На основі моїх експериментів вам потрібно буде серіалізувати ваші дати у форматі, який підтримує MongoDB, оскільки наведені нижче дані давали небажані результати пошуку.

items.save({
    name: "example",
    created_at: "Sun May 30 18.49:00 +0000 2010"
})
items.find({
    created_at: {
        $gte:"Mon May 30 18:47:00 +0000 2015",
        $lt: "Sun May 30 20:40:36 +0000 2010"
    }
})
=> { "_id" : ObjectId("4c079123b9ec877893f33638"), "name" : "example", "created_at" : "Sun May 30 18.49:00 +0000 2010" }

У другому прикладі результатів не очікували, але все-таки було отримано один. Це тому, що робиться базове порівняння рядків.


3
Виглядає цікаво, але чи потрібно зберігати дату у певному форматі. Я щойно зберігав те, що було надано щебетачем, чи потрібно це змінити в інший формат?
Том

7
Ви, ймовірно, зберігали часові позначки як рядки, тому я здогадуюсь, MongoDB не зрозуміє, що вони насправді є датами. Таким чином, запит на діапазон на них призведе до алфавітного запиту діапазону (наприклад, "Jan Mon 01.01.2010", який знаходиться до "Jan Sun 01.01.1000"). Напевно, має сенс відформатувати всі дані про дату у форматі MongoDB, який, на мою думку, є просто звичайною датою JavaScript.
пончао

2
Я просто використовував це для перетворення моїх рядків на об’єкти дати stackoverflow.com/questions/2900674/…
Том

Гаразд круто! Я б здогадувався, що запити про діапазон, згадані в кулінарній книзі, повинні спрацювати, ви їх вже випробували?
пончао

Так, колись я зберігав дати, виправляйте приклади кулінарної книги, працюючи як слід.
Том

35

Для уточнення. Що важливо знати, це:

  • Так, вам потрібно передати об’єкт дати Javascript.
  • Так, це повинно бути ISODate дружнім
  • Так, з мого досвіду, коли це працює, потрібно маніпулювати датою ISO
  • Так, робота з фініками - це завжди копіткий процес, і монго не є винятком

Ось робочий фрагмент коду, де ми робимо невелику маніпуляцію датою, щоб забезпечити Монго (тут я використовую модуль мангуста і хочу отримати результати для рядків, атрибут дати яких менший, ніж (до цього), дата, подана як параметр myDate). це правильно:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})

1
Плюс один для наочності. Якщо ви використовуєте момент використання в резервному часі, він все одно зберігає функцію toISOString (). Я використовую момент для додавання та віднімання часу для моїх запитів.
VocoJax

17

MongoDB фактично зберігає мільйони дат як int (64), як це визначено в http://bsonspec.org/#/specification

Однак це може стати досить заплутаним, коли ви отримуєте дати, оскільки клієнтський драйвер інстанціюватиме об'єкт дати з власним локальним часовим поясом. Драйвер JavaScript на консолі mongo, безумовно, зробить це.

Отже, якщо ви дбаєте про свої часові пояси, то переконайтеся, що знаєте, що це повинно бути, коли ви повернете його назад. Це не має великого значення для запитів, оскільки воно все одно прирівнюватиметься до того ж int (64), незалежно від того, в якому часовому поясі знаходиться ваш об’єкт дати (я сподіваюся). Але я б напевно робив запити з фактичними об'єктами дати (а не з рядками) і дозволю водію робити це.


16

Пітон і pymongo

Пошук об'єктів між двома датами в Python з pymongoколекцією posts(на основі підручника ):

from_date = datetime.datetime(2010, 12, 31, 12, 30, 30, 125000)
to_date = datetime.datetime(2011, 12, 31, 12, 30, 30, 125000)

for post in posts.find({"date": {"$gte": from_date, "$lt": to_date}}):
    print(post)

Де {"$gte": from_date, "$lt": to_date}вказується діапазон у розрізі datetime.datetimeтипів.


Це не працює. Щоразу, коли я запускаю цей запит, я отримую повну відповідь за замовчуванням і не відфільтровану відповідь
Abhay Bh,

14
db.collection.find({"createdDate":{$gte:new ISODate("2017-04-14T23:59:59Z"),$lte:new ISODate("2017-04-15T23:59:59Z")}}).count();

Замініть collectionназвою колекції, якою ви хочете виконати запит


3
Що це додає до прийнятої відповіді (наданої на 7 років раніше)?
Дан Даскалеску

1
@DanDascalescu - може бути, це нічого не додало, окрім чого ти тут хвилюєшся?
GSK

17
Дублюючі відповіді витрачають час на людей.
Дан Даскалеску

10

Використовуйте цей код, щоб знайти запис між двома датами, використовуючи $gteта $lt:

db.CollectionName.find({"whenCreated": {
    '$gte': ISODate("2018-03-06T13:10:40.294Z"),
    '$lt': ISODate("2018-05-06T13:10:40.294Z")
}});

Що це додає до прийнятої відповіді, наданої на 8 років раніше?
Дан Даскалеску


2

Перетворіть ваші дати в часовий пояс GMT, коли ви їх заправляєте в Монго. Таким чином, ніколи не виникає проблем із часовим поясом. Тоді просто зробіть математику в полі щебета / часового поясу, коли витягнете дані назад для презентації.


2

Чому б не перетворити рядок у ціле число форми YYYYMMDDHHMMSS? Кожен приріст часу створював би більше ціле число, і ви можете фільтрувати цілі числа, а не турбуватися про перехід на ISO-час.


Тому що час відбувається не просто в моєму місцевому часовому поясі.
Майкл Коул

2
Це стає кошмаром після того, як вам потрібно скрізь перетворювати час на формат і з цього формату. Якщо ви збираєтесь зробити щось подібне як мінімум, використовуйте повернене значення .getTime () від об'єкта дати JS.
nikk wong

1
Ось чому ми завжди зберігаємо дані в UTC
квітня

2

використовуйте $ gte та $ lte для пошуку між датами в mongodb

var tomorrowDate = moment(new Date()).add(1, 'days').format("YYYY-MM-DD");
db.collection.find({"plannedDeliveryDate":{ $gte: new Date(tomorrowDate +"T00:00:00.000Z"),$lte: new Date(tomorrowDate + "T23:59:59.999Z")}})

1
Трохи друкуйте у своїй відповіді $ gte not $ get :)
Боб

1
Вибачте, я відповів дуже втомленим станом, тому я помилився. Дякую за допомогу, щоб оновити мою відповідь. ви зробили гарну роботу :) @Bob
KARTHIKEYAN.A

2

Ви також можете це перевірити. Якщо ви використовуєте цей метод, використовуйте функцію розбору, щоб отримати значення з бази даних Mongo:

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})

1
mongoose.model('ModelName').aggregate([
    {
        $match: {
            userId: mongoose.Types.ObjectId(userId)
        }
    },
    {
        $project: {
            dataList: {
              $filter: {
                 input: "$dataList",
                 as: "item",
                 cond: { 
                    $and: [
                        {
                            $gte: [ "$$item.dateTime", new Date(`2017-01-01T00:00:00.000Z`) ]
                        },
                        {
                            $lte: [ "$$item.dateTime", new Date(`2019-12-01T00:00:00.000Z`) ]
                        },
                    ]
                 }
              }
           }
        }
     }
])

1

Ви також можете перевірити це або спробувати замість того, щоб використовувати агрегат

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})

0

я намагався в цій моделі відповідно до моїх вимог, мені потрібно зберігати дату, коли коли-небудь об’єкт буде створений пізніше, я хочу отримати всі записи (документи) між двома датами в моєму html-файлі, я використовував наступний формат mm / dd / yyyy

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>
<head>

    <script>
//jquery
    $(document).ready(function(){  
    $("#select_date").click(function() { 
    $.ajax({
    type: "post",
    url: "xxx", 
    datatype: "html",
    data: $("#period").serialize(),  
    success: function(data){
    alert(data);
    } ,//success

    }); //event triggered

    });//ajax
    });//jquery  
    </script>

    <title></title>
</head>

<body>
    <form id="period" name='period'>
        from <input id="selecteddate" name="selecteddate1" type="text"> to 
        <input id="select_date" type="button" value="selected">
    </form>
</body>
</html>

у моєму файлі py (python) я перетворив його на "iso fomate" наступним чином

date_str1   = request.POST["SelectedDate1"] 
SelectedDate1   = datetime.datetime.strptime(date_str1, '%m/%d/%Y').isoformat()

і збережено в моїй колекції dbmongo з "SelectedDate" як поле в моїй колекції

для отримання даних або документів між двома датами, я використовував наступний запит

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.