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

xpress-mongo

Package Overview
Dependencies
Maintainers
1
Versions
149
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xpress-mongo - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

fns/projection.js

20

DataTypes.js

@@ -24,3 +24,4 @@ class ModelDataType {

class NotDefined {}
class NotDefined {
}

@@ -30,2 +31,6 @@ const isString = (v) => typeof v === 'string';

/**
* DataTypes
* @type {{NotDefined: NotDefined, ModelDataType: ModelDataType, is: {Number: (function(*=): ModelDataType), ObjectId: (function(): ModelDataType), String: (function(*=): ModelDataType), Boolean: (function(*=): ModelDataType), Date: (function(*=): ModelDataType)}}}
*/
module.exports = {

@@ -73,7 +78,18 @@ NotDefined,

*/
Date: (def = new Date().toISOString()) => {
Date: (def = new Date()) => {
return new ModelDataType(def)
.validator(isString);
},
/**
* Number
* @param def
* @return {ModelDataType}
* @constructor
*/
Number: (def = 0) => {
return new ModelDataType(def)
.validator(isNaN)
}
}
};

2

index.js

@@ -20,3 +20,3 @@ const {MongoClient} = require('mongodb');

Client,
is,
is
};
{
"name": "xpress-mongo",
"version": "0.0.3",
"version": "0.0.4",
"description": "Light Weight ODM for mongoDb",

@@ -27,3 +27,4 @@ "main": "index.js",

"deep-object-diff": "^1.1.0",
"mongodb": "^3.4.0"
"mongodb": "^3.4.0",
"object-collection": "^1.0.19"
},

@@ -30,0 +31,0 @@ "devDependencies": {

@@ -17,3 +17,3 @@ # Xpress-Mongo

class User extends Collection("users") {
class Users extends Collection("users") {
constructor() {

@@ -25,3 +25,3 @@ super();

module.exports = User;
module.exports = Users;
```

@@ -31,3 +31,3 @@

```javascript
const user = new User().set({
const user = new Users().set({
first_name: 'John',

@@ -34,0 +34,0 @@ last_name: 'Doe'

const {Client} = require('../index');
const db = "mongodb://localhost:27017";
const db = "mongodb://127.0.0.1:27017";
const db_name = "test_model";

@@ -5,0 +5,0 @@ const db_options = {

const Database = require('./connection');
const {is} = require('../index');
const UserSchema = {
const ContactSchema = is => ({
user_id: is.ObjectId(),
first_name: is.String().required(),
last_name: is.String().required(),
phone: is.String(),
created_at: is.Date()
});
class Contacts extends Database.model("contacts") {
constructor() {
super();
this.setSchema(ContactSchema);
}
}
const UserSchema = is => ({
email: is.String().required(),

@@ -10,8 +24,8 @@ first_name: is.String().required(),

verified: is.Boolean(),
updated_at: is.Date(),
created_at: is.Date()
};
updated_at: is.Date,
created_at: is.Date
});
class User extends Database.model("users") {
class Users extends Database.model("users") {
constructor() {

@@ -21,8 +35,26 @@ super();

}
static append = ['fullName'];
fullName() {
return `${this.data.first_name} ${this.data.last_name}`
}
static relationships = {
contact: {
type: 'hasOne',
model: Contacts,
where: {user_id: '_id'},
options: {projection: {_id: 1}}
}
};
}
/**
* Return User
* @type {User| typeof XMongoModel}
* @type {typeof Users| typeof XMongoModel}
*/
module.exports = User;
module.exports.Users = Users;
/**
* @type {typeof Contacts| typeof XMongoModel}
*/
module.exports.Contacts = Contacts;
const {ObjectID} = require('mongodb');
const {ModelDataType} = require('./DataTypes');
const {is, ModelDataType} = require('./DataTypes');
const {diff} = require('deep-object-diff');
const ObjectCollection = require('object-collection');
const _ = ObjectCollection._;
/**

@@ -26,3 +27,3 @@ * Returns a model class tied to the given collection.

// Assume data is empty
this.data = {};
this.emptyData();
Object.defineProperty(this, 'schema', {

@@ -33,4 +34,9 @@ value: {},

});
Object.defineProperty(this, 'loadedRelationships', {
value: [],
write: true,
enumerable: false
})
}
}

@@ -44,3 +50,10 @@

/**
* Model Data
* @type {ObjectCollection}
*/
XMongoModel.prototype.$data = new ObjectCollection;
/**
* Model Original Data

@@ -58,3 +71,9 @@ * @type {*}

/** Model Loaded Relationships
* @private
* @type {*[]}
*/
XMongoModel.prototype.loadedRelationships = [];
/**

@@ -65,4 +84,4 @@ * Get Data in model

*/
XMongoModel.prototype.get = function (key) {
return this.data[key] || undefined;
XMongoModel.prototype.get = function (key, $default) {
return _.get(this.data, key, $default);
};

@@ -79,14 +98,6 @@

for (const property in key) {
Object.defineProperty(this.data, property, {
value: key[property],
writable: true,
enumerable: true,
})
_.set(this.data, property, key[property])
}
} else if (typeof key === 'string') {
Object.defineProperty(this.data, key, {
value,
writable: true,
enumerable: true,
});
_.set(this.data, key, value)
}

@@ -98,2 +109,14 @@ return this;

/**
* Insert new record and return instance.
* @param data
* @return {Promise<this|*>}
*/
XMongoModel.new = async function (data) {
const record = new this().set(data);
await record.save();
return record;
};
/**
* Set Original result gotten from db

@@ -104,2 +127,4 @@ * @param data

XMongoModel.prototype.setOriginal = function (data) {
data = _.cloneDeep(data);
Object.defineProperty(this, 'original', {

@@ -114,3 +139,8 @@ value: data,

/**
* @callback schemaWithIs
* @param {is|*} raw
*/
/**

@@ -120,3 +150,3 @@ * Set Model Schema

* if `schema` is undefined then `this.data` is used as schema object
* @param schema
* @param {schemaWithIs|Object} schema
* @returns {XMongoModel}

@@ -127,9 +157,15 @@ */

schema === undefined && (schema = this.data);
const newData = {};
const newData = {_id: null};
// If schema is a function then call it and pass is.
if (typeof schema === "function") {
schema = schema(is);
}
for (const key in schema) {
if (schema.hasOwnProperty(key)) {
const val = schema[key];
let val = schema[key];
if (val instanceof ModelDataType) {

@@ -168,3 +204,3 @@ this.schema[key] = val['schema'];

XMongoModel.prototype.id = function () {
return this.data['_id'] || null
return (this.data && this.data['_id']) || null
};

@@ -177,6 +213,28 @@

XMongoModel.prototype.changes = function () {
return diff(this.original, this.data);
const changes = diff(this.original, this.data);
const data = {};
const append = this.constructor.append || [];
const excluded = [...append, ...this.loadedRelationships];
for (const key in changes) {
if (!excluded.includes(key)) {
data[key] = this.data[key]
}
}
return data;
};
/**
* Update model
* @param set
* @param options
* @return {Promise<Collection~updateWriteOpResult|Collection~insertOneWriteOpResult>}
*/
XMongoModel.prototype.update = function (set, options) {
if (!this.id()) throw "UPDATE_ERROR: Model does not have an _id, so we assume it is not from the database.";
return this.set(set).save(options)
};
/**
* Create Model if not id is missing or save document if id is found.

@@ -191,5 +249,8 @@ * @param options

if (id) {
const $set = this.changes();
if (!Object.keys($set).length) return resolve(false);
return collection.updateOne(
{_id: this.id()},
{$set: this.changes()},
{$set},
options,

@@ -204,3 +265,5 @@ (error, res) => error ? reject(error) : resolve(res.connection))

const {insertedId} = res;
this.set('_id', insertedId);
this.setOriginal(this.data);

@@ -215,2 +278,128 @@ return resolve(res)

/**
* Delete this
* @param writeConcern
* @returns {Promise}
*/
XMongoModel.prototype.delete = function (writeConcern) {
const _id = this.id();
if (_id) {
this.emptyData();
return collection.deleteOne({_id})
} else {
throw "DELETE_ERROR: Model does not have an _id, so we assume it is not from the database.";
}
};
/**
* Sets data as an instance of ObjectCollection on this.$data
* @return {ObjectCollection}
*/
XMongoModel.prototype.toCollection = function () {
if (!this.hasOwnProperty('$data')) {
Object.defineProperty(this, '$data', {
value: new ObjectCollection(this.data),
writable: true,
enumerable: false
});
return this.$data;
}
return this.$data;
};
XMongoModel.prototype.hasOne = async function (relationship, extend = {}) {
let config = this.constructor.relationships;
if (config && config.hasOwnProperty(relationship)) {
config = config[relationship];
if (config.type !== "hasOne") {
throw Error(`Relationship: (${relationship}) is not of type "hasOne"`)
}
/**
* Raw option check.
* @type {boolean|boolean}
*/
const cast = extend.hasOwnProperty('cast') && extend.cast === true;
/**
* Get query option
* @type {*|{}}
*/
let options = _.cloneDeep(config['options'] || {});
if (extend.hasOwnProperty('options')) options = _.cloneDeep(extend.options);
/**
* Get Relationship where query.
* @type {*}
*/
let where = _.clone(config.where);
if (typeof where === "object" && Object.keys(where).length) {
/**
* Loop through all keys in where query and change the values
* to matching model instance values.
*/
for (const key in where) {
where[key] = this.get(where[key])
}
} else {
where = {};
}
if (typeof extend !== "object") {
throw Error(`hasOne second argument must be of type "Object"`);
}
/**
* Get hasOne Model.
* @type {typeof XMongoModel}
*/
let model = config.model;
if (Array.isArray(model) && typeof model[0] === "function")
model = model[0]();
let relatedData = await model.raw.findOne(where, options);
if (cast && relatedData) relatedData = model.use(relatedData);
/**
* Set relationship to value provided in the extend.as config.
*/
if (extend['as']) relationship = extend['as'];
this.set(relationship, relatedData);
this.loadedRelationships.push(relationship);
return relatedData;
}
};
/**
* @private
* @return {*}
*/
XMongoModel.prototype.toJSON = function () {
return this.data;
};
XMongoModel.prototype.toJson = function (replacer = undefined, space = undefined) {
return JSON.stringify(this.data, replacer, space);
};
XMongoModel.prototype.emptyData = function () {
this.data = {
_id: this.id()
};
return this;
};
/**
* Direct mongodb access

@@ -255,7 +444,6 @@ * @type {Collection}

XMongoModel.find = (query, options, raw = false) => {
const result = collection.find(query, options);
if (raw) return result;
return new Promise((resolve, reject) => {
const result = collection.find(query, options);
if (raw) return resolve(result);
return result.toArray((error, data) => {

@@ -268,4 +456,91 @@ if (error) return reject(error);

/**
* Turn data provided in query function to model instances.
* @param {{}} data
* @return {XMongoModel}
*/
XMongoModel.use = function (data) {
const model = new this();
model.emptyData();
// Set Original Property
model.setOriginal(data);
model.set(data);
if (this.append) {
for (const key of this.append) {
if (typeof model[key] === "function") {
model.set(key, model[key]())
}
}
}
return model;
};
/**
* @callback rawQueryFn
* @param {Collection|*} raw
*/
/**
* Turn array provided to model instances.
*
* if function is passed instead of the array
* xpress-mongo will assume you want to provide a raw query
* that it will append mongodb `.toArray` function to.
*
* @example
* E.G
* contact = ContactModel.fromArray([...SomeAlreadyFetchedData])
*
* OR
* contact = ContactModel.fromArray(raw => raw.find().limit(10));
*
* WHICH IS ===
*
* Model.raw.find().limit(10).toArray((err, lists) => {
* Model.fromArray(lists);
* })
*
*
* @static
* @method
* @param {rawQueryFn|Object[]} query - Data as array or query as function.
* @return {Promise<this[]>|this[]} returns - Array of model instances
*/
XMongoModel.fromArray = function (query) {
if (typeof query === "function") {
return new Promise((resolve, reject) => {
return query(this.raw).toArray((error, lists) => {
if (error) return reject(error);
return resolve(this.fromArray(lists));
});
});
} else {
const data = [];
for (const list of query) {
data.push(this.use(list))
}
return data;
}
};
/**
*
* @param {rawQueryFn} query
* @return {Promise<this>|XMongoModel}
*/
XMongoModel.from = function (query) {
return new Promise((resolve, reject) => {
return query(this.raw).then((error, data) => {
if (error) return reject(error);
return resolve(this.use(data));
});
});
};
/**
* Fetches the first document that matches the query

@@ -277,5 +552,11 @@ * @param query

*/
XMongoModel.findOne = (query, options, raw = false) => {
XMongoModel.findOne = function (query, options, raw = false) {
if (typeof options === "boolean") {
raw = options;
options = {};
}
return new Promise((resolve, reject) => {
collection.findOne(query, options, (error, data) => {
return collection.findOne(query, options, (error, data) => {
if (error) return reject(error);

@@ -286,9 +567,3 @@ // Return new instance of Model

const model = new XMongoModel();
// Set Original Property
model.setOriginal(data);
model.set(data);
return resolve(model);
return resolve(this.use(data));
});

@@ -302,8 +577,9 @@ });

* @param _id
* @param options
* @param isTypeObjectId
* @return {Promise<XMongoModel>}
*/
XMongoModel.findOneById = function (_id, isTypeObjectId = true) {
XMongoModel.findById = function (_id, options = {}, isTypeObjectId = true) {
let where;
if (isTypeObjectId) {
if (typeof _id === "string" || !isTypeObjectId) {
where = XMongoModel.id(_id, true);

@@ -314,3 +590,3 @@ } else {

return XMongoModel.findOne(where);
return this.findOne(where, options);
};

@@ -325,8 +601,85 @@

XMongoModel.count = function (query, options) {
return XMongoModel.raw.find(query, options).count()
return this.raw.find(query, options).count()
};
/**
* Count Aggregations
* @param query
* @param options
* @returns {Promise<number|*>}
*/
XMongoModel.countAggregate = async function (query, options) {
query = _.cloneDeep(query);
query.push({$count: "count_aggregate"});
const data = await this.raw.aggregate(query, options).toArray();
if (data.length) {
return data[0]['count_aggregate']
}
return 0;
};
/**
* Paginate Find.
* @param query
* @param options
* @param page
* @param perPage
* @return {Promise<{total: *, perPage: number, lastPage: number, data: [], page: number}>}
*/
XMongoModel.paginate = async function (page = 1, perPage = 20, query = {}, options = {}) {
page = Number(page);
perPage = Number(perPage);
const total = await this.count(query);
const lastPage = Math.ceil(total / perPage);
const skips = perPage * (page - 1);
const data = await this.raw.find(query, options).skip(skips).limit(perPage).toArray();
return {
total,
perPage,
page,
lastPage,
data
}
};
/**
* Paginate Aggregation.
* @param {number} page
* @param {number} perPage
* @param {[]} query
* @param {*} options
* @returns {Promise<{total: (*|number), perPage: number, lastPage: number, data: *, page: number}>}
*/
XMongoModel.paginateAggregate = async function (page = 1, perPage = 20, query = [], options = {}) {
query = _.cloneDeep(query);
page = Number(page);
perPage = Number(perPage);
const total = await this.countAggregate(query);
const lastPage = Math.ceil(total / perPage);
const skips = perPage * (page - 1);
query.push({$skip: skips});
query.push({$limit: perPage});
const data = await this.raw.aggregate(query, options).toArray();
return {
total,
perPage,
page,
lastPage,
data
}
};
return XMongoModel;
}
module.exports = GenerateModel;
module.exports = GenerateModel;
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