เพิ่มเขตข้อมูล created_at และ updated_at ไปยัง schongas พังพอน


166

มีวิธีเพิ่มเขตข้อมูล create_at และ updated_at ลงใน schong ของ mongoose โดยไม่ต้องผ่านมันในทุกครั้งที่มีการเรียก MyModel () ใหม่?

ฟิลด์ created_at จะเป็นวันที่และจะถูกเพิ่มเมื่อสร้างเอกสารเท่านั้น เขตข้อมูล updated_at จะได้รับการอัพเดตพร้อมกับวันที่ใหม่เมื่อใดก็ตามที่บันทึก () ถูกเรียกบนเอกสาร

ฉันได้ลองทำสิ่งนี้ในสคีมาแล้ว แต่ฟิลด์ไม่ปรากฏขึ้นเว้นแต่ว่าฉันจะเพิ่มเข้าไป

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

ฉันทำสิ่งที่คุณทำและทำงานได้ดี ใช้ Mongoose 4.8.4 อาจเป็นสิ่งใหม่หรือไม่?
martinedwards

1
นี้มาจากค่อนข้างนานมาแล้ว
chovy

ใช่คิดว่ามันคุ้มค่าที่จะสังเกตว่าตอนนี้ใช้งานได้แล้ว
martinedwards

คำตอบ:


132

ในฐานะของ 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


2
เห็นด้วยนี่เป็นตัวเลือกที่ดีที่สุดใน Mongoose 4.0
Tadd Giles

258

อัปเดต: (5 ปีต่อมา)

หมายเหตุ:หากคุณตัดสินใจที่จะใช้สถาปัตยกรรม Kappa (การจัดหากิจกรรม + CQRS ) คุณไม่จำเป็นต้องอัพเดทวันที่เลย เนื่องจากข้อมูลของคุณเป็นบันทึกเหตุการณ์แบบไม่เปลี่ยนรูปแบบและต่อท้ายคุณจึงต้องมีวันที่สร้างเหตุการณ์เท่านั้น คล้ายกับสถาปัตยกรรมแลมบ์ดาที่อธิบายไว้ด้านล่าง สถานะแอปพลิเคชันของคุณจะเป็นเส้นโครงบันทึกเหตุการณ์ (ข้อมูลที่ได้รับ) หากคุณได้รับเหตุการณ์ที่ตามมาเกี่ยวกับเอนทิตีที่มีอยู่คุณจะใช้วันที่สร้างกิจกรรมเป็นวันที่อัปเดตสำหรับเอนทิตีของคุณ นี่คือการปฏิบัติที่ใช้กันทั่วไป (และเข้าใจผิดทั่วไป) ในระบบไมโครเซอร์วิส

อัปเดต: (4 ปีต่อมา)

หากคุณใช้ObjectIdเป็น_idฟิลด์ของคุณ(ซึ่งโดยปกติจะเป็นกรณี) สิ่งที่คุณต้องทำคือ:

let document = {
  updatedAt: new Date(),
}

ตรวจสอบคำตอบดั้งเดิมของฉันด้านล่างเกี่ยวกับวิธีรับการประทับเวลาที่สร้างจาก_idฟิลด์ หากคุณต้องการใช้ ID จากระบบภายนอกให้ตรวจสอบคำตอบของ Roman Rhrn Nesterov

อัปเดต: (2.5 ปีต่อมา)

ตอนนี้คุณสามารถใช้ตัวเลือก#timestampsกับรุ่นพังพอน> = 4.0

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

ถ้า timestamps ชุดกำหนดพังพอนcreatedAtและเขตแบบแผนของคุณชนิดที่ได้รับมอบหมายเป็นupdatedAtDate

คุณยังสามารถระบุชื่อไฟล์บันทึกเวลา:

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

หมายเหตุ:หากคุณกำลังทำงานกับแอปพลิเคชันขนาดใหญ่ที่มีข้อมูลสำคัญคุณควรพิจารณาการอัปเดตเอกสารของคุณอีกครั้ง ฉันขอแนะนำให้คุณทำงานกับข้อมูลที่ไม่เปลี่ยนแปลงและต่อท้ายเท่านั้น ( สถาปัตยกรรมแลมบ์ดา ) สิ่งนี้หมายความว่าคุณอนุญาตให้มีการแทรกเท่านั้น ไม่อนุญาตให้อัปเดตและลบ! หากคุณต้องการที่จะ "ลบ" บันทึกคุณได้อย่างง่ายดายสามารถแทรกรุ่นใหม่ของเอกสารที่มีบางส่วนtimestamp/ version ยื่นและการตั้งค่าแล้วฟิลด์deleted trueในทำนองเดียวกันถ้าคุณต้องการอัปเดตเอกสาร - คุณสร้างใหม่พร้อมกับอัปเดตฟิลด์ที่เหมาะสมและส่วนที่เหลือของฟิลด์ที่คัดลอกมาจากนั้นเพื่อสอบถามเอกสารนี้คุณจะได้รับเอกสารที่มีการประทับเวลาใหม่ล่าสุดหรือเวอร์ชันสูงสุด ไม่ "ลบ" (deleted ฟิลด์ไม่ได้กำหนดหรือเท็จ `)

ความไม่สามารถเปลี่ยนแปลงของข้อมูลทำให้มั่นใจได้ว่าข้อมูลของคุณนั้นสามารถ debuggable ได้คุณสามารถติดตามประวัติของเอกสารทุกฉบับได้ นอกจากนี้คุณยังสามารถย้อนกลับไปยังเอกสารเวอร์ชันก่อนหน้าหากมีสิ่งผิดปกติ หากคุณไปกับสถาปัตยกรรมดังกล่าวObjectId.getTimestamp()เป็นสิ่งที่คุณต้องการและไม่ขึ้นอยู่กับพังพอน


คำตอบเดิม:

หากคุณใช้ ObjectId เป็นฟิลด์ข้อมูลประจำตัวของคุณคุณไม่จำเป็นต้องใช้created_atฟิลด์ ObjectIds getTimestamp()มีวิธีการที่เรียกว่า

มี objectid ( "507c7f79bcf86cd7994f6c0e"). getTimestamp ()

นี่จะส่งคืนผลลัพธ์ต่อไปนี้:

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

ข้อมูลเพิ่มเติมที่นี่ฉันจะแยกวันที่สร้างออกมาจาก 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();
});

3
คุณจะทำเช่นนั้นใน Mongoose / node.js ได้อย่างไร
Zebra Propulsion Lab

6
นั่นเป็นสิ่งที่ควรรู้แม้ว่าจะไม่ใช่โซลูชันที่นำมาใช้ แต่น่าสนใจมาก!
ไม่ตั้งใจ

แต่ถ้าฉันต้องการส่งการสร้าง ณ วันที่ไปยังมุมมองฉันจะต้องตั้งค่าตัวแปรพิเศษสำหรับสิ่งนี้หรือส่งรหัสที่ถูกต้อง?

3
ฉันขอขอบคุณคำตอบแรก แต่การสนับสนุนการใช้สถาปัตยกรรมแลมบ์ดาดูเหมือนว่าเป็นวิธีที่ดีในการแก้ปัญหาเมตาดาต้าในระดับ 5 เท่าและอาจไม่เคยส่งมอบแทนที่จะสร้างแอพ CRUD และทำให้มันเรียบง่ายเว้นแต่ว่าจะรับประกันความซับซ้อนของวิธีนี้ โปรดจำไว้ว่าคุณกำลังโพสต์เกี่ยวกับ MongoDB ที่ 'แทบจะไม่' เริ่มต้นด้วยและคุณสนับสนุนการบันทึกใหม่สำหรับการเขียนทุกครั้งที่จะฆ่า Mongo อย่างรวดเร็วในระดับที่มีความหมายใด ๆ ย้ายสิ่งนี้ไปยังส่วน Cassandra ...
John Culviner

1
ฉันเปลี่ยนการออกแบบแบ็คเอนด์จริง ๆ เพื่อทำการลบแบบนุ่มแทนการลบจริงขอบคุณข้อมูลที่ให้ไว้ในคำตอบนี้ มันให้ความรู้เชิงลึกที่มีประโยชน์จริงๆ
Kenna

133

นี่คือสิ่งที่ฉันทำ:

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();
});

8
1. เก็บเวลาปัจจุบันใน var ท้องถิ่นและกำหนดแทนแต่ละครั้งที่เรียกวันที่ใหม่ () สิ่งนี้จะทำให้แน่ใจว่าที่ผ่านครั้งแรก created_at และ updated_at มีค่า excect ที่เหมือนกัน 2. new Date => new Date ()
Shay Erlichmen

13
ต้องการเพียงชี้ให้เห็นว่าถ้าคุณใช้ ObjectId คุณสามารถสร้าง created_at จากที่นั่นได้ .... คุณไม่จำเป็นต้องมีฟิลด์แยกต่างหาก ชำระเงินgetTimestamp()
Xerri

6
ตัวเลือกอื่น ๆ สำหรับ created_at อาจเป็นการเปลี่ยนแบบจำลองเป็น -> created_at: {ชนิด: วันที่, ค่าเริ่มต้น: Date.now},
OscarVGG

1
@ajbraus สร้างปลั๊กอินของสคีมา
wlingke

2
ใช้Date.now()ที่เป็นไปได้แทนวิธีที่new Dateรวดเร็วกว่าด้วยวิธีคงที่
karlkurzer

107

ใช้timestampsตัวเลือกในตัวสำหรับ Schema ของคุณ

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

การดำเนินการนี้จะเพิ่มcreatedAtและupdatedAtฟิลด์ไปยังสคีมาของคุณโดยอัตโนมัติ

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


1
ต้องใช้รุ่นพังพอน> = 4.0
Jensen

2
ฉันพบเอกสารที่ไม่ชัดเจน - นี่เป็นการเพิ่มเขตข้อมูล แต่ยังทำให้แน่ใจว่าพวกเขาได้รับการอัปเดตในทุกการอัปเดตหรือไม่
ลุดวิ

โดยทั่วไปแล้ว createdAt จะถูกจัดเก็บเพียงครั้งเดียวเท่านั้น ... เมื่อวัตถุถูกสร้างขึ้น updatedAtมีการอัปเดตในการบันทึกใหม่แต่ละรายการ (เมื่อมีการเปลี่ยนแปลง obj)
codyc4321

1
ฉันได้รับสิ่งนี้ "2016-12-07T11: 46: 46.066Z" .. โปรดอธิบายรูปแบบการประทับเวลาได้อย่างไรเปลี่ยนโซนเวลาได้อย่างไร ?? ใช้เขตเวลาของเซิร์ฟเวอร์ mongoDB หรือไม่
Mahesh K

@mcompeau ขอบคุณสำหรับคำตอบ! ฉันรู้ว่ามันเป็นช็อตยาว แต่คุณจำได้ไหมว่ามีโอกาสที่ฟิลด์ที่สร้างด้วยวิธีนี้สามารถใช้เป็นดัชนีได้หรือไม่
โนโด

29

ถ้าใช้update()หรือfindOneAndUpdate()

ด้วย{upsert: true}ตัวเลือก

คุณสามารถใช้ได้ $setOnInsert

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

1
งานนี้ในไลบรารี mongo ส่วนใหญ่ เปลี่ยนเป็นคำตอบที่ยอมรับ
chovy

1
หากคุณใช้_idฟิลด์เริ่มต้นของ Mogo คุณไม่จำเป็นต้องมีcreatedAtฟิลด์ ตรวจสอบคำตอบของฉันด้านล่างสำหรับรายละเอียดเพิ่มเติม
Pavel Nikolov

1
... ตรวจสอบของฉันคำตอบเดิม
Pavel Nikolov

25

เพิ่ม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' }

2
ตอนนี้พังพอนทำงานได้ดีสำหรับคุณฉันยอมรับว่านี่เป็นทางออกที่ดีที่สุดในตอนนี้
Vince Bowdren

ใช่เห็นด้วยกับ @VinceBowdren
Mr.spShuvo

8

นี่คือวิธีที่ฉันประสบความสำเร็จในการสร้างและอัปเดต

ภายในสคีมาของฉันฉันได้เพิ่มสิ่งที่สร้างและอัปเดตดังนี้:

   / **
     * โครงสร้างบทความ
     * /
    var ArticleSchema = Schema ใหม่ ({
        สร้าง: {
            ประเภท: วันที่,
            เริ่มต้น: Date.now
        }
        อัปเดต: {
            ประเภท: วันที่,
            เริ่มต้น: Date.now
        }
        ชื่อ: {
            ประเภท: สตริง
            ค่าเริ่มต้น: '',
            เล็ม: จริง
            ต้องการ: 'ชื่อต้องไม่ว่างเปล่า'
        }
        เนื้อหา: {
            ประเภท: สตริง
            ค่าเริ่มต้น: '',
            ตัดแต่ง: จริง
        }
        ผู้ใช้: {
            ประเภท: Schema.ObjectId,
            อ้างอิง: 'ผู้ใช้'
        }
    });

จากนั้นในวิธีการอัปเดตบทความของฉันภายในตัวควบคุมบทความที่ฉันเพิ่ม:

/ **
     * อัปเดตบทความ
     * /
    exports.update = ฟังก์ชั่น (req, res) {
        บทความ var = req.article;

        บทความ = _.extend (บทความ req.body);
        article.set ("อัพเดท", Date.now ());

        article.save (ฟังก์ชั่น (ผิดพลาด) {
            ถ้า (ผิดพลาด) {
                return res.status (400). ส่ง ({
                    ข้อความ: errorHandler.getErrorMessage (err)
                });
            } อื่น {
                res.json (บทความ);
            }
        });
    };

ส่วนที่เป็นตัวหนาเป็นส่วนที่น่าสนใจ



4

คุณสามารถใช้timestampปลั๊กอินของmongoose-troopเพื่อเพิ่มลักษณะการทำงานนี้ให้กับสคีมาใด ๆ


3

คุณสามารถใช้ปลั๊กอินนี้ได้อย่างง่ายดายมาก จากเอกสาร:

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'
});

2

เราอาจจะสามารถบรรลุนี้โดยใช้ปลั๊กอินสคียัง

ใน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);

2

ในสคีมาโมเดลของคุณเพียงเพิ่มการประทับเวลาของแอตทริบิวต์และกำหนดค่าจริงให้กับมันดังที่แสดง: -

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

สิ่งนี้จะทำอย่างไร
chovy

แอตทริบิวต์การประทับเวลาจะเพิ่มเขตข้อมูล create_at และ updated_at ในแต่ละเอกสาร ฟิลด์ Created_at ระบุเวลาที่สร้างเอกสารและเขตข้อมูล updated_at จะระบุเวลาของการอัปเดตหากมีเวลาอื่นในการสร้างเอกสาร ค่าฟิลด์ทั้งสองอยู่ในรูปแบบเวลา ISO หวังว่านี่จะเคลียร์ข้อสงสัยของคุณ Chovy
Pavneet Kaur

1
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 }
});

ตอนนี้คุณสามารถใช้สิ่งนี้เพื่อไม่จำเป็นต้องรวมตัวเลือกนี้ในทุกตาราง


1

รุ่นพังพอนของฉันคือ 4.10.2

ดูเหมือนว่าตะขอเท่านั้นที่ใช้findOneAndUpdateงานได้

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

0

ใช้ฟังก์ชั่นเพื่อคืนค่าเริ่มต้นที่คำนวณได้:

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()
karlkurzer

1
ฉันห่อมันไว้ในฟังก์ชั่นเพื่อที่ฉันจะได้จำลอง '.now ()' ในการทดสอบ มิฉะนั้นจะเรียกใช้เพียงครั้งเดียวในระหว่างการเริ่มต้นและค่าไม่สามารถเปลี่ยนแปลงได้ง่าย
orourkedd

1
โปรดทราบว่าdefault: Date.now()จะผิด default: Date.nowถ้ามีอะไรที่มันเป็น มิฉะนั้นเอกสารทั้งหมดของคุณจะมีการประทับเวลาเดียวกัน: เวลาที่แอปพลิเคชันของคุณเริ่มต้น)
Max Truxa

ฉันชอบdefault: Date.nowกลยุทธ์ของคุณ ทำความสะอาดมาก
orourkedd

0

ที่จริงฉันทำสิ่งนี้ที่ด้านหลัง

หากทุกอย่างเป็นไปด้วยดีกับการอัพเดท:

 // 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
      });
    });

0

ใช้ 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
});

ชุดเครื่องจักรยอดเยี่ยมด้วย API ที่ชัดเจนไม่เหมือนโลกด่วนหรือ Javascript ทั่วไป


-2

คุณสามารถใช้มิดเดิลแวร์และVirtuals นี่คือตัวอย่างสำหรับupdated_atสาขาของคุณ:

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

สิ่งนี้จะเกิดขึ้นจริงเมื่อไหร่? และมันจะยังคงอยู่หรือไม่ ดังนั้น 3 วันจากนี้มันจะมีวันที่จาก 3 วันที่ผ่านมา?
chovy

เสมือนจะถูกเรียกเมื่อใดก็ตามที่คุณเปลี่ยนคุณสมบัติที่กำหนดในกรณีnameนี้ และใช่มันควรจะเป็นแบบถาวร
zemirco

สิ่งนี้จะใช้ได้กับทุกฟิลด์ในวัตถุรายการหรือไม่ ฉันไม่แน่ใจว่าวิธีการแก้ปัญหานี้จะทำในสิ่งที่ฉันต้องการ
chovy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.