Найти объекты между двумя датами 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
}

Как мне написать запрос, который проверяет созданный_кат и находит все объекты с 18:47 до 19:00? Нужно ли обновлять документы, чтобы даты сохранялись в определенном формате?

Том
источник
Вы не говорите, по какому полю вы хотите запросить?
Шингара
К сожалению, я хочу сделать запрос к create_at и найти все между двумя датами.
Том
Мне любопытно, почему бы не использовать метку времени, какие-либо преимущества с помощью Date Obj?
Лев
4
@Leo Самое большое преимущество объекта Date над миллисекундами с той эпохи, или что-то еще, что делает человека читабельным. В этом случае установить начальный диапазон 2010-04-29T00:00:00.000Zнамного проще, чем вычислять ту же дату / время в миллисекундах. Вы также можете легко конвертировать часовой пояс. Кроме того, Dates уже обрабатывает такие вещи, как високосные дни, високосные секунды и другие странности, с которыми вы обычно не хотите справляться сами.
Thunderforge

Ответы:

620

Запрос на диапазон дат (определенный месяц или день) в Кулинарной книге 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" }

Во втором примере никаких результатов не ожидалось, но был еще один. Это потому, что основное сравнение строк сделано.

ponzao
источник
3
Выглядит интересно, но должна ли сохраненная дата быть в определенном формате. Я только что сохранил то, что было предоставлено Твиттером, нужно ли это изменить в другой формат?
Том
7
Вы, вероятно, сохранили метки времени в виде строк, поэтому я предполагаю, что MongoDB не поймет, что это на самом деле даты. Таким образом, выполнение запроса диапазона к ним приведет к алфавитному запросу диапазона (например, «Jan Mon 01.01.2010» перед «Jan Sun 01.01.1000»). Вероятно, имеет смысл отформатировать все данные даты в формате MongoDB, который, как мне кажется, является просто датой JavaScript.
Понзао
2
Я просто использовал это, чтобы преобразовать мои строки в объекты даты stackoverflow.com/questions/2900674/…
Том
Хорошо! Я предполагаю, что запросы диапазона, упомянутые в кулинарной книге, должны работать тогда, вы уже пробовали их?
Понзао
Да, как только я сохранял даты правильно, примеры поваренной книги работали как ожидалось.
Том
35

Чтобы уточнить. Важно знать, что:

  • Да, вы должны передать объект Javascript Date.
  • Да, это должно быть ISODate дружественным
  • Да, из моего опыта заставить это работать, вам нужно манипулировать датой в ISO
  • Да, работа с датами, как правило, всегда утомительный процесс, и Монго не является исключением

Вот рабочий фрагмент кода, где мы делаем небольшую манипуляцию датами, чтобы гарантировать, что Mongo (здесь я использую модуль mongoose и хочу получить результаты для строк, чей атрибут date меньше (до) даты, указанной в параметре myDate), может обрабатывать это правильно:

var inputDate = new Date(myDate.toISOString());
MyModel.find({
    'date': { $lte: inputDate }
})
arcseldon
источник
1
Плюс один для ясности. Если вы используете момент в бэкэнде, он все еще сохраняет функцию toISOString (). Я использую момент, чтобы сложить и вычесть время для моих запросов.
VocoJax
17

MongoDB на самом деле хранит миллис даты как int (64), как предписано http://bsonspec.org/#/specification

Однако это может привести к путанице при получении дат, поскольку драйвер клиента будет создавать экземпляр объекта даты со своим собственным часовым поясом. Драйвер JavaScript в консоли Монго, безусловно, сделает это.

Так что, если вы заботитесь о своих часовых поясах, убедитесь, что вы знаете, что это должно быть, когда вы получите его обратно. Это не должно иметь большого значения для запросов, так как оно все равно будет равняться тому же самому int (64), независимо от того, в каком часовом поясе находится ваш объект даты (я надеюсь). Но я бы определенно делал запросы с фактическими объектами даты (не строками) и позволял бы драйверу делать свое дело.

Бен Смит
источник
16

Python и 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на название коллекции, которую вы хотите выполнить запрос

GSK
источник
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 годами ранее?
Дан Даскалеску
7

Использование с Moment.js и операторами сравнения запросов

  var today = moment().startOf('day');
  // "2018-12-05T00:00:00.00
  var tomorrow = moment(today).endOf('day');
  // ("2018-12-05T23:59:59.999

  Example.find(
  {
    // find in today
    created: { '$gte': today, '$lte': tomorrow }
    // Or greater than 5 days
    // created: { $lt: moment().add(-5, 'days') },
  }), function (err, docs) { ... });
Тинь Нго Куанг
источник
2

Конвертируйте ваши даты в часовой пояс GMT, когда вы вводите их в Mongo. Таким образом, никогда не бывает проблем с часовыми поясами. Тогда просто сделайте математику в поле твиттера / часового пояса, когда вы извлекаете данные обратно для представления.

heregear
источник
2

Почему бы не преобразовать строку в целое число вида ГГГГММДДЧЧММСС? Затем каждый прирост времени будет создавать большее целое число, и вы сможете фильтровать целые числа, не беспокоясь о преобразовании во время ISO.

ZacharyST
источник
Потому что время не происходит в моем местном часовом поясе.
Майкл Коул
2
Это становится кошмаром, когда вы начинаете преобразовывать время в и из этого формата везде. Если вы собираетесь сделать что-то подобное по крайней мере, используйте возвращаемое значение из .getTime () из объекта даты JS.
Никк Вонг
1
Вот почему мы всегда храним данные в UTC
codewandler
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")}})
KARTHIKEYAN.A
источник
1
Небольшая опечатка в вашем ответе $ gte not $ get :)
Bob
1
Извините, я ответил на очень усталое состояние, поэтому я ошибся. Спасибо за вашу помощь, чтобы обновить мой ответ. Вы хорошо поработали :) @Bob
KARTHIKEYAN.A
2

Вы также можете проверить это. Если вы используете этот метод, используйте функцию parse для получения значений из базы данных Mongo:

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})
Kevin007
источник
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`) ]
                        },
                    ]
                 }
              }
           }
        }
     }
])
Jitendra
источник
1

Вы также можете проверить это или попробовать вместо использования агрегата

db.getCollection('user').find({
    createdOn: {
        $gt: ISODate("2020-01-01T00:00:00.000Z"),
        $lt: ISODate("2020-03-01T00:00:00.000Z")
    }
})
Kevin007
источник
0

я попытался в этой модели в соответствии с моими требованиями мне нужно сохранить дату, когда когда-либо объект создается позже, я хочу получить все записи (документы) между двумя датами в моем HTML-файле, я использовал следующий формат мм / дд / гггг

<!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}})
аю для тебя
источник