Skip to content

Latest commit

 

History

History
259 lines (186 loc) · 8.55 KB

README_en.md

File metadata and controls

259 lines (186 loc) · 8.55 KB

mongoose-modified-at

CircleCI Coverage Status npm version code style: prettier license

Mongoose plugin that tracking the fields you specified and automatically record the change time of them into DB. It just like timestamps function of Mongoose itself.

English | 中文

Table of Contents

Example

Let's consider an example, we need to provide a website for users to publish and display their own articles. The data schema looks just like this:

const schema = new mongoose.Schema({
  title: String,
  is_draft: Boolean,
  is_recommended: Boolean,
  // more fields...
})

When we are displaying the latest article list, we should display it in reverse order of when the article was first released, because the article can be saved as a draft and edited many times, so we can't use createdAt or updatedAt provided by Mongoose. The correct way is to make sure that the user posts the article instead of saving it as a draft each time the article is created or updated, and then records the time as the time of the first release.

To do this we need to handle it in the code logic layer, it's possible but will make code highly coupled, or you can package a Mongoose middleware by yourself to do it, but now you can hand it over to a plugin ModifiedAt that is tested and API elegant.

First, you could install the plugin.

npm install mongoose-modified-at --save

Then simply configure the schema on it initialization, as follows:

import modifiedAt from 'mongoose-modified-at'

// before mongoose.model invoked
schema.plugin(modifiedAt, {
  // function name will as field name insert to database.
  publishedAt(doc) {
    // when returns a value of true, the time is recorded.
    return !doc.is_draft
  },
  // recommend article same as above.
  recommendedAt(doc) {
    return doc.is_recommended
  },
})

const Article = mongoose.model('Article', schema)

When the document is saved or updated with the is_draft field and the value is false, the plugin will recorded the time of document saved or updated to the publishedAt field you declared and write into database.

Just like this:

await Article.create({
  title: 'Document Title',
  is_draft: false,
  is_recommended: true,
  // more fields...
})

Results from database:

{
  "title": "Document Title",
  "is_draft": false,
  "is_recommended": true,
  "publishedAt": ISODate("2019-09-27T03:11:07.880Z"),
  "recommendedAt": ISODate("2019-09-27T03:11:07.880Z"),
  // more fields...
}

API Intro

The above is the rich API form of ModifiedAt, all the options are as follows:

schema.plugin(modifiedAt, {
  // watch fields
  fields: ['name', 'status', 'another'],
  // set suffix
  suffix: '_your_suffix',
  // set "select()" behavior for paths
  select: true,
  customField(doc) {
    // do something what you want to do,
    // then return a boolean value that telling plugin record the time or not.
  },
})

🍎 Explains:

  • fields: Set listening fields. When the document is saved or updated with them, it will automatically made the form of field name + suffix as a field and recorded the time to the field. Optional, Array type.

  • suffix: Set suffix, default value is _modifiedAt. Optional, String type.

  • select: Set select() behavior for paths, see Mongoose documentation for more details about it. Default value of true. Optional, Boolean type.

  • customField: Custom filed that used for custom logic, the function receives the unique document parameter, when it returns true value, the time will be recorded to the field. This field will not be suffixed.

🌟 1、 You can set the global suffix on application initialization, it will be used for each plugin instance, as follows:

import modifiedAt from 'mongoose-modified-at'
modifiedAt.suffix = '_your_suffix'

🚀 2、 In order to simplify API and make it user friendly while avoiding excessive overloads, ModifiedAt has only added a simplified format for the parameters, as follows:

schema.plugin(modifiedAt, ['name', 'status'])

This means that the fields option is extracted as a parameter and the result as follows.

{
  "name": "Tom",
  "status": 1,
  "name_modifiedAt": ISODate("2019-09-27T03:13:17.888Z"),
  "status_modifiedAt": ISODate("2019-09-27T03:13:17.888Z"),
}

Support Async

You need Node.js to support async/await.

import P from 'bluebird'

const petSchema = new mongoose.Schema({
  name: String,
  age: Number,
  sex: String,
  // 1: in purchasing, 2: bought, 3: sold
  status: Number,
})

petSchema.plugin(modifiedAt, {
  // record when you bought it
  async boughtAt(doc) {
    // delay 1s
    await P.delay(1000)
    return doc.status === 2
  },
  // record when you sold it
  soldAt(doc) {
    return doc.status === 3
  },
})

Details

👍 1、 For a series of update operations, you can skip the plugin function by passing { modifiedAt: false } to options for this update.

JavaScriptModel.updateOne({}, { status: 2 }, { modifiedAt: false })

TypeScriptModel.updateOne({}, { status: 2 }, { modifiedAt: false } as any)


🙌 2、 If you need passing options to Model.create(), the docs must be an array, see Mongoose documentation:

[options] «Object» Options passed down to save(). To specify options, docs must be an array, not a spread.

🤟 3、 For a series of replace operations, the plugin function is disabled by default because you probably only want to replace the data. Of course, you can enable it by passing { modifiedAt: true } to options for this replace.

For example:Model.findOneAndReplace({}, { status: 2 }, { modifiedAt: true })

Related API list:

  • Model.findOneAndReplace()
  • Model.replaceOne()
  • Query.prototype.findOneAndReplace()
  • Query.prototype.replaceOne()
  • Document.prototype.replaceOne()

🖐 4、 Does not support Model.bulkWrite() operation because it does not trigger any middleware, not save() nor update(). If you need to trigger save() middleware for every document use create() instead. See Mongoose documentation.

Though the results are the same but the performance is different, if you want to balance performance at the same time, you can manually adding time to the bulk data.


🖐 5、 Does not support MongoDB native operators of $set, $inc, $currentDate, $mul etc, maybe next release to support.

For example:Model.updateOne({}, { $inc: { quantity: 5 } })


🖐 6、 Does not support default value of the schema because it is inaccessible, as follows:

const schema = new mongoose.Schema({
  name: String,
  age: {
    type: Number,
    default: 1,
  },
})

schema.plugin(modifiedAt, ['name', 'age'])

const Cat = mongoose.model('Cat', schema)

const kitty = await Cat.create({ name: 'Kitty' })

// results:
// kitty.name => 'Kitty'
// kitty.name_modifiedAt => ISODate("2019-09-27T03:13:17.888Z")
// kitty.age => 1
// kitty.age_modifiedAt => doesn't exist

You can set the age property of default value to create() if the age need to be reached.

Version Compatibility

Current version support for Mongoose 5.x, if you are using Mongoose 4.x now please installing version 1.x, the documentation at here.

npm install mongoose-modified-at@1 --save

Changelog

Detailed changes for each release are documented in the CHANGELOG.md.

License

MIT licensed.