Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

p-odm

Package Overview
Dependencies
Maintainers
1
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

p-odm - npm Package Compare versions

Comparing version 3.0.2 to 3.0.3

lib/helpers/cache.js

932

lib/model.js

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

* @author <a href="mailto:plopes@roughcookie.com">Paulo Lopes</a>
* @version 2.0
* @version 3.0
*/
var mongodb = require('mongodb');
var baseModel = require('./protos/freemodel');
var schemaModel = require('./protos/schemamodel');
var embedSchemaModel = require('./protos/embedschemamodel');
var ObjectID = mongodb.BSONPure.ObjectID;
var objectIdRegExp = /^[0-9a-fA-F]{24}$/;
/**
* Extracts one option from the object and returns it.
* @private
*/
function extractOption(name, options, defaultValue) {
var option = defaultValue;
if (options) {
if (options.hasOwnProperty(name)) {
option = options[name];
delete options[name];
}
}
return option;
}
function extend(obj, proto, deep) {
if (obj !== undefined && obj !== null) {
var key;
var keys = Object.keys(proto);
var kl = keys.length;
while (kl--) {
key = keys[kl];
if (typeof proto[key] === 'object') {
if (deep && obj.hasOwnProperty(key)) {
// if type is object and obj has it too, recurse
extend(obj[key], proto[key], deep);
}
} else {
if (typeof obj === 'object') {
if (obj[key] === undefined) {
// set it if not defined in the object
if (Array.isArray(obj)) {
var i = obj.length;
while (i--) {
extend(obj[i], proto, deep);
}
} else {
Object.defineProperty(obj, key, {value: proto[key]});
}
}
}
}
}
}
return obj;
}
/**
* Creates a new Document Model class

@@ -81,876 +32,17 @@ *

var hasSchema = false;
if (schemaDef !== undefined && schemaDef !== null) {
hasSchema = true;
}
/**
* @name Model
* @class Document Model Customized class for a mongodb document schema.
* @param {Object} [json] if provided will update the current instance with the json properties
*/
var Model = function (json) {
if (hasSchema) {
var filterUndefined = function (target, source) {
if (source !== undefined && source !== null) {
var key, value;
// update new ones
for (key in source) {
if (source.hasOwnProperty(key)) {
value = source[key];
if (value !== undefined) {
if (typeof value === 'object') {
if (value === null || value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof Date || value instanceof ObjectID) {
target[key] = value;
} else {
if (target[key] === undefined) {
if (Array.isArray(value)) {
target[key] = [];
} else {
target[key] = {};
}
}
filterUndefined(target[key], value);
}
} else {
target[key] = value;
}
}
}
}
}
};
filterUndefined(this, json);
}
// inheritance
if (Model.$embeds !== undefined) {
extend(this, Model.$embeds, true);
}
if (Model.onLoad !== undefined) {
Model.onLoad(this);
}
};
var odm = this;
if (schemaDef !== undefined && schemaDef !== null) {
/**
* schema for embedded objects
*
* @memberOf Model
*/
Object.defineProperty(Model, '$schema', {value: odm.createSchema(schemaDef)});
}
/**
* Keep track of embeds (it is similar to prototype)
*/
Object.defineProperty(Model, '$embeds', {value: {}});
/**
* Finds one element of this collection by the given query.
*
* @memberOf Model
* @param {Object} query Query object as in mongodb documentation
* @param {Object|Function} [fields] filter fields
* @param {Object|Function} [options] Query options, such as skip, limit, etc
* @param {Function} callback Callback function (error, model) with the result of the operation
*/
Model.findOne = function (query, fields, options, callback) {
var hasFields = true;
if (callback === undefined) {
if (options === undefined) {
callback = fields;
options = {};
fields = {};
hasFields = false;
} else {
callback = options;
options = fields;
fields = {};
hasFields = false;
}
}
if (!mongoCollection) {
return callback('Cannot findOne on embedded model');
}
var wantExtend = extractOption('extend', options, true);
var wantExtendDeep = extractOption('deepExtend', options, true);
var pluck = extractOption('pluck', options);
if (pluck !== undefined) {
// state that we only care about the plucked field
fields[pluck] = true;
hasFields = true;
}
odm.findOne(mongoCollection, query, fields, options, function (err, documentLoaded) {
if (err) {
return callback(err);
}
if (documentLoaded === null) {
return callback(null, null);
}
if (hasFields) {
if (pluck !== undefined) {
documentLoaded = documentLoaded[pluck];
}
} else {
if (hasSchema && (wantExtend || wantExtendDeep)) {
extend(documentLoaded, Model.prototype, wantExtendDeep);
if (Model.$embeds !== undefined) {
extend(documentLoaded, Model.$embeds, wantExtendDeep);
}
if (Model.onLoad !== undefined) {
Model.onLoad(documentLoaded);
}
}
}
callback(null, documentLoaded);
});
};
/**
* Finds one element of this collection given its Id.
*
* @memberOf Model
* @param {ObjectID|String} id Either a ObjectId instance or, the function will try to cast it to ObjectId.
* @param {Object|Function} [fields] filter fields
* @param {Object|Function} [options] Query options, such as skip, limit, etc
* @param {Function} callback Callback function (error, model) with the result of the operation
*/
Model.findById = function (id, fields, options, callback) {
var hasFields = true;
if (callback === undefined) {
if (options === undefined) {
callback = fields;
options = {};
fields = {};
hasFields = false;
} else {
callback = options;
options = fields;
fields = {};
hasFields = false;
}
}
if (!mongoCollection) {
return callback('Cannot findById on embedded model');
}
var wantExtend = extractOption('extend', options, true);
var wantExtendDeep = extractOption('deepExtend', options, true);
var pluck = extractOption('pluck', options);
if (pluck !== undefined) {
// state that we only care about the plucked field
fields[pluck] = true;
hasFields = true;
}
if (id === undefined) {
return callback('undefined id');
}
var _id;
var includeNotFound = extractOption('includeNotFound', options);
if (id instanceof ObjectID) {
_id = id;
if (mongoCollection !== undefined) {
return schemaModel(odm, mongoCollection, schemaDef);
} else {
if (typeof id === 'string' && id.length === 24 && objectIdRegExp.test(id)) {
_id = ObjectID.createFromHexString(id);
} else {
return callback('invalid object id');
}
return embedSchemaModel(odm, schemaDef);
}
}
odm.findOne(mongoCollection, {_id: _id}, fields, options, function (err, documentLoaded) {
if (err) {
return callback(err);
}
if (mongoCollection !== undefined) {
return baseModel(odm, mongoCollection);
}
// if we search for an Id and get null it should return right away
if (documentLoaded === null) {
if (includeNotFound) {
return callback(null, null);
} else {
return callback(mongoCollection + ' ' + _id.toHexString() + ' not found');
}
}
if (hasFields) {
if (pluck !== undefined) {
documentLoaded = documentLoaded[pluck];
}
} else {
if (hasSchema && (wantExtend || wantExtendDeep)) {
extend(documentLoaded, Model.prototype, wantExtendDeep);
if (Model.$embeds !== undefined) {
extend(documentLoaded, Model.$embeds, wantExtendDeep);
}
if (Model.onLoad !== undefined) {
Model.onLoad(documentLoaded);
}
}
}
callback(null, documentLoaded);
});
};
/**
* Free form find in collection. The result is returned as a Array of this model objects.
*
* @memberOf Model
* @param {Object} query MongoDB Query
* @param {Object|Function} [fields] filter the fields to be returned
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, model) with the result of the operation
*/
Model.find = function (query, fields, options, callback) {
var hasFields = true;
if (callback === undefined) {
if (options === undefined) {
callback = fields;
options = {};
fields = {};
hasFields = false;
} else {
callback = options;
options = fields;
fields = {};
hasFields = false;
}
}
if (!mongoCollection) {
return callback('Cannot find on embedded model');
}
var wantExtend = extractOption('extend', options, true);
var wantExtendDeep = extractOption('deepExtend', options, true);
var pluck = extractOption('pluck', options);
if (pluck !== undefined) {
// state that we only care about the plucked field
fields[pluck] = true;
hasFields = true;
}
odm.find(mongoCollection, query, fields, options, function (err, documentsLoaded) {
if (err) {
return callback(err);
}
var i, len;
if (hasFields) {
if (pluck !== undefined) {
for (i = 0, len = documentsLoaded.length; i < len; i++) {
documentsLoaded[i] = documentsLoaded[i][pluck];
}
}
} else {
if (hasSchema && (wantExtend || wantExtendDeep)) {
for (i = 0, len = documentsLoaded.length; i < len; i++) {
extend(documentsLoaded[i], Model.prototype, wantExtendDeep);
if (Model.$embeds !== undefined) {
extend(documentsLoaded[i], Model.$embeds, wantExtendDeep);
}
if (Model.onLoad !== undefined) {
Model.onLoad(documentsLoaded[i]);
}
}
}
}
callback(null, documentsLoaded);
});
};
/**
* Finds all elements in this collection.
*
* @memberOf Model
* @param {Object|Function} [fields] filter the fields to be returned
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, model) with the result of the operation
*/
Model.findAll = function (fields, options, callback) {
Model.find({}, fields, options, callback);
};
/**
* Loads documents referenced by id/ids. This is a helper function that calls internally find or findById
* with the correct parameters. The order of the return is guaranteed, while with a find it is not.
*
* @memberOf Model
* @param {ObjectID|ObjectID[]} ids single or array of ObjectId objects
* @param {Object|Function} [fields] filter the fields to be returned
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, model) with the result of the operation
*/
Model.loadDbRef = function (ids, fields, options, callback) {
var hasFields = true;
if (callback === undefined) {
if (options === undefined) {
callback = fields;
options = {};
fields = {};
hasFields = false;
} else {
callback = options;
options = fields;
fields = {};
hasFields = false;
}
}
if (!mongoCollection) {
return callback('Cannot loadDbRef on embedded model');
}
var wantExtend = extractOption('extend', options, true);
var wantExtendDeep = extractOption('deepExtend', options, true);
var pluck = extractOption('pluck', options);
if (pluck !== undefined) {
// state that we only care about the plucked field
fields[pluck] = true;
hasFields = true;
}
// special case when the property does not exist
if (ids === undefined) {
callback(null, []);
}
if (Array.isArray(ids)) {
if (ids.length === 0) {
callback(null, []);
} else {
// convert the orig array to an index
var index = {};
var i, len;
var idsToFind = [];
for (i = 0, len = ids.length; i < len; i++) {
if (!(ids[i] instanceof ObjectID)) {
return callback('Non ObjectId in the array');
}
// build index for the missing data
if (index[ids[i].toHexString()] === undefined) {
index[ids[i].toHexString()] = [i];
idsToFind.push(ids[i]);
} else {
index[ids[i].toHexString()].push(i);
}
}
// no items to search
if (idsToFind.length === 0) {
return callback(null, []);
}
odm.find(mongoCollection, {_id: {'$in': idsToFind}}, fields, options, function (err, documentsLoaded) {
if (err) {
return callback(err);
}
var i, j, lenI, lenJ;
var result = [];
// using the index we have O(2n) complexity
for (i = 0, lenI = documentsLoaded.length; i < lenI; i++) {
var indexes = index[documentsLoaded[i]._id.toHexString()];
for (j = 0, lenJ = indexes.length; j < lenJ; j++) {
if (hasFields) {
if (pluck !== undefined) {
result[indexes[j]] = documentsLoaded[i][pluck];
}
} else {
if (hasSchema && (wantExtend || wantExtendDeep)) {
result[indexes[j]] = extend(documentsLoaded[i], Model.prototype, wantExtendDeep);
if (Model.$embeds !== undefined) {
result[indexes[j]] = extend(documentsLoaded[i], Model.$embeds, wantExtendDeep);
}
if (Model.onLoad !== undefined) {
Model.onLoad(documentsLoaded[i]);
}
}
}
}
}
callback(null, result);
});
}
} else {
Model.findById(ids, options, callback);
}
};
/**
* Ensure indexes are present
*
* @param fieldOrSpec
* @param [options]
* @param callback
*/
Model.ensureIndex = function (fieldOrSpec, options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot ensureIndex on embedded model');
}
var indexFields = Object.keys(fieldOrSpec);
if (indexFields.length === 1) {
var field = indexFields[0];
// only create special finder if the index is not on a sub document
if (field.indexOf('.') === -1) {
// create special find with cache method
var methodName = 'findBy' + field.substr(0, 1).toUpperCase() + field.substr(1);
Model[methodName] = function (id, fields, options, callback) {
var hasFields = true;
if (callback === undefined) {
if (options === undefined) {
callback = fields;
options = {};
fields = {};
hasFields = false;
} else {
callback = options;
options = fields;
fields = {};
hasFields = false;
}
}
if (!mongoCollection) {
return callback('Cannot ' + methodName + ' on embedded model');
}
var wantExtend = extractOption('extend', options, true);
var wantExtendDeep = extractOption('deepExtend', options, true);
var pluck = extractOption('pluck', options);
if (pluck !== undefined) {
// state that we only care about the plucked field
fields[pluck] = true;
hasFields = true;
}
if (id === undefined) {
return callback('undefined id');
}
var includeNotFound = true;
if (options.unique !== undefined && options.unique === true) {
includeNotFound = false;
}
var query = {};
query[field] = id;
odm.findOne(mongoCollection, query, fields, options, function (err, documentLoaded) {
if (err) {
return callback(err);
}
// if we search for an Id and get null it should return right away
if (documentLoaded === null) {
if (includeNotFound) {
return callback(null, null);
} else {
return callback(mongoCollection + ' ' + id + ' not found');
}
}
if (hasFields) {
if (pluck !== undefined) {
documentLoaded = documentLoaded[pluck];
}
} else {
if (hasSchema && (wantExtend || wantExtendDeep)) {
extend(documentLoaded, Model.prototype, wantExtendDeep);
if (Model.$embeds !== undefined) {
extend(documentLoaded, Model.$embeds, wantExtendDeep);
}
if (Model.onLoad !== undefined) {
Model.onLoad(documentLoaded);
}
}
}
callback(null, documentLoaded);
});
};
}
}
odm.collection(mongoCollection, options, function (err, collection) {
if (err) {
return callback(err);
}
collection.ensureIndex(fieldOrSpec, options, callback);
});
};
/**
* Verifies if an Object is valid against the configured validator
*
* @param {Boolean} [verbose]
* @return {Boolean|Object}
*/
Model.prototype.validate = function (verbose) {
if (hasSchema) {
var validation = odm.validate(this, Model.$schema);
if (Array.isArray(validation)) {
if (validation.length > 0) {
if (verbose === true) {
return validation;
} else {
return false;
}
}
}
}
// if no schema, always OK
if (verbose === true) {
return null;
} else {
return true;
}
};
/**
* Helper to have a short syntax
*
* @return {Boolean}
*/
Model.prototype.isValid = function () {
return this.validate();
};
/**
* Save this object instance to the backend mongodb instance.
*
* @memberOf Model
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, documentId) with the result of the operation
*/
Model.prototype.save = function (options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot save on embedded model');
}
var self = this;
var validation = self.validate(true);
if (validation !== null) {
return callback(validation);
}
odm.save(mongoCollection, self, options, function (err, savedDocument) {
if (err) {
return callback(err);
}
// only inserts have savedDocument
if (self._id === undefined) {
if (savedDocument) {
self._id = savedDocument._id;
}
}
callback(null, self._id);
});
};
/**
* Update this object instance to the backend mongodb instance.
*
* @memberOf Model
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, documentId) with the result of the operation
*/
Model.prototype.update = function (partUpdate, options, callback) {
if (callback === undefined) {
if (options === undefined) {
callback = partUpdate;
options = {};
partUpdate = undefined;
} else {
callback = options;
options = {};
}
}
if (!mongoCollection) {
return callback('Cannot update on embedded model');
}
var self = this;
var validation;
if (partUpdate !== undefined) {
var setPath = extractOption('$setpath', partUpdate);
if (setPath) {
if (Array.isArray(setPath)) {
var j, len0;
var path, i, len, result;
for (j = 0, len0 = setPath.length; j < len0; j++) {
if (typeof setPath[j] === 'string') {
path = setPath[j].split('.');
result = self;
for (i = 0, len = path.length; i < len; i++) {
result = result[path[i]];
}
if (partUpdate.$set === undefined || partUpdate.$set === null) {
partUpdate.$set = {};
}
partUpdate.$set[setPath[j]] = result;
} else {
return callback('$setpath only accepts a String path');
}
}
} else {
if (typeof setPath === 'string') {
path = setPath.split('.');
result = self;
for (i = 0, len = path.length; i < len; i++) {
result = result[path[i]];
}
if (partUpdate.$set === undefined || partUpdate.$set === null) {
partUpdate.$set = {};
}
partUpdate.$set[setPath] = result;
} else {
return callback('$setpath only accepts a String path');
}
}
validation = self.validate(true);
if (validation !== null) {
return callback(validation);
}
}
} else {
validation = self.validate(true);
if (validation !== null) {
return callback(validation);
}
}
odm.update(mongoCollection, {_id: self._id}, partUpdate !== undefined ? partUpdate : self, options, callback);
};
/**
* Insert this object instance to the backend mongodb instance.
*
* @memberOf Model
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, documentId) with the result of the operation
*/
Model.prototype.insert = function (options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot insert on embedded model');
}
var self = this;
var validation = self.validate(true);
if (validation !== null) {
return callback(validation);
}
odm.insert(mongoCollection, self, options, callback);
};
/**
* Remove this object instance from the backend mongodb instance.
*
* @memberOf Model
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error) with the result of the operation
*/
Model.prototype.remove = function (options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot remove on embedded model');
}
var self = this;
odm.remove(mongoCollection, {_id: self._id}, options, callback);
};
Model.prototype.reload = function (callback) {
if (!mongoCollection) {
return callback('Cannot remove on embedded model');
}
var self = this;
var _id = self._id;
if (!(_id instanceof ObjectID)) {
return callback('cannot reload a non stored model');
}
Model.findById(_id, function (err, documentLoaded) {
if (err) {
return callback(err);
}
// remove old references
var key;
for (key in self) {
if (self.hasOwnProperty(key)) {
delete self[key];
}
}
// update new ones
for (key in documentLoaded) {
if (documentLoaded.hasOwnProperty(key)) {
self[key] = documentLoaded[key];
}
}
callback(null);
});
};
/**
* Remove this object instance from the backend mongodb instance.
*
* @memberOf Model
* @param {Object} query Search query of objects to remove
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error) with the result of the operation
*/
Model.remove = function (query, options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot remove on embedded model');
}
odm.remove(mongoCollection, query, options, callback);
};
/**
* Insert this object instance to the backend mongodb instance.
*
* @memberOf Model
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error, documentId) with the result of the operation
*/
Model.insert = function (document, options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot insert on embedded model');
}
odm.insert(mongoCollection, document, options, callback);
};
/**
* Remove this object instance from the backend mongodb instance.
*
* @memberOf Model
* @param {Object} query Search query of objects to remove
* @param {Object|Function} [options] options for the query
* @param {Function} callback Callback function (error) with the result of the operation
*/
Model.update = function (query, document, options, callback) {
if (callback === undefined) {
callback = options;
options = {};
}
if (!mongoCollection) {
return callback('Cannot remove on embedded model');
}
odm.update(mongoCollection, query, document, options, callback);
};
/**
* Clones the Type prototype to this model class
* @param path {String} Path to be updated
* @param type {Object} Type prototype to be copied
*/
Model.embeds = function (path, type) {
path = path.split('.');
var i;
var result = Model.$embeds;
for (i = 0; i < path.length; i++) {
var key = path[i];
if (!result.hasOwnProperty(key)) {
result[key] = {};
}
result = result[path[i]];
}
var protokey;
for (protokey in type.prototype) {
if (type.prototype.hasOwnProperty(protokey)) {
if (protokey !== 'save' && protokey !== 'update' && protokey !== 'insert' && protokey !== 'remove' && protokey !== 'reload') {
result[protokey] = type.prototype[protokey];
}
}
}
for (protokey in type.$embeds) {
if (type.$embeds.hasOwnProperty(protokey)) {
if (protokey !== 'save' && protokey !== 'update' && protokey !== 'insert' && protokey !== 'remove' && protokey !== 'reload') {
result[protokey] = type.$embeds[protokey];
}
}
}
};
return Model;
throw new Error('Cannot instantiate model without schema and collection');
};

@@ -8,3 +8,3 @@ {

],
"version": "3.0.2",
"version": "3.0.3",
"engines": {

@@ -14,4 +14,4 @@ "node": ">=0.4.12"

"dependencies": {
"mongodb": "1.0.2",
"jsonschema": ">= 0.0.3"
"mongodb": "1.1.2",
"jsonschema": "0.0.4"
},

@@ -18,0 +18,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