New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

mongorito

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mongorito - npm Package Compare versions

Comparing version 1.4.6 to 2.0.0

410

lib/mongorito.js

@@ -7,7 +7,9 @@ 'use strict';

const isGeneratorFn = require('is-generator-fn');
const pluralize = require('pluralize');
const compose = require('koa-compose');
const mongodb = require('mongodb');
const Promise = require('bluebird');
const extend = require('class-extend').extend;
const result = require('lodash.result');
const assign = require('object-assign');
const clone = require('clone');

@@ -17,2 +19,3 @@ const get = require('get-value');

const is = require('is_js');
const co = require('co');

@@ -24,2 +27,3 @@ const Query = require('./query');

/**

@@ -31,97 +35,103 @@ * Mongorito

class Mongorito {
/**
* Connect to a MongoDB database and return connection object
*
* @param {String} urls - connection urls (as arguments)
* @see http://mongodb.github.io/node-mongodb-native/2.0/api/MongoClient.html#.connect
* @api public
*/
function Mongorito () {}
static connect () {
// parse arguments
let args = Array.prototype.slice.call(arguments);
let urls = [];
let options = {};
/**
* Connect to a MongoDB database and return connection object
*
* @param {String} urls - connection urls (as arguments)
* @see http://mongodb.github.io/node-mongodb-native/2.0/api/MongoClient.html#.connect
* @api public
*/
args.forEach(function (arg) {
if (is.string(arg)) {
urls.push(arg);
}
Mongorito.connect = function () {
// parse arguments
let args = Array.prototype.slice.call(arguments);
if (is.object(arg)) {
options = arg;
}
});
let urls = [];
let options = {};
urls = urls.map(function (url) {
if (!url.startsWith('mongodb://')) {
url = 'mongodb://' + url;
}
args.forEach(function (arg) {
if (is.string(arg)) {
urls.push(arg);
}
return url;
});
if (is.object(arg)) {
options = arg;
}
});
let self = this;
urls = urls.map(function (url) {
if (!url.startsWith('mongodb://')) {
url = 'mongodb://' + url;
}
return MongoClient.connect(urls.join(','), options).then(function (db) {
if (!self.db) {
self.db = db;
}
return url;
});
return db;
});
}
let self = this;
let connection = MongoClient.connect(urls.join(','), options).then(function (db) {
if (!self.db) {
self.db = db;
}
return db;
});
/**
* Disconnect from a database
*
* @api public
*/
static disconnect () {
return this.db.close();
if (!this._connection) {
this._connection = connection;
}
return connection;
};
/**
* Alias for .disconnect()
*
* @api public
*/
static close () {
return Mongorito.disconnect.apply(this, arguments);
}
/**
* Disconnect from a database
*
* @api public
*/
Mongorito.disconnect = function () {
return this.db.close();
};
/**
* Return a co-wrapped monk collection
*
* @api private
*/
static _collection (db, name) {
let url = db.s.options.url;
let collections = this._collections[url];
/**
* Alias for .disconnect()
*
* @api public
*/
if (!collections) {
collections = this._collections[url] = {};
}
Mongorito.close = function () {
return Mongorito.disconnect.apply(this, arguments);
};
if (collections[name]) {
return collections[name];
}
let collection = db.collection(name);
/**
* Return a co-wrapped monk collection
*
* @api private
*/
collections[name] = collection;
Mongorito._collection = function (db, name) {
let url = db.s.options.url;
let collections = this._collections[url];
if (!collections) {
collections = this._collections[url] = {};
}
if (collections[name]) {
return collections[name];
}
}
let collection = db.collection(name);
collections[name] = collection;
return collections[name];
};
/**

@@ -141,6 +151,6 @@ * Cache for collections

function Model (attrs, options) {
this.attributes = clone(attrs || {});
this.attributes = assign({}, attrs);
this.changed = {};
this.previous = {};
this.options = clone(options || {});
this.options = options || {};

@@ -178,17 +188,21 @@ // reset hooks

Model.prototype._collection = function () {
if (is.string(this.collection)) {
return Mongorito._collection(this._db(), this.collection);
}
let self = this;
// get collectio name
// from the "collection" property
// or generate the default one
let defaultName = pluralize(this.constructor.name).toLowerCase();
let name = result(this, 'collection', defaultName);
return this._db().then(function (db) {
if (is.string(self.collection)) {
return Mongorito._collection(db, self.collection);
}
// save collection name
// to avoid the same check in future
this.collection = this.constructor.prototype.collection = name;
// get collectio name
// from the "collection" property
// or generate the default one
let defaultName = pluralize(self.constructor.name).toLowerCase();
let name = result(self, 'collection', defaultName);
return Mongorito._collection(this._db(), this.collection);
// save collection name
// to avoid the same check in future
self.collection = self.constructor.prototype.collection = name;
return Mongorito._collection(db, self.collection);
});
};

@@ -207,3 +221,5 @@

// or use a default one
return this.db ? this.db() : Mongorito.db;
let db = this.db ? this.db() : Mongorito._connection;
return Promise.resolve(db);
};

@@ -309,3 +325,3 @@

Model.prototype.toJSON = function () {
return this.attributes;
return assign({}, this.attributes);
};

@@ -355,3 +371,3 @@

// for each item
if (is.array(method)) {
if (Array.isArray(method)) {
let methods = method;

@@ -369,5 +385,17 @@

if (is.not.function(method)) {
let name = method;
method = this[method];
if (!method.name) {
method.name = name;
}
}
// if method is a generator function
// convert it to promise
if (isGeneratorFn(method)) {
method = co.wrap(method);
}
// around hooks should be

@@ -442,2 +470,4 @@ // at the end of before:*

Model.prototype._runHooks = function (when, action, options) {
let self = this;
if (!options) {

@@ -458,7 +488,14 @@ options = {};

hooks = hooks.filter(function (fn) {
return skip.indexOf(fn.name) === -1;
// generator functions are wrapped using co.wrap()
// which hides the real function name, so we need
// to get function name ourselves
let fnName = fn.__generatorFunction__ ? fn.__generatorFunction__.name : fn.name;
return skip.indexOf(fnName) === -1;
});
}
return compose(hooks).call(this);
return Promise.each(hooks, function (hook) {
return hook.call(self);
});
};

@@ -513,3 +550,3 @@

if (is.array(value)) {
if (Array.isArray(value)) {
value = value.map(function (doc) {

@@ -535,6 +572,6 @@ return doc.get('_id');

Model.prototype.create = function * (options) {
let collection = this._collection();
Model.prototype.create = function (options) {
let self = this;
let attrs = this.attributes;
let date = new Date();

@@ -547,12 +584,18 @@

yield* this._runHooks('before', 'create', options);
return this._collection()
.tap(function () {
return self._runHooks('before', 'create', options);
})
.then(function (collection) {
return collection.insert(attrs);
})
.then(function (inserted) {
let doc = inserted.ops[0];
let inserted = yield collection.insert(attrs);
let doc = inserted.ops[0];
this.set('_id', doc._id);
yield* this._runHooks('after', 'create', options);
return this;
self.set('_id', doc._id);
})
.then(function () {
return self._runHooks('after', 'create', options);
})
.return(this);
};

@@ -567,4 +610,5 @@

Model.prototype.update = function * (options) {
let collection = this._collection();
Model.prototype.update = function (options) {
let self = this;
let attrs = this.attributes;

@@ -574,7 +618,13 @@

yield* this._runHooks('before', 'update', options);
yield collection.update({ _id: attrs._id }, attrs);
yield* this._runHooks('after', 'update', options);
return this;
return this._collection()
.tap(function () {
return self._runHooks('before', 'update', options);
})
.then(function (collection) {
return collection.update({ _id: attrs._id }, attrs);
})
.then(function () {
return self._runHooks('after', 'update', options);
})
.return(this);
};

@@ -589,10 +639,16 @@

Model.prototype.remove = function * (options) {
let collection = this._collection();
Model.prototype.remove = function (options) {
let self = this;
yield* this._runHooks('before', 'remove', options);
yield collection.remove({ _id: this.get('_id') });
yield* this._runHooks('after', 'remove', options);
return this;
return this._collection()
.tap(function () {
return self._runHooks('before', 'remove', options);
})
.then(function (collection) {
return collection.remove({ _id: self.get('_id') });
})
.then(function () {
return self._runHooks('after', 'remove', options);
})
.return(this);
};

@@ -609,3 +665,5 @@

Model.prototype.inc = function * (props, options) {
Model.prototype.inc = function (props, options) {
let self = this;
let id = this.get('_id');

@@ -617,29 +675,28 @@

let collection = this._collection();
return this._collection()
.tap(function () {
return self._runHooks('before', 'update', options);
})
.then(function (collection) {
return collection.update({ _id: id }, { '$inc': props });
})
.then(function () {
// perform increment locally
// to prevent the need to refresh
// the model from a database
Object.keys(props).forEach(function (key) {
// get current value
let value = self.get(key);
yield* this._runHooks('before', 'update', options);
// perform increment
value += props[key];
yield collection.update({ _id: id }, {
'$inc': props
});
// perform increment locally
// to prevent the need to refresh
// the model from a database
let self = this;
Object.keys(props).forEach(function (key) {
// get current value
let value = self.get(key);
// perform increment
value += props[key];
// save
self.set(key, value);
});
yield* this._runHooks('after', 'update', options);
return this;
// save
self.set(key, value);
});
})
.then(function () {
return self._runHooks('after', 'update', options);
})
.return(this);
};

@@ -658,3 +715,5 @@

// use it, otherwise use the default
return this.prototype.db ? this.prototype.db() : Mongorito.db;
let db = this.prototype.db ? this.prototype.db() : Mongorito._connection;
return Promise.resolve(db);
};

@@ -670,17 +729,21 @@

Model._collection = function () {
if (is.string(this.prototype.collection)) {
return Mongorito._collection(this._db(), this.prototype.collection);
}
let self = this;
// get collection name
// from the "collection" property
// or generate the default one
let defaultName = pluralize(this.name).toLowerCase();
let name = result(this.prototype, 'collection', defaultName);
return this._db().then(function (db) {
if (is.string(self.prototype.collection)) {
return Mongorito._collection(db, self.prototype.collection);
}
// save collection name
// to avoid the same check in future
this.prototype.collection = name;
// get collection name
// from the "collection" property
// or generate the default one
let defaultName = pluralize(self.name).toLowerCase();
let name = result(self.prototype, 'collection', defaultName);
return Mongorito._collection(this._db(), name);
// save collection name
// to avoid the same check in future
self.prototype.collection = name;
return Mongorito._collection(db, name);
});
};

@@ -732,6 +795,7 @@

Model.findOne = function * (query) {
let docs = yield* this.find(query);
return docs[0];
Model.findOne = function (query) {
return this.find(query)
.then(function (docs) {
return docs[0];
});
};

@@ -772,3 +836,5 @@

Model.drop = function () {
return this._collection().drop();
return this._collection().then(function (collection) {
return collection.drop();
});
};

@@ -784,6 +850,8 @@

Model.index = function * () {
let collection = this._collection();
Model.index = function () {
let args = Array.prototype.slice.call(arguments);
return yield collection.ensureIndex.apply(collection, arguments);
return this._collection().then(function (collection) {
return collection.ensureIndex.apply(collection, args);
});
};

@@ -799,11 +867,16 @@

Model.indexes = function * () {
let collection = this._collection();
Model.indexes = function () {
let args = Array.prototype.slice.call(arguments);
let cursor = collection.listIndexes.apply(collection, arguments);
let indexes = yield cursor.toArray();
return this._collection().then(function (collection) {
let cursor = collection.listIndexes.apply(collection, args);
cursor.close();
return cursor
.toArray()
.then(function (indexes) {
cursor.close();
return indexes;
return indexes;
});
});
};

@@ -842,3 +915,2 @@

let query = new Query(this._collection(), this);
query[method].apply(query, arguments);

@@ -845,0 +917,0 @@

@@ -8,5 +8,4 @@ 'use strict';

const toObjectId = require('../util/to-objectid');
const Promise = require('bluebird');
const assign = require('object-assign');
const each = require('array-generators').forEach;
const map = require('array-generators').map;
const is = require('is_js');

@@ -111,3 +110,3 @@

if (is.array(key)) {
if (Array.isArray(key)) {
let fields = key;

@@ -148,3 +147,3 @@

if (is.array(key)) {
if (Array.isArray(key)) {
let fields = key;

@@ -304,6 +303,10 @@

Query.prototype.count = function * (query) {
Query.prototype.count = function (query) {
let self = this;
this.where(query);
return yield this.collection.count(this.query);
return this.collection.then(function (collection) {
return collection.count(self.query);
});
};

@@ -319,7 +322,8 @@

Query.prototype.find = function * (query, options) {
Query.prototype.find = function (query, options) {
let Model = this.model;
let self = this;
this.where(query);
let Model = this.model;
// query options

@@ -337,29 +341,36 @@ options = assign({}, this.options, options);

// find
let cursor = this.collection.find(this.query, options);
let docs = yield cursor.toArray();
return this.collection
.then(function (collection) {
let cursor = collection.find(self.query, options);
// close cursor
cursor.close();
return cursor
.toArray()
.then(function (docs) {
cursor.close();
docs = yield map(docs, function * (doc) {
yield each(populate, function * (key) {
let childModel = options.populate[key];
return docs;
});
})
.map(function (doc) {
return Promise.each(populate, function (key) {
let childModel = options.populate[key];
let value = doc[key];
let value = doc[key];
if (Array.isArray(value)) {
value = Promise.map(value, function (id) {
return childModel.findById(id);
});
} else {
value = childModel.findById(value);
}
if (is.array(value)) {
value = value.map(childModel.findById, childModel);
} else {
value = childModel.findById(value);
}
doc[key] = yield value;
return value.then(function (value) {
doc[key] = value;
});
}).then(function () {
return new Model(doc, {
populate: options.populate
});
});
});
return new Model(doc, {
populate: options.populate
});
});
return docs;
};

@@ -375,6 +386,7 @@

Query.prototype.findOne = function * (query) {
let docs = yield this.find(query);
return docs[0];
Query.prototype.findOne = function (query) {
return this.find(query)
.then(function (docs) {
return docs[0];
});
};

@@ -402,6 +414,10 @@

Query.prototype.remove = function * (query) {
Query.prototype.remove = function (query) {
let self = this;
this.where(query);
return yield this.collection.remove(this.query, this.options);
return this.collection.then(function (collection) {
return collection.remove(self.query, self.options);
});
};

@@ -448,3 +464,3 @@

Query.prototype[method] = function () {
let args = is.array(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments);
let args = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments);
let operator = '$' + method;

@@ -451,0 +467,0 @@

{
"name": "mongorito",
"version": "1.4.6",
"version": "2.0.0",
"description": "ES6 generator-based MongoDB ODM. It rocks.",
"author": "Vadim Demedes <vdemedes@gmail.com>",
"dependencies": {
"array-generators": "^1.1.0",
"bluebird": "^3.3.1",
"class-extend": "^0.1.2",
"clone": "^1.0.2",
"get-value": "^2.0.2",
"co": "^4.6.0",
"get-value": "^2.0.3",
"is-generator-fn": "^1.0.0",
"is_js": "^0.7.4",
"koa-compose": "^2.3.0",
"lodash.result": "^3.1.2",
"lodash.result": "^4.2.0",
"mongodb": "^2.0.48",

@@ -20,10 +21,9 @@ "object-assign": "^4.0.1",

"devDependencies": {
"ava": "^0.8.0",
"ava": "^0.12.0",
"chance": "^0.8.0",
"coveralls": "^2.11.4",
"eslint-config-vdemedes": "*",
"eslint-config-vdemedes": "^1.0.2",
"nyc": "^5.0.0",
"xo": "^0.11.2"
"xo": "^0.12.1"
},
"main": "index.js",
"files": [

@@ -35,3 +35,3 @@ "index.js",

"scripts": {
"test": "xo && nyc ava --serial test/test.js",
"test": "xo && nyc ava --serial",
"coveralls": "nyc report --reporter=text-lcov | coveralls"

@@ -52,7 +52,15 @@ },

"extends": "vdemedes",
"esnext": true,
"env": [
"node",
"mocha"
]
],
"ignore": [
"examples/*.js"
],
"rules": {
"prefer-arrow-callback": 0,
"prefer-spread": 0
}
}
}
# Mongorito
[![Build Status](https://travis-ci.org/vdemedes/mongorito.svg?branch=master)](https://travis-ci.org/vdemedes/mongorito) [![Coverage Status](https://coveralls.io/repos/vdemedes/mongorito/badge.svg?branch=master&service=github)](https://coveralls.io/github/vdemedes/mongorito?branch=master)
[![Build Status](https://travis-ci.org/vdemedes/mongorito.svg?branch=master)](https://travis-ci.org/vdemedes/mongorito)
[![Coverage Status](https://coveralls.io/repos/vdemedes/mongorito/badge.svg?branch=master&service=github)](https://coveralls.io/github/vdemedes/mongorito?branch=master)
Awesome ES6 generator-based MongoDB ODM for Node.js v4.x (or newer).
Just take a look on its pretty models and beautiful API.
Awesome MongoDB ODM for Node.js apps.
Just take a look at its beautiful models and API.
Uses official [mongodb](https://www.npmjs.com/package/mongodb) driver under the hood.

@@ -17,8 +19,31 @@

## Quick overview
```js
const mongorito = require('mongorito');
const Model = mongorito.Model;
class Post extends Model {
}
mongorito.connect('localhost/blog');
let post = new Post({
title: 'Steve Angello rocks',
author: {
name: 'Emma'
}
});
post.save();
// post saved
```
## Features
- Based on ES6 generators, which means **no callbacks**
- Based on Promises, which means **no callbacks**
- Established API you've already used to
- Hooks (before:save, around:create, after:remove, etc)
- Very simple and easy-to-understand implementation
- Fully covered by tests

@@ -34,56 +59,227 @@ - Using this module results in a beautiful code

**Note**: In order for the following examples to work, you need to use [co](https://github.com/tj/co) to run generators.
## Usage
- [Connection](#connection)
- [Models](#models)
- [Attributes](#attributes)
- [Save & remove](#save--remove)
- [Queries](#queries)
### Connection
*Check out [connection](examples/connection.js) example.*
Here's how to connect to a `blog` database on `localhost`:
```js
const Mongorito = require('mongorito');
const Model = Mongorito.Model;
await mongorito.connect('localhost/blog');
```
// connect to localhost/blog
yield Mongorito.connect('localhost/blog');
To disconnect, use `mongorito.disconnect()`:
```js
await mongorito.disconnect();
```
// define model
### Models
Use classes to define models:
```js
const Model = mongorito.Model;
class Post extends Model {
}
```
This defines model `Post` with documents in `posts` collection.
To use a custom collection, add `collection()` method, which returns the name of the desired collection:
```js
class Post extends Model {
collection () {
return 'super_cool_posts';
}
}
```
Mongorito models can also be defined old-fashioned Backbone way:
// create and save new Post document
```js
const Post = Model.extend({
collection: 'posts'
});
```
**Note**: `collection` property has to be set.
### Attributes
*Check out [attributes](examples/attributes.js) example.*
To create a new instance of a model, simply use `new`:
```js
let post = new Post({
title: 'Node.js with --harmony rocks!',
body: 'Long post body',
author: {
name: 'John Doe'
}
title: 'Great title',
author: {
name: 'Emma'
}
});
```
yield post.save();
To retrieve a specific attribute (even a nested one):
```js
let title = post.get('title');
let author = post.get('author.name');
```
// update document
post.set('title', 'Post got a new title!');
post.set('author.name', 'Doe John');
All attributes can be retrieved at once using either `toJSON()` or `get()`:
yield post.save();
```js
let attrs = post.toJSON();
let attrs = post.get();
```
// find posts where body equals "Long post body"
let posts = yield Post.where('body', 'Long post body').find();
Set new values via `set()` method:
// find posts where author's name equals "John Doe"
posts = yield Post.where('author.name', 'John Doe').find();
```js
post.set('title', 'New title');
post.set('author.name', 'Rachel');
```
// Bonus: find posts where title starts with "Node"
posts = yield Post.where('title', /^node/i).find();
### Save & Remove
*Check out [manage](examples/manage.js) example.*
Use a `save()` method to create/update (Mongorito handles that for you) a model:
```js
let post = new Post();
await post.save(); // creates a new post
post.set('title', 'New title');
await post.save(); // updates an existing post
```
To remove a model from collection:
## API
```js
await post.remove();
```
Check out [Getting Started](http://mongorito.com/guides/getting-started) guide on [http://mongorito.com](http://mongorito.com).
There are more guides available to learn more.
You can also remove all models matching a certain criteria:
```js
await Post.remove({ title: 'New title' });
```
### Queries
#### Find all
To fetch all models `find()` or `all()` can be used (they're identical):
```js
Post.find();
Post.all();
```
#### Find one
```js
Post.findOne({ title: 'New title' });
```
#### Find by ID
```js
Post.findById('56c9e0922cc9215110ab26dc');
```
#### Find where value equals
```js
Post.where('title', 'New title').find();
Post.where('author.name', 'Emma').find();
```
#### Find where value matches a regular expression
```js
Post.where('title', /something/i).find();
```
#### Find where attribute exists
```js
Post.exists('title').find();
```
#### Find where value is less/greater than
```js
Post.where('comments_number').lt(5).find(); // less than 5
Post.where('comments_number').lte(5).find(); // less than or equal 5
Post.where('comments_number').gt(5).find(); // greater than 5
Post.where('comments_number').gte(5).find(); // greater than or equal 5
```
### Find where value is one of
```js
Post.in('comments_number', [4, 8]).find();
```
#### Find using OR
Find all models where `title` equals either "First" or "Second":
```js
Post.or({ title: 'First' }, { title: 'Second' }).find();
```
#### Limit results
```js
Post.limit(10).find();
```
#### Skip results
Skip first N results:
```js
Post.skip(4).find();
```
#### Sort results
```js
// descending
Post.sort('comments_number', -1);
// ascending
Post.sort('comments_number', 1);
```
#### Count results
Count all documents in collection:
```js
Post.count();
```
Count all documents matching a certain criteria:
```js
Post.count({ awesome: true });
```
## Tests

@@ -90,0 +286,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc