Comparing version 2.2.0 to 3.0.0
18
index.js
@@ -1,1 +0,17 @@ | ||
module.exports = require('./lib/mongorito'); | ||
'use strict'; | ||
const mongodb = require('mongodb'); | ||
const Database = require('./lib/database'); | ||
const Model = require('./lib/model'); | ||
const ActionTypes = require('./lib/action-types'); | ||
const x = module.exports = Database; | ||
x.Database = Database; | ||
x.Model = Model; | ||
x.ActionTypes = ActionTypes; | ||
x.Timestamp = mongodb.Timestamp; | ||
x.ObjectId = mongodb.ObjectId; | ||
x.MinKey = mongodb.MinKey; | ||
x.MaxKey = mongodb.MaxKey; | ||
x.DBRef = mongodb.DBRef; | ||
x.Long = mongodb.Long; |
563
lib/query.js
'use strict'; | ||
/** | ||
* Dependencies | ||
*/ | ||
const isObject = require('is-plain-obj'); | ||
const queryMethods = require('./query-methods'); | ||
const toObjectId = require('../util/to-objectid'); | ||
const Promise = require('bluebird'); | ||
const assign = require('object-assign'); | ||
const is = require('is_js'); | ||
/** | ||
* Query | ||
*/ | ||
function Query (collection, model, key) { | ||
this.collection = collection; | ||
this.model = model; | ||
this.query = {}; | ||
this.options = { | ||
populate: {}, | ||
sort: {}, | ||
fields: {} | ||
}; | ||
this.lastKey = key; | ||
} | ||
/** | ||
* Set "where" condition | ||
* | ||
* @param {String} key - key | ||
* @param {Mixed} value - value | ||
* @api public | ||
*/ | ||
Query.prototype.where = function (key, value) { | ||
// if object was passed instead of key-value pair | ||
// iterate over that object and call .where(key, value) | ||
if (is.object(key)) { | ||
let conditions = key; | ||
let keys = Object.keys(conditions); | ||
let self = this; | ||
keys.forEach(function (key) { | ||
self.where(key, conditions[key]); | ||
}); | ||
class Query { | ||
constructor(modelClass) { | ||
this.Model = modelClass; | ||
this.query = []; | ||
} | ||
if (is.string(key)) { | ||
// if only one argument was supplied | ||
// save the key in this.lastKey | ||
// for future methods, like .equals() | ||
if (is.undefined(value)) { | ||
this.lastKey = key; | ||
return this; | ||
find(query = {}) { | ||
if (typeof query !== 'undefined' && typeof query !== 'object') { | ||
throw new TypeError(`Expected \`query\` to be object or undefined, got ${typeof query}`); | ||
} | ||
// if value is a regular expression | ||
// use $regex modifier | ||
if (is.regexp(value)) { | ||
value = { $regex: value }; | ||
} | ||
this.where(query); | ||
if (is.array(value)) { | ||
value = { $in: value }; | ||
} | ||
return this.Model.query('find', this.query) | ||
.then(documents => { | ||
return documents.map(doc => new this.Model(doc)); | ||
}); | ||
} | ||
this.query[key] = value; | ||
then(...args) { | ||
return this.find(...args); | ||
} | ||
return this; | ||
}; | ||
findOne(query = {}) { | ||
if (typeof query !== 'undefined' && typeof query !== 'object') { | ||
throw new TypeError(`Expected \`query\` to be object or undefined, got ${typeof query}`); | ||
} | ||
this.where(query); | ||
/** | ||
* Match documents using $elemMatch | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api public | ||
*/ | ||
Query.prototype.matches = function (key, value) { | ||
if (this.lastKey) { | ||
value = key; | ||
key = this.lastKey; | ||
this.lastKey = null; | ||
return this.Model.query('findOne', this.query) | ||
.then(doc => doc ? new this.Model(doc) : null); | ||
} | ||
this.query[key] = { $elemMatch: value }; | ||
findById(id) { | ||
if (typeof id !== 'object' && typeof id !== 'string') { | ||
throw new TypeError(`Expected \`id\` to be object or string, got ${typeof id}`); | ||
} | ||
return this; | ||
}; | ||
this.where('_id', id); | ||
Query.prototype.match = function () { | ||
return this.matches.apply(this, arguments); | ||
}; | ||
/** | ||
* Include fields in a result | ||
* | ||
* @param {String} key | ||
* @param {Mixed} value | ||
* @api public | ||
*/ | ||
Query.prototype.include = function (key, value) { | ||
let self = this; | ||
if (Array.isArray(key)) { | ||
let fields = key; | ||
fields.forEach(function (key) { | ||
self.include(key); | ||
}); | ||
} else if (is.object(key)) { | ||
let fields = key; | ||
let keys = Object.keys(fields); | ||
keys.forEach(function (key) { | ||
self.include(key, fields[key]); | ||
}); | ||
return this.Model.query('findOne', this.query) | ||
.then(doc => doc ? new this.Model(doc) : null); | ||
} | ||
if (is.string(key)) { | ||
this.options.fields[key] = value === undefined ? 1 : value; | ||
} | ||
remove(query = {}) { | ||
if (typeof query !== 'undefined' && typeof query !== 'object') { | ||
throw new TypeError(`Expected \`query\` to be object or undefined, got ${typeof query}`); | ||
} | ||
return this; | ||
}; | ||
this.where(query); | ||
/** | ||
* Exclude fields from result | ||
* | ||
* @param {String} key | ||
* @param {String} value | ||
* @api public | ||
*/ | ||
Query.prototype.exclude = function (key, value) { | ||
let self = this; | ||
if (Array.isArray(key)) { | ||
let fields = key; | ||
fields.forEach(function (key) { | ||
self.exclude(key); | ||
}); | ||
} else if (is.object(key)) { | ||
let fields = key; | ||
let keys = Object.keys(fields); | ||
keys.forEach(function (key) { | ||
self.exclude(key, fields[key]); | ||
}); | ||
return this.Model.query('remove', this.query); | ||
} | ||
if (is.string(key)) { | ||
this.options.fields[key] = value === undefined ? 0 : value; | ||
} | ||
return this; | ||
}; | ||
/** | ||
* Search using text index | ||
* | ||
* @param {String} text | ||
* @api public | ||
*/ | ||
Query.prototype.search = function (text) { | ||
this.where({ | ||
'$text': { | ||
'$search': text | ||
count(query = {}) { | ||
if (typeof query !== 'undefined' && typeof query !== 'object') { | ||
throw new TypeError(`Expected \`query\` to be object or undefined, got ${typeof query}`); | ||
} | ||
}); | ||
return this; | ||
}; | ||
this.where(query); | ||
/** | ||
* Get distinct | ||
* | ||
* @param {String} field for distinct | ||
* @param {Object} query - query to filter the results | ||
* @see http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#distinct | ||
* @api public | ||
*/ | ||
Query.prototype.distinct = function (field, query) { | ||
let self = this; | ||
this.where(query); | ||
return this.collection.then(function (collection) { | ||
return collection.distinct(field, self.query); | ||
}); | ||
}; | ||
/** | ||
* Aggregation query | ||
* | ||
* @param {String} pipeline aggregation pipeline | ||
* @param {Object} options - Options to be passed to aggregation pipeline | ||
* @see http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#aggregate | ||
* @api public | ||
*/ | ||
Query.prototype.aggregate = function (pipeline) { | ||
return this.collection.then(function (collection) { | ||
let cursor = collection.aggregate(pipeline, { cursor: { batchSize: 1 } }); | ||
return cursor | ||
.toArray() | ||
.then(function (docs) { | ||
cursor.close(); | ||
return docs; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Set query limit | ||
* | ||
* @param {Number} limit - limit number | ||
* @api public | ||
*/ | ||
Query.prototype.limit = function (limit) { | ||
this.options.limit = limit; | ||
return this; | ||
}; | ||
/** | ||
* Set query skip | ||
* | ||
* @param {Number} skip - skip number | ||
* @api public | ||
*/ | ||
Query.prototype.skip = function (skip) { | ||
this.options.skip = skip; | ||
return this; | ||
}; | ||
/** | ||
* Sort query results | ||
* | ||
* @param {Object} sort - sort params | ||
* @see http://mongodb.github.io/node-mongodb-native/2.0/api/Cursor.html#sort | ||
* @api public | ||
*/ | ||
Query.prototype.sort = function (key, value) { | ||
if (is.object(key)) { | ||
assign(this.options.sort, key); | ||
return this.Model.query('count', this.query); | ||
} | ||
if (is.string(key) && value) { | ||
this.options.sort[key] = value; | ||
} | ||
include(field, value = 1) { | ||
if (!Array.isArray(field) && typeof field !== 'object' && typeof field !== 'string') { | ||
throw new TypeError(`Expected \`field\` to be array, object or string, got ${typeof field}`); | ||
} | ||
return this; | ||
}; | ||
if (typeof value !== 'number') { | ||
throw new TypeError(`Expected \`value\` to be number, got ${typeof value}`); | ||
} | ||
if (Array.isArray(field)) { | ||
field.forEach(field => this.include(field)); | ||
return this; | ||
} | ||
/** | ||
* Same as .where(), only less flexible | ||
* | ||
* @param {String} key - key | ||
* @param {Mixed} value - value | ||
* @api public | ||
*/ | ||
const select = isObject(field) ? field : {[field]: value}; | ||
this.query.push(['select', select]); | ||
Query.prototype.equals = function (value) { | ||
let key = this.lastKey; | ||
this.lastKey = null; | ||
this.query[key] = value; | ||
return this; | ||
}; | ||
/** | ||
* Set property that must or mustn't exist in resulting docs | ||
* | ||
* @param {String} key - key | ||
* @param {Boolean} exists - exists or not | ||
* @api public | ||
*/ | ||
Query.prototype.exists = function (key, exists) { | ||
if (this.lastKey) { | ||
exists = key; | ||
key = this.lastKey; | ||
this.lastKey = null; | ||
return this; | ||
} | ||
this.query[key] = { $exists: (exists === undefined ? true : exists) }; | ||
exclude(field, value = 0) { | ||
if (!Array.isArray(field) && typeof field !== 'object' && typeof field !== 'string') { | ||
throw new TypeError(`Expected \`field\` to be array, object or string, got ${typeof field}`); | ||
} | ||
return this; | ||
}; | ||
if (typeof value !== 'number') { | ||
throw new TypeError(`Expected \`value\` to be number, got ${typeof value}`); | ||
} | ||
/** | ||
* Query population | ||
* | ||
* @param {String} key - key | ||
* @param {Model} model - model to populate with | ||
* @see http://mongorito.com/guides/query-population/ | ||
* @api public | ||
*/ | ||
Query.prototype.populate = function (key, model) { | ||
this.options.populate[key] = model; | ||
return this; | ||
}; | ||
/** | ||
* Count documents | ||
* | ||
* @param {Object} query - find conditions, same as this.where() | ||
* @api public | ||
*/ | ||
Query.prototype.count = function (query) { | ||
let self = this; | ||
this.where(query); | ||
return this.collection.then(function (collection) { | ||
return collection.count(self.query); | ||
}); | ||
}; | ||
/** | ||
* Find documents | ||
* | ||
* @param {Object} query - find conditions, same as this.where() | ||
* @api public | ||
*/ | ||
Query.prototype.find = function (query, options) { | ||
let Model = this.model; | ||
query = assign({}, this.query, query); | ||
// query options | ||
options = assign({}, this.options, options); | ||
// fields to populate | ||
let populate = Object.keys(options.populate); | ||
// ensure _id is ObjectId | ||
if (query._id) { | ||
if (is.object(query._id)) { | ||
if (query._id.$in) { | ||
let convertedIds = []; | ||
query._id.$in.forEach(function (id) { | ||
convertedIds.push(toObjectId(id)); | ||
}); | ||
query._id.$in = convertedIds; | ||
} | ||
} else { | ||
query._id = toObjectId(query._id); | ||
if (Array.isArray(field)) { | ||
field.forEach(field => this.exclude(field)); | ||
return this; | ||
} | ||
} | ||
// find | ||
return this.collection | ||
.then(function (collection) { | ||
let cursor = collection.find(query, options); | ||
const select = isObject(field) ? field : {[field]: value}; | ||
this.query.push(['select', select]); | ||
return cursor | ||
.toArray() | ||
.then(function (docs) { | ||
cursor.close(); | ||
return docs; | ||
}); | ||
}) | ||
.map(function (doc) { | ||
return Promise.each(populate, function (key) { | ||
let childModel = options.populate[key]; | ||
let idsArray = doc[key]; | ||
let promise = childModel.findById(idsArray); | ||
return promise.then(function (subdocs) { | ||
// reorder the received documents as ordered in the IDs Array | ||
let orderedDocuments = idsArray.slice(); | ||
subdocs.map(doc => { | ||
let id = toObjectId(doc.get('_id')); | ||
for (let index in idsArray) { | ||
if (idsArray[index].equals && idsArray[index].equals(id)) { | ||
orderedDocuments[index] = doc; | ||
} | ||
} | ||
}); | ||
doc[key] = orderedDocuments; | ||
}); | ||
}).then(function () { | ||
return new Model(doc, { | ||
populate: options.populate | ||
}); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Find one document | ||
* | ||
* @param {Object} query - find conditions, same as this.where() | ||
* @api public | ||
*/ | ||
Query.prototype.findOne = function (query) { | ||
return this.find(query) | ||
.then(function (docs) { | ||
return docs[0]; | ||
}); | ||
}; | ||
/** | ||
* Find documents by ID | ||
* | ||
* @param {ObjectID} id - document id or Array of document ids | ||
* @api public | ||
*/ | ||
Query.prototype.findById = function (id) { | ||
if (Array.isArray(id)) { | ||
let ids = id.map(id => toObjectId(id)); | ||
return this.find({ _id: { $in: ids } }); | ||
return this; | ||
} | ||
return this.findOne({ _id: toObjectId(id) }); | ||
}; | ||
sort(field, value = 'desc') { | ||
if (!Array.isArray(field) && typeof field !== 'object' && typeof field !== 'string') { | ||
throw new TypeError(`Expected \`field\` to be array, object or string, got ${typeof field}`); | ||
} | ||
/** | ||
* Remove documents | ||
* | ||
* @param {Object} query - remove conditions, same as this.where() | ||
* @api public | ||
*/ | ||
if (typeof value !== 'string' && typeof value !== 'number') { | ||
throw new TypeError(`Expected \`value\` to be string or number, got ${typeof value}`); | ||
} | ||
Query.prototype.remove = function (query) { | ||
let self = this; | ||
this.where(query); | ||
return this.collection.then(function (collection) { | ||
return collection.remove(self.query, self.options); | ||
}); | ||
}; | ||
// Setting up functions that | ||
// have the same implementation | ||
const methods = [ | ||
'lt', | ||
'lte', | ||
'gt', | ||
'gte', | ||
'in', | ||
'nin', | ||
'ne' | ||
]; | ||
methods.forEach(function (method) { | ||
Query.prototype[method] = function (key, value) { | ||
// if .where() was called with one argument | ||
// key was already set in this.lastKey | ||
if (this.lastKey) { | ||
value = key; | ||
key = this.lastKey; | ||
this.lastKey = null; | ||
if (Array.isArray(field)) { | ||
field.forEach(field => this.sort(field)); | ||
return this; | ||
} | ||
let operator = '$' + method; | ||
let hasValue = value !== undefined; | ||
const sort = isObject(field) ? field : {[field]: value}; | ||
this.query.push(['sort', sort]); | ||
if (hasValue) { | ||
this.query[key] = {}; | ||
this.query[key][operator] = value; | ||
} else { | ||
this.query[operator] = key; | ||
} | ||
return this; | ||
}; | ||
}); | ||
} | ||
} | ||
// or, nor and and share the same imlpementation | ||
['or', 'nor', 'and'].forEach(function (method) { | ||
Query.prototype[method] = function () { | ||
let args = Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.call(arguments); | ||
let operator = '$' + method; | ||
this.query[operator] = args; | ||
queryMethods.forEach(name => { | ||
Query.prototype[name] = function (...args) { | ||
this.query.push([name, args]); | ||
return this; | ||
@@ -525,7 +139,2 @@ }; | ||
/** | ||
* Expose Query | ||
*/ | ||
module.exports = Query; |
{ | ||
"name": "mongorito", | ||
"version": "2.2.0", | ||
"description": "ES6 generator-based MongoDB ODM. It rocks.", | ||
"version": "3.0.0", | ||
"description": "ES6 generator-based MongoDB ODM.", | ||
"author": "Vadim Demedes <vdemedes@gmail.com>", | ||
"dependencies": { | ||
"bluebird": "^3.3.1", | ||
"class-extend": "^0.1.2", | ||
"clone": "^1.0.2", | ||
"co": "^4.6.0", | ||
"get-value": "^2.0.3", | ||
"is-generator-fn": "^1.0.0", | ||
"is_js": "^0.7.4", | ||
"lodash.result": "^4.2.0", | ||
"mongodb": "^2.0.48", | ||
"object-assign": "^4.0.1", | ||
"pluralize": "^1.2.1", | ||
"set-value": "^0.2.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^0.12.0", | ||
"chance": "^0.8.0", | ||
"coveralls": "^2.11.4", | ||
"eslint-config-vdemedes": "^1.0.2", | ||
"nyc": "^5.0.0", | ||
"xo": "^0.12.1" | ||
}, | ||
"repository": "vdemedes/mongorito", | ||
"keywords": [ | ||
"mongo", | ||
"mongodb", | ||
"odm" | ||
], | ||
"license": "MIT", | ||
"files": [ | ||
@@ -37,29 +22,33 @@ "index.js", | ||
}, | ||
"repository": "vdemedes/mongorito", | ||
"keywords": [ | ||
"mongo", | ||
"mongodb", | ||
"co-mongo", | ||
"co-mongodb", | ||
"orm", | ||
"odm", | ||
"es6" | ||
], | ||
"license": "MIT", | ||
"engines": { | ||
"node": ">= 6" | ||
}, | ||
"dependencies": { | ||
"arrify": "^1.0.1", | ||
"dot-prop": "^4.0.0", | ||
"is-plain-obj": "^1.1.0", | ||
"lodash.merge": "^4.6.0", | ||
"lodash.omit": "^4.5.0", | ||
"lodash.result": "^4.5.2", | ||
"map-obj": "^2.0.0", | ||
"mongodb": "^2.2.9", | ||
"mquery": "^1.11.0", | ||
"pluralize": "^3.0.0", | ||
"redux": "^3.6.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^0.16.0", | ||
"chance": "^1.0.4", | ||
"coveralls": "^2.11.12", | ||
"nyc": "^8.1.0", | ||
"sinon": "^2.1.0", | ||
"xo": "^0.17.0" | ||
}, | ||
"xo": { | ||
"extends": "vdemedes", | ||
"esnext": true, | ||
"env": [ | ||
"node", | ||
"mocha" | ||
], | ||
"ignore": [ | ||
"examples/*.js" | ||
], | ||
"rules": { | ||
"prefer-arrow-callback": 0, | ||
"prefer-spread": 0, | ||
"no-use-extend-native/no-use-extend-native": 0 | ||
} | ||
"env": "node" | ||
}, | ||
"ava": { | ||
"serial": true, | ||
"failFast": true | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
35430
11
26
547
682
1
+ Addedarrify@^1.0.1
+ Addeddot-prop@^4.0.0
+ Addedis-plain-obj@^1.1.0
+ Addedlodash.merge@^4.6.0
+ Addedlodash.omit@^4.5.0
+ Addedmap-obj@^2.0.0
+ Addedmquery@^1.11.0
+ Addedredux@^3.6.0
+ Addedarrify@1.0.1(transitive)
+ Addedbluebird@2.10.2(transitive)
+ Addeddebug@2.2.0(transitive)
+ Addeddot-prop@4.2.1(transitive)
+ Addedis-obj@1.0.1(transitive)
+ Addedis-plain-obj@1.1.0(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlodash-es@4.17.21(transitive)
+ Addedlodash.merge@4.6.2(transitive)
+ Addedlodash.omit@4.5.0(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedmap-obj@2.0.0(transitive)
+ Addedmquery@1.11.0(transitive)
+ Addedms@0.7.1(transitive)
+ Addedpluralize@3.1.0(transitive)
+ Addedredux@3.7.2(transitive)
+ Addedregexp-clone@0.0.1(transitive)
+ Addedsliced@0.0.5(transitive)
+ Addedsymbol-observable@1.2.0(transitive)
- Removedbluebird@^3.3.1
- Removedclass-extend@^0.1.2
- Removedclone@^1.0.2
- Removedco@^4.6.0
- Removedget-value@^2.0.3
- Removedis-generator-fn@^1.0.0
- Removedis_js@^0.7.4
- Removedobject-assign@^4.0.1
- Removedset-value@^0.2.0
- Removedbluebird@3.7.2(transitive)
- Removedclass-extend@0.1.2(transitive)
- Removedclone@1.0.4(transitive)
- Removedco@4.6.0(transitive)
- Removedget-value@2.0.6(transitive)
- Removedis-generator-fn@1.0.0(transitive)
- Removedis_js@0.7.6(transitive)
- Removedisobject@1.0.2(transitive)
- Removednoncharacters@1.1.0(transitive)
- Removedobject-assign@2.1.14.1.1(transitive)
- Removedpluralize@1.2.1(transitive)
- Removedset-value@0.2.0(transitive)
Updatedlodash.result@^4.5.2
Updatedmongodb@^2.2.9
Updatedpluralize@^3.0.0