做者:Shailesh Shekhawat翻譯:瘋狂的技術宅html
原文:https://www.freecodecamp.org/...前端
未經容許嚴禁轉載node
本教程須要事先了解 mongoose 對象關係映射(ORM)技術
隨着程序的增加,日誌記錄成爲跟蹤全部內容的關鍵部分。它對於調試目的尤其重要。程序員
如今已經有了 npm 的日誌記錄模塊。這些模塊能夠將日誌存儲在不一樣格式或級別的文件中。咱們將使用流行的ORM Mongoose 討論 Node.js Express 程序中的 API 日誌記錄。面試
那麼如何建立一個 Mongoose 插件,以更清潔的方式爲你進行記錄並簡化 API 日誌?正則表達式
在 Mongoose 中,模式是可插入的。插件就像一個函數,你能夠在模式中使用它,並在模式實例上一次次地重用。mongodb
Mongoose 還提供全局插件,你能夠將其用於全部模式。例如咱們將會編寫一個插件,它將建立兩個 jsons
的diff
並寫入 mongodb
。數據庫
讓咱們建立一個具備如下六個屬性的基本日誌模式:express
create
、update
、delete
仍是別的什麼。若是你但願對本身的應用程序有意義,能夠添加更多字段,也能夠根據須要更改和升級架構。
這是咱們的模型:models/log.js
npm
const mongoose = require('mongoose') const Schema = mongoose.Schema const { ObjectId } = Schema const LogSchema = new Schema({ action: { type: String, required: true }, category: { type: String, required: true }, createdBy: { type: ObjectId, ref: 'Account', required: true }, message: { type: String, required: true }, diff: { type: Schema.Types.Mixed }, },{ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' }, }) LogSchema.index({ action: 1, category: 1 }) module.exports = mongoose.model('Log', LogSchema)
因此下一步是你須要一個可重用的函數,它將動態建立兩個 JSON 的 diff
。
咱們稱之爲 diff.js
const _ = require('lodash') exports.getDiff = (curr, prev) => { function changes(object, base) { return _.transform(object, (result, value, key) => { if (!_.isEqual(value, base[key])) result[key] = (_.isObject(value) && _.isObject(base[key])) ? changes(value, base[key]) : value }) } return changes(curr, prev) }
我使用了 lodash
,這是一個提供相同功能的受歡迎的庫。
讓咱們分解上面的函數,看看發生了什麼:
.reduce
的替代品。它會迭代你對象的 keys
和 values
。它提供了一個 accumulator
,是第一個參數。 result
是累加器,是可變的。
isEqual:此方法支持比較數組、數組緩衝區、布爾值、日期對象、錯誤對象、映射、數字、對象、正則表達式、集合、字符串、符號和類型化數組。對象經過它們本身的方法比較,而不是經過繼承的、可枚舉的屬性進行比較。函數和 DOM 節點則進行嚴格相等的比較,即便用
===
。
這裏咱們迭代每一個對象的屬性和值,並將它與舊對象進行比較。
若是當前對象的 value
不等於前一個對象中相同屬性的值:base[key]
若是該值是對象自己,咱們遞歸調用函數changes
直到它獲得一個值,它最終將做爲 result[key]=value
存儲在 result
中。
如今咱們須要跟蹤數據庫中的前一個 document
並在保存到 mongodb
以前建立一個 diff
。
const _ = require('lodash') const LogSchema = require('../models/log') const { getDiff } = require('../utils/diff') const plugin = function (schema) { schema.post('init', doc => { doc._original = doc.toObject({transform: false}) }) schema.pre('save', function (next) { if (this.isNew) { next() }else { this._diff = getDiff(this, this._original) next() } }) schema.methods.log = function (data) { data.diff = { before: this._original, after: this._diff, } return LogSchema.create(data) } } module.exports = plugin
在 Mongoose 中,有不一樣的鉤子可用。如今咱們須要使用架構上可用的 init
和 save
方法。
this.isNew()
:若是你正在建立新文檔,那麼只需返回 next()
中間件。
在 schema.post('init')
的 toObject()
中:
doc._original = doc.toObject({transform: false})
Mongoose Model
繼承自 document
,它有一個 toObject()
方法。它將 document
轉換爲 Object()
和transform:false
是爲了避免容許轉換返回對象。
在你的主server.js
或app.js
中:
初始化全局 plugin 以便它可用於全部模式。你還能夠經過在架構模型中初始化它來將其用於特定架構。
const mongoose = require('mongoose') mongoose.plugin(require('./app/utils/diff-plugin'))
這是 user
更新 API 的基本示例:
const User = require('../models/user') exports.updateUser = (req, res, next) => { return User.findById(req.params.id) .then(user => { if (!user) throw new Error('Target user does not exist. Failed to update.') const { name } = req.body if (name) user.name = name return user.save() }) .then(result => { res.json(result) return result }) .catch(next) .then(user => { if (user && typeof user.log === 'function') { const data = { action: 'update-user', category: 'users', createdBy: req.user.id, message: 'Updated user name', } return user.log(data) } }).catch(err => { console.log('Caught error while logging: ', err) }) }
在本教程中,你學習瞭如何建立 Mongoose 插件並用它來記錄 API 中的 changes
。你可使用插件執行更多操做來構建健壯的 Node.js 程序。