Tìm các đối tượng giữa hai ngày MongoDB


406

Tôi đã chơi xung quanh việc lưu trữ các tweet bên trong mongodb, mỗi đối tượng trông như thế này:

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

Làm thế nào tôi có thể viết một truy vấn kiểm tra created_at và tìm thấy tất cả các đối tượng trong khoảng thời gian từ 18 giờ 47 đến 19:00? Tôi có cần cập nhật tài liệu của mình để ngày được lưu trữ ở một định dạng cụ thể không?


Bạn không nói về lĩnh vực nào bạn muốn truy vấn?
shingara

Rất tiếc, tôi muốn truy vấn created_at và tìm tất cả giữa hai ngày.
Tom

Tôi tò mò rằng tại sao không sử dụng dấu thời gian, bất kỳ lợi thế nào bằng cách sử dụng Date Obj?
Leo

4
@Leo Ưu điểm lớn nhất với đối tượng Date trong một phần nghìn giây kể từ kỷ nguyên hoặc bất cứ điều gì là khả năng đọc của con người. Trong trường hợp này, việc đặt phạm vi bắt đầu của bạn 2010-04-29T00:00:00.000Zdễ dàng hơn nhiều so với việc tính toán cùng ngày / giờ tính bằng mili giây. Bạn cũng có thể thực hiện chuyển đổi múi giờ khá dễ dàng. Ngoài ra, Ngày đã xử lý những việc như ngày nhuận, giây nhuận và những điều kỳ lạ khác mà bạn thường không muốn tự xử lý.
Thunderforge

Câu trả lời:


619

Truy vấn về Phạm vi Ngày (Tháng hoặc Ngày cụ thể) trong Sách dạy nấu ăn MongoDB có một lời giải thích rất hay về vấn đề này, nhưng dưới đây là điều tôi đã tự mình thử và nó có vẻ hiệu quả.

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

Dựa trên các thử nghiệm của tôi, bạn sẽ cần phải tuần tự hóa các ngày của mình thành định dạng mà MongoDB hỗ trợ, vì những điều sau đây đã cho kết quả tìm kiếm không mong muốn.

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

Trong ví dụ thứ hai, không có kết quả nào được mong đợi, nhưng vẫn còn một kết quả. Điều này là do một so sánh chuỗi cơ bản được thực hiện.


3
Có vẻ thú vị, nhưng ngày lưu trữ cần phải ở một định dạng cụ thể. Tôi vừa mới lưu trữ những gì được cung cấp bởi twitter, điều này có cần phải thay đổi thành một định dạng khác không?
Tom

7
Có lẽ bạn đã lưu trữ dấu thời gian dưới dạng chuỗi, vì vậy tôi đoán MongoDB sẽ không nhận ra rằng chúng thực sự là ngày. Do đó, thực hiện một truy vấn phạm vi trên chúng sẽ dẫn đến một truy vấn phạm vi theo thứ tự chữ cái (ví dụ: "Jan Mon 01.01.2010" trước "Jan Sun 01.01.1000"). Có lẽ sẽ hợp lý khi định dạng tất cả dữ liệu ngày thành định dạng MongoDB, mà tôi nghĩ chỉ là Ngày JavaScript đơn giản.
ponzao

2
Tôi chỉ sử dụng điều này để chuyển đổi chuỗi của mình thành các đối tượng ngày stackoverflow.com/questions/2900674/
Tom

Được, tuyệt đấy! Tôi đoán các truy vấn phạm vi được đề cập trong sách dạy nấu ăn sẽ hoạt động sau đó, bạn đã thử chúng chưa?
ponzao

Đúng khi tôi đang lưu trữ ngày chính xác, các ví dụ về sách dạy nấu ăn hoạt động như mong đợi.
Tom

35

Làm rõ. Điều quan trọng cần biết là:

  • Có, bạn phải vượt qua một đối tượng Ngày Javascript.
  • Vâng, nó phải thân thiện với ISODate
  • Có, từ kinh nghiệm của tôi để làm việc này, bạn cần thao tác ngày thành ISO
  • Đúng vậy, làm việc với ngày thường luôn là một quá trình tẻ nhạt và mongo cũng không ngoại lệ

Đây là một đoạn mã hoạt động, trong đó chúng tôi thực hiện một chút thao tác ngày để đảm bảo Mongo (ở đây tôi đang sử dụng mô đun mongoose và muốn kết quả cho các hàng có thuộc tính ngày nhỏ hơn (trước) ngày được cung cấp như myDate param) nó chính xác:

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

1
Thêm một cho rõ ràng. Nếu thời điểm sử dụng của bạn trong phần phụ trợ, nó vẫn giữ chức năng toISOString (). Tôi sử dụng thời điểm để thêm và trừ thời gian cho các truy vấn của mình.
VocoJax

17

MongoDB thực sự lưu trữ một phần nghìn của một ngày dưới dạng int (64), theo quy định của http://bsonspec.org/#/specification

Tuy nhiên, nó có thể trở nên khá khó hiểu khi bạn truy xuất ngày vì trình điều khiển máy khách sẽ khởi tạo một đối tượng ngày với múi giờ địa phương của chính nó. Trình điều khiển JavaScript trong bảng điều khiển mongo chắc chắn sẽ làm điều này.

Vì vậy, nếu bạn quan tâm đến múi giờ của mình, thì hãy chắc chắn rằng bạn biết nó là gì khi bạn lấy lại. Điều này không quan trọng lắm đối với các truy vấn, vì nó vẫn sẽ tương đương với cùng một int (64), bất kể đối tượng ngày của bạn nằm ở múi giờ nào (tôi hy vọng). Nhưng tôi chắc chắn thực hiện truy vấn với các đối tượng ngày thực tế (không phải chuỗi) và để trình điều khiển thực hiện việc đó.


16

Python và pymongo

Tìm các đối tượng giữa hai ngày trong Python với pymongotrong bộ sưu tập posts(dựa trên hướng dẫn ):

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)

Trong đó {"$gte": from_date, "$lt": to_date}chỉ định phạm vi về các datetime.datetimeloại.


Đây không phải là làm việc. Bất cứ khi nào tôi chạy truy vấn này, tôi nhận được phản hồi hoàn toàn theo mặc định và phản hồi không được lọc
Abhay Bh

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

Thay thế collectionbằng tên của bộ sưu tập bạn muốn thực hiện truy vấn


3
Điều này thêm gì vào câu trả lời được chấp nhận (cung cấp 7 năm trước)?
Dan Dascalescu

1
@DanDascalescu - có thể không thêm gì nhưng mối quan tâm của bạn ở đây là gì?
GSK

17
Câu trả lời trùng lặp lãng phí thời gian của mọi người.
Dan Dascalescu

10

Sử dụng mã này để tìm bản ghi giữa hai ngày bằng $gte$lt:

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

Điều này thêm gì vào câu trả lời được chấp nhận, được cung cấp 8 năm trước?
Dan Dascalescu

7

Sử dụng với Moment.jsToán tử truy vấn so sánh

  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

Chuyển đổi ngày của bạn thành múi giờ GMT khi bạn nhồi chúng vào Mongo. Theo cách đó, không bao giờ có vấn đề múi giờ. Sau đó, chỉ cần làm toán trên trường twitter / múi giờ khi bạn kéo dữ liệu ra để trình bày.


2

Tại sao không chuyển đổi chuỗi thành một số nguyên có dạng YYYYMMDDHHMMSS? Mỗi lần tăng thời gian sau đó sẽ tạo ra một số nguyên lớn hơn và bạn có thể lọc trên các số nguyên thay vì lo lắng về việc chuyển đổi sang thời gian ISO.


Bởi vì thời gian không chỉ xảy ra trong múi giờ địa phương của tôi.
Michael Cole

2
Điều này trở thành một cơn ác mộng khi bạn bắt đầu phải chuyển đổi thời gian sang và từ định dạng đó ở mọi nơi. Nếu bạn sẽ làm một cái gì đó như thế này thì ít nhất hãy sử dụng giá trị trả về từ .getTime () từ một đối tượng ngày JS.
nikk wong

1
đó là lý do tại sao chúng tôi luôn lưu trữ dữ liệu trong UTC
codewandler

2

sử dụng $ gte$ lte để tìm giữa dữ liệu ngày trong 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
Lỗi đánh máy nhẹ trong câu trả lời của bạn $ gte không phải $ get :)
Bob

1
Xin lỗi tôi đã trả lời tình trạng rất mệt mỏi, vì vậy tôi đã phạm sai lầm. Cảm ơn sự giúp đỡ của bạn để cập nhật câu trả lời của tôi. bạn đã làm rất tốt :)
@Bob

2

Bạn cũng có thể kiểm tra điều này. Nếu bạn đang sử dụng phương thức này, thì hãy sử dụng hàm phân tích để nhận các giá trị từ Cơ sở dữ liệu 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

Bạn cũng có thể kiểm tra điều này hoặc thử thay vì sử dụng tổng hợp

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

0

Tôi đã thử trong mô hình này theo yêu cầu của mình, tôi cần lưu trữ một ngày khi có một đối tượng được tạo sau này tôi muốn lấy tất cả các bản ghi (tài liệu) giữa hai ngày trong tệp html của mình, tôi đã sử dụng định dạng sau 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>

trong tập tin py (python) của tôi, tôi đã chuyển đổi nó thành "iso fomate" theo cách sau

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

và được lưu trong bộ sưu tập dbmongo của tôi với "Chọn ngày" làm trường trong bộ sưu tập của tôi

để lấy dữ liệu hoặc tài liệu trong khoảng từ 2 ngày tôi đã sử dụng truy vấn sau

db.collection.find( "SelectedDate": {'$gte': SelectedDate1,'$lt': SelectedDate2}})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.