npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mongoose-track

v0.7.0

Published

Mongoose Track allows you to track and manage document changes (deeply) with author references and simple helper methods/statics.

Downloads

130

Readme

NPM

Mongoose Track

Mongoose Track allows you to track and manage document changes (deeply) with author references.

Install

npm i mongoose-track --save

Getting 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*/ }
  • historyIgnore indicates whether field history should be tracked

    • true Changes to this property will not be added to the historyEvent
    • false Changes to this property will be added to the historyEvent
      let 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 whether document.historyAuthor will be addred to history.

  • options.author.type, type: Mixed, default: mongoose.Schema.Types.String. This should be set to the _id type of the author document, typically you'll use mongoose.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 all historyEvent's for the document
  • historyEvent.date, type: Date, default: new Date(). This value is set just before static.save() is fired
  • historyEvent.author, type: Mixed. This value is set from document.historyAuthor, assuming options.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 current historyEvent
  • 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 query value is an ObjectId value from a historyEvent or historyChangeEvent this will return a document with values matching the historyEvent._id || historyChangeEvent._id
    • If the query value is a Date value it will find the latest historyEvent that occurred prior to the Date value.
    • If deepRevision is set to true a deep revision will occur, this will revise the document to exactly how it was when the matching historyEvent was created by recursivly setting all prior values from oldest to latest, stopping at the matching **storyEvent.
    • If deepRevision is set to false only the changes within the matching historyEvent or historyChangeEvent will be revised.
    • Currently deepRevision does not support a query value of a historyChangeEvent ObjectId.
  • method.historyForget(historyEventId, single), query: ObjectId, deepRevision: Boolean. This method accepts an _id from a historyEvent and will remove all document.history prior to and including the matching historyEvent. If single is set to true only the matching historyEvent will be removed.

Statics

  • static.historyind(query), query: mongoose.Query. This static allows you to pass additional query operators to static.find(). Passing $revision to the query with a Date value will return matching documents revised to that date, uses method._revise(). Additionally you can define $deepRevision to return documents with a deep revision, same as method._revise().

  • static.historyFindOne(query), query: mongoose.Query. This static allows you to pass additional query operators to static.findOne() Passing $revision to the query with a Date value will return a matching document revised to that date, same as method._revise() Additionally you can define $deepRevision to return documents with a deep revision, same as method._revise()

Questions

What properties are excluded from a historyEvent by default?

Changes to the following: ['_id', '__v', 'history', 'historyAuthor'] will not be recorded, along with any schema properties that have historyIgnore === true.

If a historyEvent occurs but no historyChangeEvent'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.js

You 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 😉