Awesome Open Source
Awesome Open Source

Mongolass

NPM version Build status Dependency Status License Downloads

Elegant MongoDB driver for Node.js.

Installation

$ npm i mongolass --save

Usage

const Mongolass = require('mongolass')
const mongolass = new Mongolass()
mongolass.connect('mongodb://localhost:27017/test')// const mongolass = new Mongolass('mongodb://localhost:27017/test')

const User = mongolass.model('User')

User
  .find()
  .select({ name: 1, age: 1 })
  .sort({ name: -1 })
  .exec()
  .then(console.log)
  .catch(console.error)

Or use optional schema:

const Mongolass = require('mongolass')
const Schema = Mongolass.Schema
const mongolass = new Mongolass('mongodb://admin:[email protected]:27017/admin', {
  dbName: 'testdb'
})

const UserSchema = new Schema('UserSchema', {
  name: { type: 'string', required: true },
  age: { type: 'number', default: 18 }
})
const User = mongolass.model('User', UserSchema)

/*
equal to:
const User = mongolass.model('User', {
  name: { type: 'string', required: true },
  age: { type: 'number', default: 18 }
})
will create inner schema named `UserSchema`.
*/

User
  .insertOne({ name: 'nswbmw', age: 'wrong age' })
  .exec()
  .then(console.log)
  .catch(console.error)
/*
{ TypeError: ($.age: "wrong age") āœ– (type: number)
    at Model.insertOne (/Users/nswbmw/Desktop/test/node_modules/mongolass/lib/query.js:101:16)
    at Object.<anonymous> (/Users/nswbmw/Desktop/test/app.js:21:4)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
  validator: 'type',
  path: '$.age',
  actual: 'wrong age',
  expected: { type: 'number', default: 18 },
  schema: 'UserSchema',
  model: 'User',
  op: 'insertOne',
  args: [ { name: 'nswbmw', age: 'wrong age' } ],
  pluginName: 'MongolassSchema',
  pluginOp: 'beforeInsertOne',
  pluginArgs: [] }
*/

ObjectId schema:

'use strict'

const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')

const Post = mongolass.model('Post', {
  author: { type: Mongolass.Types.ObjectId }
}, { collName: 'post' })

Post.insertOne({ author: '111111111111111111111111' })
  .then(function () {
    return Post.find({ author: '111111111111111111111111' })
  })
  .then(console.log)
/*
[ { _id: 57caed24ecda6ffb15962591,
    author: 111111111111111111111111 } ]
 */

NB: You can pass collName as collection name.

API

Same as node-mongodb-native.

Mongolass vs Mongoose

ēŸ„ä¹Žļ¼šä»Žé›¶å¼€å§‹å†™äø€äøŖ Node.js ēš„ MongoDB é©±åŠØåŗ“


I've been using Mongoose for years, it's great but complex sucks, so i wrote Mongolass. Mongolass is not simply mimicking Mongoose, but rather draw on the advantages of mongoose redesigned the architecture. Mongolass has some exciting features different from Mongoose:

  1. Pure Schema. In Mongoose, Schema and Model and Entity are confused.

Schemas not only define the structure of your document and casting of properties, they also define document instance methods, static Model methods, compound indexes and document lifecycle hooks called middleware.

In Mongolass, Schema is only used for defining the structure of your document and casting of properties, Model used for retrievaling data from mongodb and register plugins, Entity(as result) is plain object. Schema is also optional.

  1. Awesome plugin system. Mongoose plugin system is not strong enough, eg: .pre, .post, use async next(). In Mongolass, we can register a plugin for Model or global mongolass instance. like:
User.plugin('xx', {
  beforeFind: function (...args) {},// or function return Promise
  afterFind: async function (result, ...args) {
    console.log(result, args)
    ...
  },
  // afterFind: async function (result, ...args) {
  //   console.log(result, args)
  //   ...
  // }
})

Above added two hook functions for User, when User.find().xx().exec() is called, the execution order is as follows:

beforeFind(handle query args) -> retrieve data from mongodb -> afterFind(handle query result)

Mongolass's plugins could be substituted for Mongoose's (document instance methods + static Model methods + plugins).

  1. Detailed error informations. see usage.
User
  .insertOne({ name: 'nswbmw', age: 'wrong age' })
  .exec()
  .then(console.log)
  .catch(console.error)
/*
{ TypeError: ($.age: "wrong age") āœ– (type: number)
    at Model.insertOne (/Users/nswbmw/Desktop/test/node_modules/mongolass/lib/query.js:105:16)
    at Object.<anonymous> (/Users/nswbmw/Desktop/test/app.js:23:4)
    at Module._compile (module.js:573:30)
    at Object.Module._extensions..js (module.js:584:10)
    at Module.load (module.js:507:32)
    at tryModuleLoad (module.js:470:12)
    at Function.Module._load (module.js:462:3)
    at Function.Module.runMain (module.js:609:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:598:3
  validator: 'type',
  actual: 'wrong age',
  expected: { type: 'number' },
  path: '$.age',
  schema: 'UserSchema',
  model: 'User',
  op: 'insertOne',
  args: [ { name: 'nswbmw', age: 'wrong age' } ],
  pluginName: 'MongolassSchema',
  pluginOp: 'beforeInsertOne',
  pluginArgs: [] }
*/

According to the error instance, esay to know age expect a number but got a string, from error stack know it's broken on app.js:23:4 and the operator is Model.insertOne.

Schema

see another-json-schema, support default and required.

required

const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')

const User = mongolass.model('User', {
  name: { type: 'string', required: true },
  age: { type: 'number', default: 18 }
})

;(async function () {
  await User.insert({ age: 17 })
})().catch(console.error)

Output:

{ TypeError: ($.name: undefined) āœ– (required: true)
    at Model.insert (/Users/nswbmw/Desktop/test/node_modules/mongolass/lib/query.js:101:16)
    at /Users/nswbmw/Desktop/test/app.js:10:14
    at Object.<anonymous> (/Users/nswbmw/Desktop/test/app.js:11:3)
    at Module._compile (module.js:635:30)
    at Object.Module._extensions..js (module.js:646:10)
    at Module.load (module.js:554:32)
    at tryModuleLoad (module.js:497:12)
    at Function.Module._load (module.js:489:3)
    at Function.Module.runMain (module.js:676:10)
    at startup (bootstrap_node.js:187:16)
  validator: 'required',
  path: '$.name',
  actual: undefined,
  expected: { type: 'string', required: true },
  schema: 'UserSchema',
  model: 'User',
  op: 'insert',
  args: [ { age: 17 } ],
  pluginName: 'MongolassSchema',
  pluginOp: 'beforeInsert',
  pluginArgs: [] }

default

const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')

const User = mongolass.model('User', {
  name: { type: 'string', required: true },
  age: { type: 'number', default: 18 },
  createdAt: { type: Mongolass.Types.Date, default: Date.now }
})

;(async function () {
  await User.insert({ name: 'nswbmw' })
  const user = await User.findOne({ name: 'nswbmw' })
  console.log(user)
  // { _id: 5a530c5d39d9a4eb3aa57856, name: 'nswbmw', age: 18, createdAt: 2019-01-10T09:50:26.831Z }
})()

Types

  • string
  • number
  • boolean
  • Mongolass.Types.ObjectId
  • Mongolass.Types.String
  • Mongolass.Types.Number
  • Mongolass.Types.Date
  • Mongolass.Types.Buffer
  • Mongolass.Types.Boolean
  • Mongolass.Types.Mixed

What's difference between number and Mongolass.Types.Number ? number only check type, Mongolass.Types.Number will try to convert value to a number, if failed then throw error.

Plugins

mongolass.plugin(pluginName, hooks)// register global plugin
User.plugin(pluginName, hooks)// register model plugin

example:

const moment = require('moment')
const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')
const User = mongolass.model('User')

mongolass.plugin('addCreatedAt', {
  beforeInsert: function (format) {
    console.log('beforeInsert', this._op, this._args, format)
    // beforeInsert insert [ { firstname: 'san', lastname: 'zhang' } ] YYYY-MM-DD
    this._args[0].createdAt = moment().format(format)
  }
})

User.plugin('addFullname', {
  afterFindOne: function (user, opt) {
    console.log('afterFindOne', this._op, this._args, opt)
    // afterFindOne findOne [] { sep: '-' }
    if (!user) return user
    user.fullname = user.firstname + opt.sep + user.lastname
    return user
  },
  afterFind: async function (users, opt) {
    console.log('afterFind', this._op, this._args, opt)
    // afterFind find [ { firstname: 'san' } ] { sep: ' ' }
    if (!users.length) return users
    return users.map(user => {
      user.fullname = user.firstname + opt.sep + user.lastname
      return user
    })
  }
})

;(async function () {
  // when use await, .exec() is omissible.
  await User.insert({ firstname: 'san', lastname: 'zhang' }).addCreatedAt('YYYY-MM-DD')
  console.log(await User.findOne().addFullname({ sep: '-' }))
  // { _id: 5850186544c3b82d23a82e45,
  //   firstname: 'san',
  //   lastname: 'zhang',
  //   createdAt: '2016-12-13',
  //   fullname: 'san-zhang' }
  console.log(await User.find({ firstname: 'san' }).addFullname({ sep: ' ' }))
  // [ { _id: 5850186544c3b82d23a82e45,
  //     firstname: 'san',
  //     lastname: 'zhang',
  //     createdAt: '2016-12-13',
  //     fullname: 'san zhang' } ]
})().catch(console.error)

NOTE: Different order of calling plugins will output different results. The priority of Model's plugins is greater than global's.

example:

const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')
const User = mongolass.model('User')

User.plugin('add2', {
  afterFindOne: function (user) {
    if (!user) return user
    user.name = `${user.name}2`
    return user
  }
})
User.plugin('add3', {
  afterFindOne: async function (user) {
    if (!user) return user
    user.name = `${user.name}3`
    return user
  }
})

;(async function () {
  await User.insert({ name: '1' })
  console.log(await User.findOne().add2().add3())
  // { _id: 58501a8a7cc264af259ca691, name: '123' }
  console.log(await User.findOne().add3().add2())
  // { _id: 58501a8a7cc264af259ca691, name: '132' }
})().catch(console.error)

see mongolass-plugin-populate.

Built-in plugins

Mongolass has some built-in plugins, only for find and findOne.

example:

const Mongolass = require('mongolass')
const mongolass = new Mongolass('mongodb://localhost:27017/test')
const User = mongolass.model('User')

;(async function () {
  await User.insert({ name: '1' })
  await User.insert({ name: '2' })
  const result = await User
    .find()
    .skip(1)
    .limit(1)
  console.log(result)
  // [ { _id: 58501c1281ea915a2760a2ee, name: '2' } ]
})().catch(console.error)

Test

$ npm test (coverage 100%)

License

MIT


Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
javascript (67,992)Ā 
nodejs (3,625)Ā 
mongodb (769)Ā 
schema (169)Ā 
odm (26)Ā 

Find Open Source By Browsing 7,000 Topics Across 59 Categories

Advertising šŸ“¦Ā 10
All Projects
Application Programming Interfaces šŸ“¦Ā 124
Applications šŸ“¦Ā 192
Artificial Intelligence šŸ“¦Ā 78
Blockchain šŸ“¦Ā 73
Build Tools šŸ“¦Ā 113
Cloud Computing šŸ“¦Ā 80
Code Quality šŸ“¦Ā 28
Collaboration šŸ“¦Ā 32
Command Line Interface šŸ“¦Ā 49
Community šŸ“¦Ā 83
Companies šŸ“¦Ā 60
Compilers šŸ“¦Ā 63
Computer Science šŸ“¦Ā 80
Configuration Management šŸ“¦Ā 42
Content Management šŸ“¦Ā 175
Control Flow šŸ“¦Ā 213
Data Formats šŸ“¦Ā 78
Data Processing šŸ“¦Ā 276
Data Storage šŸ“¦Ā 135
Economics šŸ“¦Ā 64
Frameworks šŸ“¦Ā 215
Games šŸ“¦Ā 129
Graphics šŸ“¦Ā 110
Hardware šŸ“¦Ā 152
Integrated Development Environments šŸ“¦Ā 49
Learning Resources šŸ“¦Ā 166
Legal šŸ“¦Ā 29
Libraries šŸ“¦Ā 129
Lists Of Projects šŸ“¦Ā 22
Machine Learning šŸ“¦Ā 347
Mapping šŸ“¦Ā 64
Marketing šŸ“¦Ā 15
Mathematics šŸ“¦Ā 55
Media šŸ“¦Ā 239
Messaging šŸ“¦Ā 98
Networking šŸ“¦Ā 315
Operating Systems šŸ“¦Ā 89
Operations šŸ“¦Ā 121
Package Managers šŸ“¦Ā 55
Programming Languages šŸ“¦Ā 245
Runtime Environments šŸ“¦Ā 100
Science šŸ“¦Ā 42
Security šŸ“¦Ā 396
Social Media šŸ“¦Ā 27
Software Architecture šŸ“¦Ā 72
Software Development šŸ“¦Ā 72
Software Performance šŸ“¦Ā 58
Software Quality šŸ“¦Ā 133
Text Editors šŸ“¦Ā 49
Text Processing šŸ“¦Ā 136
User Interface šŸ“¦Ā 330
User Interface Components šŸ“¦Ā 514
Version Control šŸ“¦Ā 30
Virtualization šŸ“¦Ā 71
Web Browsers šŸ“¦Ā 42
Web Servers šŸ“¦Ā 26
Web User Interface šŸ“¦Ā 210