将 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 会将 createdAt
和 updatedAt
字段分配给您的架构,分配的类型是 Date
。
您还可以指定时间戳文件的名称:
timestamps: createdAt: 'created_at', updatedAt: 'updated_at'
注意:如果您正在处理具有关键数据的大型应用程序,您应该重新考虑更新您的文档。我建议您使用不可变的、仅附加的数据 (lambda architecture)。这意味着什么 你只允许插入。 更新和删除不应该 允许! 如果您想“删除”一条记录,您可以轻松地 插入带有一些
timestamp
/version
的新版本文档 归档,然后将deleted
字段设置为true
。同样,如果你想 更新文档 - 您使用适当的创建新文档 字段已更新,其余字段已复制。然后为了 查询此文档,您将获得具有最新时间戳的文档或 未被“删除”的最高版本(deleted
字段未定义或为假)。数据不变性确保您的数据是可调试的——您可以跟踪 每个文件的历史。您也可以回滚到上一个 如果出现问题,文档的版本。如果你带着这样的 建筑
ObjectId.getTimestamp()
是你所需要的,但它不是 依赖猫鼬。
原始答案:
如果您使用 ObjectId 作为身份字段,则不需要 created_at
字段。 ObjectIds 有一个名为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 列表中,我是否可以通过 propcreatedAt
、created_at
、timestamps.createdAt
或 timestamps.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
);
这会自动将createdAt
和updatedAt
字段添加到您的架构中。
http://mongoosejs.com/docs/guide.html#timestamps
【讨论】:
需要猫鼬版本>= 4.0 我发现文档不清楚 - 这会添加字段,但是否也确保它们在每次更新时都得到更新? 一般来说,createdAt 总是只存储一次……当对象被创建时。updatedAt
在每次新保存时更新(当 obj 更改时)
我得到了这个“2016-12-07T11:46:46.066Z”.. 请解释一下时间戳格式,如何更改时区?是采用 mongoDB 服务器时区吗??
@mcompeau,谢谢你的回答!我知道这是一个很长的尝试,但是您是否还记得以这种方式创建的字段是否可以用作索引?【参考方案5】:
像这样将timestamps
添加到您的Schema
然后createdAt
和updatedAt
将自动为您生成
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-troop
的timestamp
插件将此行为添加到任何架构。
【讨论】:
【参考方案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 自动取值