11

MongoDB provides a way to update a date field by the system on update operations: https://docs.mongodb.com/manual/reference/operator/update/currentDate/. Is there any equivalent to this for insert operations?

russoue
  • 5,180
  • 5
  • 27
  • 29

4 Answers4

11

You may try to do a few things if you do not want to handle this from code (I have executed the code below directly on mongo shell):

  1. If you want to use $currentDate use update with upsert = true:

    db.orders.update(
       {"_id":ObjectId()},
       {
           $currentDate: {
             createtime: true
           }
       },
       { upsert: true }
    )
    

It will now generate the objectid on app server instead of date/time (unless you use raw command).

  1. Use new timestamp or date object directly:

    db.orders.insert(
        "createtime": new Timestamp()
    )
    

The problem with most driver will be then to make sure the new object is created on mondodb server- not on the machine where the code is running. You driver hopefully allows to run raw insert command.

Both will serve the purpose of avoiding time differences/ time sync issue between application server machines.

  • Thanks. I think the first one is the better approach although I would like to explicitly avoid creating the `_id` myself. I agree with your concern on the second one that depending on the driver the date/timestamp may be populated on the client side or the server side. – russoue Jun 13 '16 at 17:01
  • Hey @russoue, check https://stackoverflow.com/a/37061284/2911851 for a way of having the server create the `_id` itself. – Gian Franco Zabarino Jun 26 '19 at 17:30
1

The $currentDate is an update operator which populates date field with current date through update operation.

To auto populate date field while insertion of new MongoDB document,please try executing following code snippet

var current_date=new Date();
db.collection.insert({datefield:current_date})

In above code snippet the statement

new Date()

creates a new JavaScript Date object which consists of a year, a month, a day, an hour, a minute, a second, and milliseconds

Rubin Porwal
  • 3,736
  • 1
  • 23
  • 26
  • 2
    My concern with this approach is I don't want to populate this field in the client side, e.g. for a `created_at` type field, as I want the exact time the record was created on the server side. The record creation may be delayed or queued or retried for transient issues and if the field is populated on the client side then the value will not actually reflect when it was created on the server side. – russoue Jun 13 '16 at 16:59
1

If you want to populate this value when running it in the server side, and are concerned about it being passed by the client, you can add properties to the data object that is being used in the insert statement only when it will be saved. This way, you can guarantee that it will be added every time with the server's date, not the client's:

Client side

...
let data = { info1: 'value1', info2: 'value2'}
someApi.addInfo(data);
...

Server side

function addInfo(data){
    ...
    data['creationDate'] = new Date();
    db.collection.insertOne(data);
    ...
}

Result will be:

{
    info1: 'value1', 
    info2: 'value2',
    creationDate: ISODate("2018-09-15T21:42:13.815Z")
}

If you are passing multiple values for insertion (using insertMany) you have to loop over the items and add this property for all of them.

You can also use this approach when updating documents if for some reason you can't use the $currentDate operator, just be sure you are not replacing any existing properties in the data passed to mongodb.

c-chavez
  • 7,237
  • 5
  • 35
  • 49
0

Since mongo 3.6 you can use 'change stream': https://emptysqua.re/blog/driver-features-for-mongodb-3-6/#change-streams

to use it you need to create a change stream object by the 'watch' query:

def update_ts_by(change):
    update_fields = change["updateDescription"]["updatedFields"].keys()
    print("update_fields: {}".format(update_fields))

    collection = change["ns"]["coll"]
    db = change["ns"]["db"]
    key = change["documentKey"]

    if len(update_fields) == 1 and "update_ts" in update_fields:
        pass
    else:
        client[db][collection].update(key, {"$set": {"update_ts": datetime.now()}})


client = MongoClient("172.17.0.2")
db = client["Data"]

change_stream = db.watch()

for change in change_stream:
    print(change)
    update_ts_by(change)

Note, to use the change_stream object, your mongodb instance should run as 'replica set'. It can be done also as a 1-node replica set (almost no change then the standalone use): Mongo DB - difference between standalone & 1-node replica set

Rea Haas
  • 2,018
  • 1
  • 16
  • 18