mongoose-track v0.7.0
Mongoose Track
Mongoose Track allows you to track and manage document changes (deeply) with author references.
Install
npm i mongoose-track --saveGetting Started
const mongoose = require('mongoose')
const mongooseTrack = require('mongoose-track')
mongooseTrack.options = { ... }
let fruitSchema = new mongoose.Schema({
name: { type: String },
color: { type: String }
})
fruitSchema.plugin(mongooseTrack.plugin, { ... })
let fruitModel = mongoose.model('fruitModel', fruitSchema)All changes to fruitModel documents will now be written to the document at document.history.
Options
You can set options globaly or per schema by passing a second argument to the plugin, schema specific options override global options.
To set Global Options:
const mongooseTrack = require('mongoose-track')
mongooseTrack.options = { /*options*/ }To set Schema Specific Options:
const mongoose = require('mongoose')
const mongooseTrack = require('mongoose-track')
let mySchema = new mongoose.Schema({ ... })
mySchema.plugin(mongooseTrack.plugin, { /*options*/ }historyIgnoreindicates whether field history should be trackedtrueChanges to this property will not be added to thehistoryEventfalseChanges to this property will be added to thehistoryEventlet fruitSchema = new mongoose.Schema({ name: { type: String }, color: { type: String, historyIgnore: true } })
options.track.N, type:Boolean, default:true. Indicates whether history for newly created fields should be tracked.options.track.E, type:Boolean, default:true. Indicates whether history for edited (i.e. previously defined and changed) created fields should be tracked.options.track.D, type:Boolean, default:true. Indicates whether history for deleted (i.e. previously defined and removed) fields should be tracked.options.track.A, type:Boolean, default:true. Indicates whether history for array fields should be tracked.options.author.enabled, type:Boolean, default:false. Indicated whetherdocument.historyAuthorwill be addred to history.options.author.type, type:Mixed, default:mongoose.Schema.Types.String. This should be set to the_idtype of the author document, typically you'll usemongoose.Schema.Types.ObjectId.options.author.ref, type:String, default:undefined. This should be set to the model name of the author document, such as"userModel"
History Event historyEvent
A historyEvent is created when you save a document, if there are (tracked) property changes to that document they will be appended to the historyEvent and the historyEvent will be placed at the top of the document.history Array, otherwise no historyEvent will be saved.
history: [{
_id: ObjectId,
date: Date,
author: Mixed,
changes: [{ ... }]
}][historyEvent], type:Array. This array contains allhistoryEvent's for the documenthistoryEvent.date, type:Date, default:new Date(). This value is set just beforestatic.save()is firedhistoryEvent.author, type:Mixed. This value is set fromdocument.historyAuthor, assumingoptions.author.enabled === true
History Change Event historyChangeEvent
A historyChangeEvent is a (singular) change to a document property that occurred within document.history[].changes.
[{
_id: ObjectId,
path: [String],
before: Mixed,
after: Mixed
}][historyChangeEvent], type:Array
- This array contains all
historyChangeEvent's made within the currenthistoryEvent
historyChangeEvent.path, type:[String]
- This array denotes a reference to the changed key, for example:
{ color: { primary: "blue" } } === [ 'color', 'primary' ]
historyChangeEvent.before, type:Mixed
- This value is taken from the property (located at
historyChangeEvent.path) before being saved
historyChangeEvent.after, type:Mixed
- This value is taken from the property (located at
historyChangeEvent.path) after being saved
Methods
method.historyRevise(query, deepRevision), query:Mixed, deepRevision:Boolean.- If the
queryvalue is anObjectIdvalue from ahistoryEventorhistoryChangeEventthis will return a document with values matching thehistoryEvent._id || historyChangeEvent._id - If the
queryvalue is aDatevalue it will find the latesthistoryEventthat occurred prior to theDatevalue. - If
deepRevisionis set totruea deep revision will occur, this will revise the document to exactly how it was when the matchinghistoryEventwas created by recursivly setting all prior values from oldest to latest, stopping at the matching **storyEvent. - If
deepRevisionis set tofalseonly the changes within the matchinghistoryEventorhistoryChangeEventwill be revised. - Currently
deepRevisiondoes not support aqueryvalue of ahistoryChangeEventObjectId.
- If the
method.historyForget(historyEventId, single), query:ObjectId, deepRevision:Boolean. This method accepts an_idfrom ahistoryEventand will remove alldocument.historyprior to and including the matchinghistoryEvent. Ifsingleis set totrueonly the matchinghistoryEventwill be removed.
Statics
static.historyind(query), query:mongoose.Query. This static allows you to pass additional query operators tostatic.find(). Passing$revisionto the query with aDatevalue will return matching documents revised to that date, usesmethod._revise(). Additionally you can define$deepRevisionto return documents with a deep revision, same asmethod._revise().static.historyFindOne(query), query:mongoose.Query. This static allows you to pass additional query operators tostatic.findOne()Passing$revisionto the query with aDatevalue will return a matching document revised to that date, same asmethod._revise()Additionally you can define$deepRevisionto return documents with a deep revision, same asmethod._revise()
Questions
What properties are excluded from a
historyEventby default?
Changes to the following: ['_id', '__v', 'history', 'historyAuthor'] will not be recorded, along with any schema properties that have historyIgnore === true.
If a
historyEventoccurs but nohistoryChangeEvent's are logged, is it recorded?
No. If the history.changes Array is empty, the historyEvent will not be saved.
Can I pick where the history is stored? (other than
document.history)
Not yet, in the future you'll be able to set most if not all of the Mongoose Track keys, methods and statics.
Example
Usage
Clone this repository and run example.js
git clone https://github.com/brod/mongoose-track.git
cd mongoose-track
node example.jsYou should see the output of all **storyEvent's and historyChangeEvent's to a document including manual changes, authored changes, forget changes and a revision.
This will connect to mongodb://localhost/mongooseTrackExample
Minimum Example
The example below uses the minimum setup.
const mongoose = require('mongoose')
const mongooseTrack = require('mongoose-track')
let fruitSchema = new mongoose.Schema({
name: { type: String },
color: { type: String }
})
fruitSchema.plugin(mongooseTrack.plugin)
let fruitModel = mongoose.model('fruitModel', fruitSchema)Option Example
The example below does not track N events.
const mongoose = require('mongoose')
const mongooseTrack = require('mongoose-track')
mongooseTrack.options = {
track: {
N: false
}
}
let fruitSchema = new mongoose.Schema({
name: { type: String },
color: { type: String }
})
fruitSchema.plugin(mongooseTrack.plugin)
let fruitModel = mongoose.model('fruitModel', fruitSchema)Author Example
The example below appends an author to events.
const mongoose = require('mongoose')
const mongooseTrack = require('mongoose-track')
mongooseTrack.options = {
author: {
enable: true,
ref: 'userModel'
}
let fruitSchema = new mongoose.Schema({
name: { type: String },
color: { type: String }
})
fruitSchema.plugin(mongooseTrack.plugin)
let fruitModel = mongoose.model('fruitModel', fruitSchema)
let userSchema = new mongoose.Schema({
name: { type: String },
color: { type: String }
})
userSchema.plugin(mongooseTrack.plugin)
let userModel = mongoose.model('userModel', userSchema)To pass the author reference, set document.historyAuthor before you save the document.
var fruit = new fruitModel({
name: 'Banana',
color: 'Yellow',
historyAuthor: '507f191e810c19729de860ea'
})
fruit.save()
/* Document
{
name: 'Banana',
color: 'Yellow',
history: [{
date: ...
author: '507f191e810c19729de860ea',
changes: [{
type: 'N',
path: [],
after: {
name: 'Banana'
color: 'Yellow'
}
}]
}
*/Contribute
Feel free to send pull requests and submit issues 😉
