将 created_at 和 updated_at 字段添加到猫鼬模式

Posted

技术标签:

【中文标题】将 created_at 和 updated_at 字段添加到猫鼬模式【英文标题】:add created_at and updated_at fields to mongoose schemas 【发布时间】:2019-10-16 17:45:20 【问题描述】:

有没有办法将 created_at 和 updated_at 字段添加到猫鼬模式中,而不必在每次调用新的 MyModel() 时都传递它们?

created_at 字段将是一个日期,并且仅在创建文档时添加。 每当在文档上调用 save() 时,updated_at 字段就会更新为新日期。

我已经在我的架构中尝试过这个,但是除非我明确添加它,否则该字段不会显示:

var ItemSchema = new Schema(
    name    :  type: String, required: true, trim: true ,
    created_at    :  type: Date, required: true, default: Date.now 
);

【问题讨论】:

我完全按照你的做法做了,而且效果很好。使用猫鼬 4.8.4。可能是新事物? 这是很久以前的了。 是的,值得一提的是上面的方法现在可以工作了。 如果我已经有一些数据如何更新它? 【参考方案1】:

更新:(5 年后)

注意:如果您决定使用Kappa Architecture(Event Sourcing + CQRS),那么您根本不需要更新日期。由于您的数据是不可变的、仅追加的事件日志,因此您只需要事件创建日期。类似于 Lambda 架构,如下所述。那么您的应用程序状态是事件日志(派生数据)的投影。如果您收到有关现有实体的后续事件,那么您将使用该事件的创建日期作为您的实体的更新日期。这是miceroservice系统中常用的(也经常被误解)的做法。

更新:(4 年后)

如果您使用ObjectId 作为您的_id 字段(通常是这种情况),那么您需要做的就是:

let document = 
  updatedAt: new Date(),

在下面查看我的原始答案,了解如何从 _id 字段获取创建的时间戳。 如果您需要使用来自外部系统的 ID,请查看 Roman Rhrn Nesterov 的答案。

更新:(2.5 年后)

您现在可以在 mongoose 版本 >= 4.0 中使用 #timestamps 选项。

let ItemSchema = new Schema(
  name:  type: String, required: true, trim: true 
,

  timestamps: true
);

如果设置时间戳,mongoose 会将 createdAtupdatedAt 字段分配给您的架构,分配的类型是 Date

您还可以指定时间戳文件的名称:

timestamps:  createdAt: 'created_at', updatedAt: 'updated_at' 

注意:如果您正在处理具有关键数据的大型应用程序,您应该重新考虑更新您的文档。我建议您使用不可变的、仅附加的数据 (lambda architecture)。这意味着什么 你只允许插入。 更新和删除不应该 允许! 如果您想“删除”一条记录,您可以轻松地 插入带有一些 timestamp/version 的新版本文档 归档,然后将deleted 字段设置为true。同样,如果你想 更新文档 - 您使用适当的创建新文档 字段已更新,其余字段已复制。然后为了 查询此文档,您将获得具有最新时间戳的文档或 未被“删除”的最高版本(deleted 字段未定义或为假)。

数据不变性确保您的数据是可调试的——您可以跟踪 每个文件的历史。您也可以回滚到上一个 如果出现问题,文档的版本。如果你带着这样的 建筑ObjectId.getTimestamp() 是你所需要的,但它不是 依赖猫鼬。


原始答案:

如果您使用 ObjectId 作为身份字段,则不需要 created_at 字段。 ObjectIds 有一个名为getTimestamp() 的方法。

ObjectId("507c7f79bcf86cd7994f6c0e").getTimestamp()

这将返回以下输出:

ISODate("2012-10-15T21:26:17Z")

更多信息在这里How do I extract the created date out of a Mongo ObjectID

要添加updated_at 字段,您需要使用:

var ArticleSchema = new Schema(
  updated_at:  type: Date 
  // rest of the fields go here
);

ArticleSchema.pre('save', function(next) 
  this.updated_at = Date.now();
  next();
);

【讨论】:

你如何在 Mongoose/node.js 中做到这一点? 但是如果我想将创建日期传递给视图,我需要为此设置特殊变量,或者传递 id 对吗? 这是非常有价值的信息!但是我有一个担心,使用这种方法(不可变数据),数据库会很快变大!尤其是在更新频繁的应用程序中! 我很欣赏最初的回答,但提倡使用 lambda 架构听起来也是解决 5 倍级别元问题的好方法,而且可能永远不会交付,而不是构建 CRUD 应用程序并保持简单,除非这种做法确实是有道理的。请记住,您发布的关于 MongoDB 的信息从一开始就“几乎”向外扩展,并且您主张为每次写入创建一个新记录,这将迅速以任何有意义的规模杀死 Mongo。将此移至 Cassandra 部分... 我实际上更改了我的后端设计,以便执行软删除来代替真正的删除,这要归功于此答案中提供的信息。它提供了一些真正有用的更深入的知识。【参考方案2】:

从 Mongoose 4.0 开始,您现在可以在 Schema 上设置时间戳选项,让 Mongoose 为您处理:

var thingSchema = new Schema(..,  timestamps: true );

您可以像这样更改使用的字段的名称:

var thingSchema = new Schema(..,  timestamps:  createdAt: 'created_at'  );

http://mongoosejs.com/docs/guide.html#timestamps

【讨论】:

同意,这是 Mongoose 4.0 中的最佳选择。 在 react-admin 列表中,我是否可以通过 prop createdAtcreated_attimestamps.createdAttimestamps.created_at 找到这个? 我可以更改时间戳 createdAt 和 updatedAt 格式就像 (YYYY-MM-DD HH:mm:ss.)【参考方案3】:

这就是我最终做的:

var ItemSchema = new Schema(
    name    :  type: String, required: true, trim: true 
  , created_at    :  type: Date 
  , updated_at    :  type: Date 
);


ItemSchema.pre('save', function(next)
  now = new Date();
  this.updated_at = now;
  if ( !this.created_at ) 
    this.created_at = now;
  
  next();
);

【讨论】:

1.将当前时间存储在本地 var 中并分配它,而不是每次调用 new Date(),这将确保首先通过 created_at 和 updated_at 具有相同的 excect 值。 2. 新日期 => 新日期() 只想指出,如果您使用 ObjectId,那么您可以从那里获取 created_at ....您不需要单独的字段。查看getTimestamp() created_at 的其他选项可能是将模型更改为 -> created_at: type: Date, default: Date.now , @ajbraus 制作一个模式插件 尽可能使用Date.now(),而不是new Date,因为它是静态方法,所以速度更快【参考方案4】:

为您的架构使用内置的timestamps 选项。

var ItemSchema = new Schema(
    name:  type: String, required: true, trim: true 
,

    timestamps: true
);

这会自动将createdAtupdatedAt 字段添加到您的架构中。

http://mongoosejs.com/docs/guide.html#timestamps

【讨论】:

需要猫鼬版本>= 4.0 我发现文档不清楚 - 这会添加字段,但是否也确保它们在每次更新时都得到更新? 一般来说,createdAt 总是只存储一次……当对象被创建时。 updatedAt 在每次新保存时更新(当 obj 更改时) 我得到了这个“2016-12-07T11:46:46.066Z”.. 请解释一下时间戳格式,如何更改时区?是采用 mongoDB 服务器时区吗?? @mcompeau,谢谢你的回答!我知道这是一个很长的尝试,但是您是否还记得以这种方式创建的字段是否可以用作索引?【参考方案5】:

像这样将timestamps 添加到您的Schema 然后createdAtupdatedAt 将自动为您生成

var UserSchema = new Schema(
    email: String,
    views:  type: Number, default: 0 ,
    status: Boolean
,  timestamps:  );

您也可以通过

更改createdAt -> created_at
timestamps:  createdAt: 'created_at', updatedAt: 'updated_at' 

【讨论】:

我可以更改时间戳 createdAt 和 updatedAt 格式就像 (YYYY-MM-DD HH:mm:ss)【参考方案6】:

如果使用update()findOneAndUpdate()

带有upsert: true 选项

你可以使用$setOnInsert

var update = 
  updatedAt: new Date(),
  $setOnInsert: 
    createdAt: new Date()
  
;

【讨论】:

如果您使用 Mogo 的默认 _id 字段,则不需要 createdAt 字段。请查看下面的答案以获取更多详细信息。 ...检查我的原始答案【参考方案7】:

这就是我创建和更新的方式。

在我的架构中,我添加了这样创建和更新的内容:

/** * 文章架构 */ var ArticleSchema = new Schema( 创建: 类型:日期, 默认值:Date.now , 更新: 类型:日期, 默认值:Date.now , 标题: 类型:字符串, 默认: '', 修剪:真的, 必填:'标题不能为空' , 内容: 类型:字符串, 默认: '', 修剪:真 , 用户: 类型:Schema.ObjectId, 参考:'用户' );

然后在我的文章控制器里面的文章更新方法中我添加了:

/** * 更新一篇文章 */ 出口。更新=功能(请求,水库) var 文章 = req.article; 文章 = _.extend(文章, req.body); article.set("更新", Date.now()); 文章.保存(功能(错误) 如果(错误) 返回 res.status(400).send( 消息:errorHandler.getErrorMessage(err) ); 别的 res.json(文章); ); ;

粗体部分是感兴趣的部分。

【讨论】:

【参考方案8】:
var ItemSchema = new Schema(
    name :  type: String, required: true, trim: true 
);

ItemSchema.set('timestamps', true); // this will add createdAt and updatedAt timestamps

文档:https://mongoosejs.com/docs/guide.html#timestamps

【讨论】:

如何在 createdAt 字段上添加索引? 如何更改时间戳 createdAt 和 updatedAt 格式就像 (YYYY-MM-DD HH:mm:ss)【参考方案9】:

在您的模型架构中,只需添加一个属性timestamps 并为其分配值true,如下所示:-

var ItemSchema = new Schema(
   name :   type: String, required: true, trim: true ,
,timestamps : true
);

【讨论】:

这会做什么? 时间戳属性将在每个文档中添加 created_at 和 updated_at 字段。 Created_at 字段表示文档的创建时间,updated_at 字段表示更新时间(如果有的话)是文档的创建时间。这两个字段的值都是 ISO 时间格式。希望这能消除您的疑虑 Chovy。 我可以更改时间戳 createdAt 和 updatedAt 格式就像 (YYYY-MM-DD HH:mm:ss) 我会说这种格式使用时刻。否则,您可以添加 createdAt 之类的属性:new Date()【参考方案10】:

对于带有 Mongoose 的 NestJ,使用这个

@Schema(timestamps: true)

【讨论】:

谢谢,我正在找这个。【参考方案11】:

您可以使用mongoose-trooptimestamp 插件将此行为添加到任何架构。

【讨论】:

【参考方案12】:

在你的model

const User = Schema(
  
    firstName:  type: String, required: true ,
    lastName:  type: String, required: true ,
    password:  type: String, required: true 
  ,
  
    timestamps: true
  
);

之后,collection 中的 model 将是这样的:


    "_id" : ObjectId("5fca632621100c230ce1fb4b"),
    "firstName" : "first",
    "lastName" : "last",
    "password" : "$2a$15$Btns/B28lYIlSIcgEKl9eOjxOnRjJdTaU6U2vP8jrn3DOAyvT.6xm",
    "createdAt" : ISODate("2020-12-04T16:26:14.585Z"),
    "updatedAt" : ISODate("2020-12-04T16:26:14.585Z"),

【讨论】:

这帮助我解决了我的挑战。【参考方案13】:

您可以非常轻松地使用this plugin。 来自文档:

var timestamps = require('mongoose-timestamp');
var UserSchema = new Schema(
    username: String
);
UserSchema.plugin(timestamps);
mongoose.model('User', UserSchema);
var User = mongoose.model('User', UserSchema)

如果您愿意,还可以设置字段的名称:

mongoose.plugin(timestamps,  
  createdAt: 'created_at', 
  updatedAt: 'updated_at'
);

【讨论】:

【参考方案14】:

我们也可以通过使用 schema 插件 来实现。

helpers/schemaPlugin.js 文件中

module.exports = function(schema) 

  var updateDate = function(next)
    var self = this;
    self.updated_at = new Date();
    if ( !self.created_at ) 
      self.created_at = now;
    
    next()
  ;
  // update date for bellow 4 methods
  schema.pre('save', updateDate)
    .pre('update', updateDate)
    .pre('findOneAndUpdate', updateDate)
    .pre('findByIdAndUpdate', updateDate);
;

models/ItemSchema.js 文件中:

var mongoose = require('mongoose'),
  Schema   = mongoose.Schema,
  SchemaPlugin = require('../helpers/schemaPlugin');

var ItemSchema = new Schema(
  name    :  type: String, required: true, trim: true ,
  created_at    :  type: Date ,
  updated_at    :  type: Date 
);
ItemSchema.plugin(SchemaPlugin);
module.exports = mongoose.model('Item', ItemSchema);

【讨论】:

【参考方案15】:
const mongoose = require('mongoose');
const config = require('config');
const util = require('util');

const Schema = mongoose.Schema;
const BaseSchema = function(obj, options) 
  if (typeof(options) == 'undefined') 
    options = ;
  
  if (typeof(options['timestamps']) == 'undefined') 
    options['timestamps'] = true;
  

  Schema.apply(this, [obj, options]);
;
util.inherits(BaseSchema, Schema);

var testSchema = new BaseSchema(
  jsonObject:  type: Object 
  , stringVar :  type: String 
);

现在你可以使用这个了,这样就不需要在每个表中都包含这个选项了

【讨论】:

【参考方案16】:

我的猫鼬版本是 4.10.2

似乎只有钩子 findOneAndUpdate 有效

ModelSchema.pre('findOneAndUpdate', function(next) 
  // console.log('pre findOneAndUpdate ....')
  this.update(, $set:  updatedAt: new Date()  );
  next()
)

【讨论】:

【参考方案17】:

从 mongo 3.6 开始,您可以使用“更改流”: https://emptysqua.re/blog/driver-features-for-mongodb-3-6/#change-streams

要使用它,您需要通过“监视”查询创建一个更改流对象,并且对于每个更改,您可以做任何您想做的事情...

python解决方案:

def update_at_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_at" in update_fields:
        pass  # to avoid recursion updates...
    else:
        client[db][collection].update(key, "$set": "update_at": 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)

注意,要使用 change_stream 对象,您的 mongodb 实例应作为“副本集”运行。 它也可以作为一个 1 节点副本集来完成(几乎没有变化然后独立使用):

将 mongo 作为副本集运行: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/

副本集配置与独立: Mongo DB - difference between standalone & 1-node replica set

【讨论】:

【参考方案18】:

使用函数返回计算的默认值:

var ItemSchema = new Schema(
    name: 
      type: String,
      required: true,
      trim: true
    ,
    created_at: 
      type: Date,
      default: function()
        return Date.now();
      
    ,
    updated_at: 
      type: Date,
      default: function()
        return Date.now();
      
    
);

ItemSchema.pre('save', function(done) 
  this.updated_at = Date.now();
  done();
);

【讨论】:

无需将Date.now() 包装在函数中,只需:...default: Date.now() 我将它包装在一个函数中,这样我就可以在测试中模拟“.now()”。否则它只会在初始化时运行一次,并且值不能轻易更改。 请注意default: Date.now() 是错误的。如果有的话,那就是default: Date.now。否则,您的所有文档都将具有相同的时间戳:您的应用程序开始的时间;) 我喜欢你的default: Date.now 策略。干净得多。【参考方案19】:

我实际上是在后面这样做的

如果更新一切顺利:

 // All ifs passed successfully. Moving on the Model.save
    Model.lastUpdated = Date.now(); // <------ Now!
    Model.save(function (err, result) 
      if (err) 
        return res.status(500).json(
          title: 'An error occured',
          error: err
        );
      
      res.status(200).json(
        message: 'Model Updated',
        obj: result
      );
    );

【讨论】:

【参考方案20】:

使用 machinepack-datetime 格式化日期时间。

tutorialSchema.virtual('createdOn').get(function () 
    const DateTime = require('machinepack-datetime');
    let timeAgoString = "";
    try 
        timeAgoString = DateTime.timeFrom(
            toWhen: DateTime.parse(
                datetime: this.createdAt
            ).execSync(),
            fromWhen: new Date().getTime()
        ).execSync();
     catch(err) 
        console.log('error getting createdon', err);
    
    return timeAgoString; // a second ago
);

Machine pack 非常棒,API 清晰,不像 express 或一般的 javascript 世界。

【讨论】:

【参考方案21】:

如果您使用的是 nestjs 和 @Schema 装饰器,您可以这样实现:

@Schema(
  timestamps: true,
)

timestamps 选项告诉 mongoose 将 createdAt 和 updatedAt 字段分配给您的架构。分配的类型是日期。

默认情况下,字段的名称是 createdAt 和 updatedAt。

通过设置 timestamps.createdAt 和 timestamps.updatedAt 自定义字段名称。

【讨论】:

【参考方案22】:

您可以使用middleware 和virtuals。这是您的 updated_at 字段的示例:

ItemSchema.virtual('name').set(function (name) 
  this.updated_at = Date.now;
  return name;
);

【讨论】:

什么时候真正设置好?它会持续吗?那么从现在起 3 天后,它还会有 3 天前的日期吗? 每当您更改给定属性时都会调用虚拟,在本例中为name。是的,它应该是持久的。 这对 Item 对象上的所有字段都有效吗?我不确定这个解决方案是否符合我的要求

以上是关于将 created_at 和 updated_at 字段添加到猫鼬模式的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 合并 created_at 和 updated_at 时间戳

避免 created_at 和 updated_at 由 sequelize 自动生成

Laravel 自定义 created_at 和 updated_at

在 Doctrine 中为 updated_at、created_at 自动取值

? created_at ? updated_at ?????????

Laravel 中 created_at 和 updated_at 列的优点是啥