Socket
Socket
Sign inDemoInstall

objection

Package Overview
Dependencies
8
Maintainers
1
Versions
200
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.6 to 0.2.0

3

lib/EagerFetcher.js

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

var self = this;
var queryBuilder = QueryBuilder.forClass(relation.relatedModelClass);
return relation.find(queryBuilder, this.models).then(function (related) {
return relation.find(relation.relatedModelClass.query(), this.models).then(function (related) {
return self._fetchNextEager(relation, related, nextEager);

@@ -54,0 +53,0 @@ });

@@ -189,3 +189,3 @@ 'use strict';

return QueryBuilder
return ModelClass.QueryBuilder
.forClass(ModelClass)

@@ -340,3 +340,3 @@ .findImpl(function () {

return QueryBuilder
return ModelClass.QueryBuilder
.forClass(ModelClass)

@@ -550,2 +550,10 @@ .findImpl(function () {

/**
* QueryBuilder subclass to use.
*
* This constructor is used whenever a query builder is created. You can override this
* to use your own `QueryBuilder` subclass.
*/
Model.QueryBuilder = QueryBuilder;
/**
* one-to-many relation type.

@@ -810,3 +818,3 @@ *

return QueryBuilder
return ModelClass.QueryBuilder
.forClass(ModelClass)

@@ -872,2 +880,19 @@ .insertImpl(function (models) {

/**
* Shortcut for `SomeModel.knex().raw()`.
*/
Model.raw = function () {
var knex = this.knex();
return knex.raw.apply(knex, arguments);
};
/**
* Shortcut for `SomeModel.knex().client.formatter()`.
*
* @return {Formatter}
*/
Model.formatter = function () {
return this.knex().client.formatter();
};
/**
* Shortcut for `SomeModel.knex().table(SomeModel.tableName)`.

@@ -999,3 +1024,3 @@ *

if (!input) {
return null;
return [];
}

@@ -1190,2 +1215,3 @@

model.$id(ids[idx]);
model.$afterInsert();
});

@@ -1198,12 +1224,2 @@

}
}).runAfter(function (model) {
if (_.isArray(model)) {
_.each(model, function (model) {
model.$afterInsert();
});
} else {
model.$afterInsert();
}
return model;
});

@@ -1246,6 +1262,4 @@ };

return builder.update(update.$toDatabaseJson()).runAfterModelCreatePushFront(function () {
$update.$afterUpdate(options);
return $update;
}).runAfter(function (model) {
model.$afterUpdate(options);
return model;
});

@@ -1252,0 +1266,0 @@ };

@@ -23,4 +23,3 @@ 'use strict';

* The query is executed when one of its promise methods `then()`, `catch()`, `map()`,
* `bind()` or `return()` is called. Also calling either of the paging methods `page()`
* or `range()` will execute the query.
* `bind()` or `return()` is called.
*

@@ -31,3 +30,4 @@ * @constructor

this._modelClass = modelClass;
this._knexCalls = [];
this._queryMethodCalls = [];
this._formatter = modelClass.formatter();

@@ -42,9 +42,11 @@ this._explicitResolveValue = null;

this._findImpl = null;
this._insertImpl = null;
this._updateImpl = null;
this._patchImpl = null;
this._deleteImpl = null;
this._relateImpl = null;
this._unrelateImpl = null;
this._customImpl = {
find: null,
insert: null,
update: null,
patch: null,
delete: null,
relate: null,
unrelate: null
};

@@ -56,2 +58,13 @@ this._eagerExpression = null;

/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {function}
*/
QueryBuilder.extend = function (subclassConstructor) {
utils.inherits(subclassConstructor, this);
return subclassConstructor;
};
/**
* Create QueryBuilder for a Model subclass.

@@ -318,3 +331,3 @@ *

QueryBuilder.prototype.findImpl = function (findImpl) {
this._findImpl = findImpl;
this._customImpl.find = findImpl || null;
return this;

@@ -333,3 +346,3 @@ };

QueryBuilder.prototype.insertImpl = function (insertImpl) {
this._insertImpl = insertImpl;
this._customImpl.insert = insertImpl || null;
return this;

@@ -348,3 +361,3 @@ };

QueryBuilder.prototype.updateImpl = function (updateImpl) {
this._updateImpl = updateImpl;
this._customImpl.update = updateImpl || null;
return this;

@@ -363,3 +376,3 @@ };

QueryBuilder.prototype.patchImpl = function (patchImpl) {
this._patchImpl = patchImpl;
this._customImpl.patch = patchImpl || null;
return this;

@@ -379,3 +392,3 @@ };

QueryBuilder.prototype.deleteImpl = function (deleteImpl) {
this._deleteImpl = deleteImpl;
this._customImpl.delete = deleteImpl || null;
return this;

@@ -391,3 +404,3 @@ };

QueryBuilder.prototype.relateImpl = function (relateImpl) {
this._relateImpl = relateImpl;
this._customImpl.relate = relateImpl || null;
return this;

@@ -403,3 +416,3 @@ };

QueryBuilder.prototype.unrelateImpl = function (unrelateImpl) {
this._unrelateImpl = unrelateImpl;
this._customImpl.unrelate = unrelateImpl || null;
return this;

@@ -582,3 +595,3 @@ };

clone._knexCalls = this._knexCalls.slice();
clone._queryMethodCalls = this._queryMethodCalls.slice();
clone._explicitResolveValue = this._explicitResolveValue;

@@ -590,9 +603,3 @@ clone._explicitRejectValue = this._explicitRejectValue;

clone._runAfter = this._runAfter.slice();
clone._findImpl = this._findImpl;
clone._insertImpl = this._insertImpl;
clone._updateImpl = this._updateImpl;
clone._patchImpl = this._patchImpl;
clone._deleteImpl = this._deleteImpl;
clone._relateImpl = this._relateImpl;
clone._unrelateImpl = this._unrelateImpl;
clone._customImpl = _.clone(this._customImpl);
clone._eagerExpression = this._eagerExpression;

@@ -608,9 +615,5 @@ clone._allowedEagerExpression = this._allowedEagerExpression;

QueryBuilder.prototype.clearCustomImpl = function () {
this._findImpl = null;
this._insertImpl = null;
this._updateImpl = null;
this._patchImpl = null;
this._deleteImpl = null;
this._relateImpl = null;
this._unrelateImpl = null;
for (var key in this._customImpl) {
this._customImpl[key] = null;
}
return this;

@@ -625,7 +628,7 @@ };

var args = _.toArray(arguments);
this._knexCalls = _.reject(this._knexCalls, function (call) {
this._queryMethodCalls = _.reject(this._queryMethodCalls, function (call) {
return _.contains(args, call.method);
});
} else {
this._knexCalls = [];
this._queryMethodCalls = [];
}

@@ -640,3 +643,3 @@

QueryBuilder.prototype.has = function (methodName) {
return !!_.find(this._knexCalls, {method: methodName});
return !!_.find(this._queryMethodCalls, {method: methodName});
};

@@ -701,3 +704,3 @@

/**
* Returns the amount of rows the current query would produce.
* Returns the amount of rows the current query would produce without `limit` and `offset` applied.
*

@@ -726,4 +729,7 @@ * Note that this executes a query (not the one we are building) and returns a Promise. Use it

// orderBy is useless here and it can make things a lot slower (at least with postgresql 9.3).
// Remove it from the count query.
var query = this.clone().clear('orderBy').build();
// Remove it from the count query. We also remove the offset and limit
var query = this
.clone()
.clear('orderBy', 'offset', 'limit')
.build();

@@ -739,3 +745,3 @@ var rawQuery = knex.raw(query).wrap('(', ') as temp');

/**
* Executes the query and returns a page of the results along with the total count.
* Only returns the given page of results.
*

@@ -759,3 +765,3 @@ * ```js

*
* @returns {Promise}
* @returns {QueryBuilder}
*/

@@ -767,3 +773,3 @@ QueryBuilder.prototype.page = function (page, pageSize) {

/**
* Executes the query and returns a range of the results along with the total count.
* Only returns the given range of results.
*

@@ -787,110 +793,37 @@ * ```js

*
* @returns {Promise}
* @returns {QueryBuilder}
*/
QueryBuilder.prototype.range = function (start, end) {
return Promise.all([
this.resultSize(),
this.limit(end - start + 1).offset(start)
]).spread(function (total, results) {
return {
results: results,
total: total
};
});
};
var self = this;
var resultSizePromise;
/**
* @protected
*/
QueryBuilder.prototype.build = function () {
return this.constructor.build(this.clone());
return this
.limit(end - start + 1)
.offset(start)
.runBefore(function (result) {
// Don't return the promise so that it is executed
// in parallel with the actual query.
resultSizePromise = self.resultSize();
})
.runAfter(function (results) {
// Now that the actual query is finished, wait until the
// result size has been calculated.
return Promise.all([results, resultSizePromise]);
})
.runAfter(function (arr) {
return {
results: arr[0],
total: _.parseInt(arr[1])
};
});
};
/**
* @protected
* Builds the query into a knex query builder.
*
* @returns {knex.QueryBuilder}
* The built knex query builder.
*/
QueryBuilder.build = function (builder) {
var isFindQuery = builder.isFindQuery();
var inserts = _.where(builder._knexCalls, {method: 'insert'});
var updates = _.where(builder._knexCalls, {method: 'update'});
var patches = _.where(builder._knexCalls, {method: 'patch'});
var deletes = _.where(builder._knexCalls, {method: 'delete'});
var relates = _.where(builder._knexCalls, {method: 'relate'});
var unrelates = _.where(builder._knexCalls, {method: 'unrelate'});
if (builder._insertImpl && inserts.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'insert'});
}
if (builder._updateImpl && updates.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'update'});
}
if (builder._patchImpl && patches.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'patch'});
}
if (builder._deleteImpl && deletes.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'delete'});
}
if (builder._relateImpl && relates.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'relate'});
}
if (builder._unrelateImpl && unrelates.length) {
builder._knexCalls = _.reject(builder._knexCalls, {method: 'unrelate'});
}
if (builder._insertImpl) {
_.each(inserts, function (insert) {
builder._insertImpl.apply(builder, insert.args);
});
}
if (builder._updateImpl) {
_.each(updates, function (update) {
builder._updateImpl.apply(builder, update.args);
});
}
if (builder._patchImpl) {
_.each(patches, function (patch) {
builder._patchImpl.apply(builder, patch.args);
});
}
if (builder._deleteImpl) {
_.each(deletes, function (del) {
builder._deleteImpl.apply(builder, del.args);
});
}
if (builder._relateImpl) {
_.each(relates, function (relate) {
builder._relateImpl.apply(builder, relate.args);
});
}
if (builder._unrelateImpl) {
_.each(unrelates, function (unrelate) {
builder._unrelateImpl.apply(builder, unrelate.args);
});
}
if (builder._findImpl && isFindQuery) {
builder._findImpl.call(builder);
}
var knexBuilder = builder._modelClass.knexQuery();
_.each(builder._knexCalls, function (call) {
if (_.isFunction(knexBuilder[call.method])) {
knexBuilder[call.method].apply(knexBuilder, call.args);
}
});
return knexBuilder;
QueryBuilder.prototype.build = function () {
return build(this.clone());
};

@@ -919,3 +852,3 @@

promise = promise.then(function () {
throw builder._explicitRejectValue;
return Promise.reject(builder._explicitRejectValue);
});

@@ -926,2 +859,4 @@ }

// Either return the explicit resolve value or execute the knex builder.
// (the knex builder is executed when the `then` method is invoked by
// the promise chaining)
return builder._explicitResolveValue || knexBuilder;

@@ -1603,2 +1538,24 @@ });

/**
* Compares a column reference to another
*
* ```js
* builder.whereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
QueryBuilder.prototype.whereRef = function (lhs, op, rhs) {
return this._whereRef('and', lhs, op, rhs);
};
/**
* Compares a column reference to another
*
* ```js
* builder.orWhereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
QueryBuilder.prototype.orWhereRef = function (lhs, op, rhs) {
return this._whereRef('or', lhs, op, rhs);
};
/**
* Json query APIs

@@ -1960,2 +1917,22 @@ _*/

/**
* @private
*/
QueryBuilder.prototype._whereRef = function (bool, lhs, op, rhs) {
var func = (bool === 'and') ? this.whereRaw : this.orWhereRaw;
if (_.isUndefined(rhs)) {
rhs = op;
op = '=';
}
op = this._formatter.operator(op);
if (!_.isString(lhs) || !_.isString(rhs) || !_.isString(op)) {
throw new Error('whereRef: invalid operands or operator');
}
return func.call(this, this._formatter.wrap(lhs) + ' ' + op + ' ' + this._formatter.wrap(rhs));
};
/**
* @returns {Function}

@@ -1977,2 +1954,5 @@ */

return this;
} else if (arguments[i] instanceof QueryBuilder) {
// Convert QueryBuilders into knex query builders.
args[i] = arguments[i].build();
} else {

@@ -1983,3 +1963,3 @@ args[i] = arguments[i];

this._knexCalls.push({
this._queryMethodCalls.push({
method: methodName,

@@ -2025,3 +2005,3 @@ args: args

try {
return builder.constructor.build(builder);
return build(builder);
} catch (err) {

@@ -2032,2 +2012,50 @@ builder.reject(err);

function build(builder) {
var customImpl = null;
var customArgs = null;
var isWriteQuery = false;
var nonWriteCalls = [];
_.each(builder._queryMethodCalls, function (call) {
var isWriteMethod = _.has(builder._customImpl, call.method) && call.method !== 'find';
if (isWriteMethod) {
var impl = builder._customImpl[call.method];
if (_.isFunction(impl)) {
customImpl = impl;
customArgs = call.args;
}
isWriteQuery = true;
} else {
nonWriteCalls.push(call);
}
});
if (isWriteQuery) {
if (customImpl) {
// We replace the `_queryMethodCalls` of the builder with the non-write calls so that the
// custom implementation call is not called again for the knex builder later in
// this function.
builder._queryMethodCalls = nonWriteCalls;
customImpl.apply(builder, customArgs);
}
} else {
if (builder._customImpl.find) {
builder._customImpl.find.call(builder);
}
}
var knexBuilder = builder._modelClass.knexQuery();
_.each(builder._queryMethodCalls, function (call) {
if (_.isFunction(knexBuilder[call.method])) {
knexBuilder[call.method].apply(knexBuilder, call.args);
}
});
return knexBuilder;
}
/**

@@ -2034,0 +2062,0 @@ * Field expression how to refer certain nested field inside jsonb column.

@@ -18,2 +18,6 @@ 'use strict';

/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.setMapping = function (mapping) {

@@ -29,2 +33,28 @@ var retVal = Relation.prototype.setMapping.call(this, mapping);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.findQuery = function (builder, ownerCol, isColumnRef) {
builder.join(this.joinTable, this.fullJoinTableRelatedCol(), this.fullRelatedCol());
if (isColumnRef) {
builder.whereRef(this.fullJoinTableOwnerCol(), ownerCol);
} else {
if (_.isArray(ownerCol)) {
builder.whereIn(this.fullJoinTableOwnerCol(), ownerCol);
} else {
builder.where(this.fullJoinTableOwnerCol(), ownerCol);
}
}
return builder.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.find = function (builder, $owners) {

@@ -39,3 +69,3 @@ var self = this;

// Add the statements that select the owners' rows.
this._makeFindQuery(builder, _.unique(_.pluck(owners, this.ownerProp)));
this.findQuery(builder, _.unique(_.pluck(owners, this.ownerProp)));

@@ -45,3 +75,3 @@ // Select the joined identifier of the owner model.

return builder.runAfterModelCreatePushFront(function (related) {
return builder.runAfterModelCreate(function (related) {
var relatedByOwnerId = _.groupBy(related, ownerJoinColumnAlias);

@@ -61,2 +91,27 @@

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
var joinTable = this.joinTable;
var relatedTable = this.relatedModelClass.tableName;
var joinTableAlias = this.joinTableAlias();
var relatedTableAlias = this.relatedTableAlias();
return builder
[joinMethod](joinTable + ' as ' + joinTableAlias, joinTableAlias + '.' + this.joinTableOwnerCol, this.fullOwnerCol())
[joinMethod](relatedTable + ' as ' + relatedTableAlias, joinTableAlias + '.' + this.joinTableRelatedCol, relatedTableAlias + '.' + this.relatedCol)
.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.insert = function (builder, $owner, $insertion) {

@@ -84,2 +139,7 @@ var self = this;

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.update = function (builder, $owner, $update) {

@@ -97,2 +157,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.patch = function (builder, $owner, $patch) {

@@ -110,2 +175,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.delete = function (builder, $owner) {

@@ -123,2 +193,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.relate = function (builder, $owner, $ids) {

@@ -130,3 +205,3 @@ var owner = this.ownerModelClass.ensureModel($owner);

// Insert join rows into the join table.
return builder.insert(joinRows).into(this.joinTable).returning('id').runAfterModelCreatePushFront(function (ids) {
return builder.insert(joinRows).into(this.joinTable).returning('id').runAfterModelCreate(function (ids) {
_.each(joinRows, function (row, idx) {

@@ -139,2 +214,7 @@ row.id = ids[idx] || null;

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.unrelate = function (builder, $owner) {

@@ -158,12 +238,5 @@ var self = this;

.whereIn(self.fullJoinTableRelatedCol(), idSelectQuery.build())
.runAfterModelCreatePushFront(_.constant({}));
.runAfterModelCreate(_.constant({}));
};
ManyToManyRelation.prototype._makeFindQuery = function (builder, ownerIds) {
return builder
.join(this.joinTable, this.fullJoinTableRelatedCol(), this.fullRelatedCol())
.whereIn(this.fullJoinTableOwnerCol(), ownerIds)
.call(this.filter);
};
ManyToManyRelation.prototype._makeFindIdQuery = function (ownerId) {

@@ -177,9 +250,8 @@ return this.ownerModelClass

/**
* @private
*/
ManyToManyRelation.prototype._createJoinRows = function (ownerId, relatedIds) {
var self = this;
if (!_.isArray(relatedIds)) {
relatedIds = [relatedIds];
}
return _.map(relatedIds, function (relatedId) {

@@ -186,0 +258,0 @@ var joinRow = {};

@@ -17,2 +17,26 @@ 'use strict';

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.findQuery = function (builder, ownerCol, isColumnRef) {
if (isColumnRef) {
builder.whereRef(this.fullRelatedCol(), ownerCol);
} else {
if (_.isArray(ownerCol)) {
builder.whereIn(this.fullRelatedCol(), ownerCol);
} else {
builder.where(this.fullRelatedCol(), ownerCol);
}
}
return builder.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.find = function (builder, $owners) {

@@ -23,3 +47,3 @@ var self = this;

return this._makeFindQuery(builder, ownerIds).runAfterModelCreatePushFront(function (related) {
return this.findQuery(builder, ownerIds).runAfterModelCreate(function (related) {
var relatedByOwnerId = _.groupBy(related, self.relatedProp);

@@ -35,2 +59,23 @@

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
return builder
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol())
.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.insert = function (builder, $owner, $insertion) {

@@ -51,6 +96,11 @@ var self = this;

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.update = function (builder, $owner, $update) {
var owner = this.ownerModelClass.ensureModel($owner);
this._makeFindQuery(builder, [owner[this.ownerProp]]);
this.findQuery(builder, [owner[this.ownerProp]]);
this.relatedModelClass.$$update(builder, $update);

@@ -61,6 +111,11 @@

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.patch = function (builder, $owner, $patch) {
var owner = this.ownerModelClass.ensureModel($owner);
this._makeFindQuery(builder, [owner[this.ownerProp]]);
this.findQuery(builder, [owner[this.ownerProp]]);
this.relatedModelClass.$$patch(builder, $patch);

@@ -71,6 +126,11 @@

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.delete = function (builder, $owner) {
var owner = this.ownerModelClass.ensureModel($owner);
this._makeFindQuery(builder, [owner[this.ownerProp]]);
this.findQuery(builder, [owner[this.ownerProp]]);
this.relatedModelClass.$$delete(builder);

@@ -81,2 +141,7 @@

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.relate = function (builder, $owner, $ids) {

@@ -91,3 +156,3 @@ var owner = this.ownerModelClass.ensureModel($owner);

.whereIn(this.relatedModelClass.getFullIdColumn(), _.flatten([$ids]))
.runAfter(function () {
.runAfterModelCreate(function () {
return $ids;

@@ -97,2 +162,7 @@ });

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.unrelate = function (builder, $owner) {

@@ -108,3 +178,3 @@ var owner = this.ownerModelClass.ensureModel($owner);

.call(this.filter)
.runAfter(function () {
.runAfterModelCreate(function () {
return {};

@@ -114,6 +184,2 @@ });

OneToManyRelation.prototype._makeFindQuery = function (builder, ownerIds) {
return builder.whereIn(this.fullRelatedCol(), _.compact(ownerIds)).call(this.filter);
};
module.exports = OneToManyRelation;

@@ -17,2 +17,26 @@ 'use strict';

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.findQuery = function (builder, ownerCol, isColumnRef) {
if (isColumnRef) {
builder.whereRef(this.fullRelatedCol(), ownerCol);
} else {
if (_.isArray(ownerCol)) {
builder.whereIn(this.fullRelatedCol(), ownerCol);
} else {
builder.where(this.fullRelatedCol(), ownerCol)
}
}
return builder.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.find = function (builder, $owners) {

@@ -34,2 +58,23 @@ var self = this;

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
return builder
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol())
.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.insert = function (builder, $owner, $insertion) {

@@ -62,2 +107,7 @@ var self = this;

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.update = function (builder, $owner, $update) {

@@ -72,2 +122,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.patch = function (builder, $owner, $patch) {

@@ -82,2 +137,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.delete = function (builder, $owner) {

@@ -92,2 +152,7 @@ var owner = this.ownerModelClass.ensureModel($owner);

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.relate = function (builder, $owner, $ids) {

@@ -109,3 +174,3 @@ var owner = this.ownerModelClass.ensureModel($owner);

.where(this.ownerModelClass.getFullIdColumn(), owner.$id())
.runAfter(function () {
.runAfterModelCreate(function () {
return $ids;

@@ -115,2 +180,7 @@ });

/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.unrelate = function (builder, $owner) {

@@ -127,3 +197,3 @@ var owner = this.ownerModelClass.ensureModel($owner);

.where(this.ownerModelClass.getFullIdColumn(), owner.$id())
.runAfter(function () {
.runAfterModelCreate(function () {
return {};

@@ -133,2 +203,5 @@ });

/**
* @private
*/
OneToOneRelation.prototype._makeFindQuery = function (builder, relatedIds) {

@@ -140,3 +213,3 @@ relatedIds = _.compact(relatedIds);

} else {
return builder.whereIn(this.fullRelatedCol(), relatedIds).call(this.filter);
return this.findQuery(builder, relatedIds);
}

@@ -143,0 +216,0 @@ };

@@ -10,7 +10,44 @@ 'use strict';

*
* An object literal that describes how two tables are related to one another. For example:
*
* ```js
* {
* from: 'Animal.ownerId',
* to: 'Person.id'
* }
* ```
*
* or in the case of a many-to-many relation:
*
* ```js
* {
* from: 'Person.id',
* through: {
* from: 'Person_Movie.actorId',
* to: 'Person_Movie.movieId'
* },
* to: 'Movie.id'
* }
* ```
*
* @property {String} from
* The relation column in the owner table. Must be given with the table name.
* For example `Person.id`. Note that neither this nor `to` need to be foreign
* keys or primary keys. You can join any column to any column.
*
* @property {String} to
* The relation column in the related table. Must be given with the table name.
* For example `Movie.id`. Note that neither this nor `from` need to be foreign
* keys or primary keys. You can join any column to any column.
*
* @property {Object} through
* Describes the join table if the models are related through one.
*
* @property {String} through.from
* The column that is joined to `from` property of the `RelationJoin`. For example
* `Person_Movie.actorId` where `Person_Movie` is the join table.
*
* @property {String} through.to
* The column that is joined to `to` property of the `RelationJoin`. For example
* `Person_Movie.movieId` where `Person_Movie` is the join table.
*/

@@ -39,2 +76,4 @@

*
* This is an abstract base class and should never be instantiated.
*
* @param {String} relationName

@@ -47,2 +86,3 @@ * Name of the relation.

* @ignore
* @abstract
* @constructor

@@ -144,4 +184,10 @@ */

/**
* Constructs the instance based on a mapping data.
*
* @param {RelationMapping} mapping
*/
Relation.prototype.setMapping = function (mapping) {
var Model = require('../Model');
// Avoid require loop and import here.
var Model = require(__dirname + '/../Model');

@@ -254,2 +300,9 @@ if (!utils.isSubclassOf(this.ownerModelClass, Model)) {

/**
* Reference to the relation column in the owner model's table.
*
* For example: `Person.id`.
*
* @returns {string}
*/
Relation.prototype.fullOwnerCol = function () {

@@ -259,2 +312,9 @@ return this.ownerModelClass.tableName + '.' + this.ownerCol;

/**
* Reference to the relation column in the related model's table.
*
* For example: `Movie.id`.
*
* @returns {string}
*/
Relation.prototype.fullRelatedCol = function () {

@@ -264,2 +324,9 @@ return this.relatedModelClass.tableName + '.' + this.relatedCol;

/**
* Reference to the column in the join table that is joined with `fullOwnerCol()`.
*
* For example: `Person_Movie.actorId`.
*
* @returns {string}
*/
Relation.prototype.fullJoinTableOwnerCol = function () {

@@ -269,2 +336,9 @@ return this.joinTable + '.' + this.joinTableOwnerCol;

/**
* Reference to the column in the join table that is joined with `fullRelatedCol()`.
*
* For example: `Person_Movie.movieId`.
*
* @returns {string}
*/
Relation.prototype.fullJoinTableRelatedCol = function () {

@@ -274,2 +348,29 @@ return this.joinTable + '.' + this.joinTableRelatedCol;

/**
* Alias to use for the related table when joining with the owner table.
*
* For example: `Movie_rel_movies`.
*
* @returns {string}
*/
Relation.prototype.relatedTableAlias = function () {
return this.relatedModelClass.tableName + '_rel_' + this.name;
};
/**
* Alias to use for the join table when joining with the owner table.
*
* For example: `Person_Movie_rel_movies`.
*
* @returns {string}
*/
Relation.prototype.joinTableAlias = function () {
return this.joinTable + '_rel_' + this.name;
};
/**
* Clones this relation.
*
* @returns {Relation}
*/
Relation.prototype.clone = function () {

@@ -291,2 +392,10 @@ var clone = new this.constructor(this.name, this.ownerModelClass);

/**
* Returns a clone of this relation with `relatedModelClass` and `ownerModelClass` bound to the given knex.
*
* See `Model.bindKnex`.
*
* @param knex
* @returns {Relation}
*/
Relation.prototype.bindKnex = function (knex) {

@@ -301,10 +410,55 @@ var bound = this.clone();

/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {number|string} ownerCol
* @param {boolean} isColumnRef
* @returns {QueryBuilder}
*/
Relation.prototype.findQuery = function (builder, ownerCol, isColumnRef) {
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object|Array.<Model>|Array.<Object>} $owners
* @returns {QueryBuilder}
*/
Relation.prototype.find = function (builder, $owners) {
return builder;
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {string} joinMethod
* @returns {QueryBuilder}
*/
Relation.prototype.join = function (builder, joinMethod) {
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @param {Model|Object|Array.<Model>|Array.<Object>} $insertion
* @returns {QueryBuilder}
*/
Relation.prototype.insert = function (builder, $owner, $insertion) {
return builder;
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @param {Model|Object} $update
* @returns {QueryBuilder}
*/
Relation.prototype.update = function (builder, $owner, $update) {

@@ -314,18 +468,51 @@ return builder;

/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @param {Model|Object} $patch
* @returns {QueryBuilder}
*/
Relation.prototype.patch = function (builder, $owner, $patch) {
return builder;
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @returns {QueryBuilder}
*/
Relation.prototype.delete = function (builder, $owner) {
return builder;
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @param {number|string|Array.<number>|Array.<string>} ids
* @returns {QueryBuilder}
*/
Relation.prototype.relate = function (builder, $owner, ids) {
return builder;
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} $owner
* @returns {QueryBuilder}
*/
Relation.prototype.unrelate = function (builder, $owner) {
return builder;
throw new Error('not implemented');
};
/**
* @private
*/
Relation.prototype._propertyName = function (column, modelClass) {

@@ -344,2 +531,5 @@ var propertyName = modelClass.columnNameToPropertyName(column.name);

/**
* @private
*/
function parseFilter(mapping) {

@@ -357,2 +547,5 @@ if (_.isFunction(mapping.filter)) {

/**
* @private
*/
function parseColumn(column) {

@@ -359,0 +552,0 @@ var parts = column.split('.');

{
"name": "objection",
"version": "0.1.6",
"version": "0.2.0",
"description": "An SQL-friendly ORM for Node.js",

@@ -9,3 +9,3 @@ "main": "objection.js",

"test": "istanbul --config=.istanbul.yml cover _mocha -- --slow 10 --timeout 5000 --reporter spec --recursive tests",
"just-tests": "mocha --slow 10 --timeout 5000 --reporter spec --recursive tests",
"test-only": "mocha --slow 10 --timeout 5000 --reporter spec --recursive tests",
"coveralls": "cat ./test-coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"

@@ -12,0 +12,0 @@ },

@@ -6,3 +6,3 @@ [![Build Status](https://travis-ci.org/Vincit/objection.js.svg?branch=master)](https://travis-ci.org/Vincit/objection.js) [![Coverage Status](https://coveralls.io/repos/Vincit/objection.js/badge.svg)](https://coveralls.io/r/Vincit/objection.js)

Objection.js is a Node.js ORM built around the wonderful SQL query builder [knex](http://knexjs.org). All databases
supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [fully tested](https://travis-ci.org/Vincit/objection.js).
supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://travis-ci.org/Vincit/objection.js).

@@ -16,8 +16,8 @@ What objection.js gives you:

* Completely [Promise](https://github.com/petkaantonov/bluebird) based API
* Simple [transactions](#transactions)
* Easy to use [transactions](#transactions)
* [JSON schema](#validation) validation
What objection.js doesn't give you:
What objection.js **doesn't** give you:
* A custom query DSL. SQL is used everywhere
* A custom query DSL. SQL is used as a query language.
* Automatic database schema creation and migration.

@@ -28,4 +28,5 @@ It is useful for the simple things, but usually just gets in your way when doing anything non-trivial.

Objection.js uses Promises and coding practices that make it ready for future. You can already use things like ES7 [async/await](http://jakearchibald.com/2014/es7-async-functions/)
and ES6 classes using a transpiler such as [Babel](https://babeljs.io/). Check out our [ES7 example project](https://github.com/Vincit/objection.js/tree/master/examples/express-es7).
Objection.js uses Promises and coding practices that make it ready for the future. You can already use things like ES7
[async/await](http://jakearchibald.com/2014/es7-async-functions/) and ES6 classes using a transpiler such as
[Babel](https://babeljs.io/). Check out our [ES7 example project](https://github.com/Vincit/objection.js/tree/master/examples/express-es7).

@@ -45,2 +46,3 @@ # Topics

- [Recipe book](RECIPES.md)
- [Changelog](#changelog)

@@ -50,9 +52,38 @@ # Installation

```sh
npm install objection
npm install knex objection
```
You also need to install one of the following depending on the database you want to use:
```sh
npm install pg
npm install sqlite3
npm install mysql
npm install mysql2
npm install mariasql
```
# Getting started
Best way to get started is to use one of the example projects:
To use objection.js all you need to do is [initialize knex](http://knexjs.org/#Installation-node) and give the
connection to objection.js using `Model.knex(knex)`:
```js
var Knex = require('knex');
var Model = require('objection').Model;
var knex = Knex({
client: 'postgres',
connection: {
host: '127.0.0.1',
database: 'your_database'
}
});
Model.knex(knex);
```
The next step is to create some migrations and models and start using objection.js. The best way to get started is to
use the [example project](https://github.com/Vincit/objection.js/tree/master/examples/express):
```sh

@@ -93,3 +124,3 @@ git clone git@github.com:Vincit/objection.js.git objection

The Person model used in the examples is defined [here](#models).
The `Person` model used in the examples is defined [here](#models).

@@ -101,3 +132,3 @@ All queries are started with one of the [Model](http://vincit.github.io/objection.js/Model.html) methods [query()](http://vincit.github.io/objection.js/Model.html#_P_query),

Insert a Person model to the database:
Insert a person to the database:

@@ -118,4 +149,8 @@ ```js

Fetch all Persons from the database:
```sql
insert into "Person" ("firstName", "lastName") values ('Jennifer', 'Lawrence')
```
Fetch all persons from the database:
```js

@@ -133,2 +168,6 @@ Person

```sql
select * from "Person"
```
The return value of the `.query()` method is an instance of [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html)

@@ -150,2 +189,34 @@ that has all the methods a [knex QueryBuilder](http://knexjs.org/#Builder) has. Here is a simple example that uses some of them:

```sql
select * from "Person"
where "age" > 40
and "age" < 60
and "firstName" = 'Jennifer'
order by "lastName" asc
```
The next example shows how easy it is to build complex queries:
```js
Person
.query()
.select('Person.*', 'Parent.firstName as parentFirstName')
.join('Person as Parent', 'Person.parentId', 'Parent.id')
.where('Person.age', '<', Person.query().avg('Person.age'))
.whereExists(Animal.query().select(1).whereRef('Person.id', 'Animal.ownerId'))
.orderBy('Person.lastName')
.then(function (persons) {
console.log(persons[0].parentFirstName);
});
```
```sql
select "Person".*, "Parent"."firstName" as "parentFirstName"
from "Person"
inner join "Person" as "Parent" on "Person"."parentId" = "Parent"."id"
where "Person"."age" < (select avg("Person"."age") from "Person")
and exists (select 1 from "Animal" where "Person"."id" = "Animal"."ownerId")
order by "Person"."lastName" asc
```
Update models:

@@ -156,3 +227,3 @@

.query()
.patch({lastName: 'Dinosaur'});
.patch({lastName: 'Dinosaur'})
.where('age', '>', 60)

@@ -168,2 +239,6 @@ .then(function (patch) {

```sql
update "Person" set "lastName" = 'Dinosaur' where "age" > 60
```
While the static `.query()` method can be used to create a query to a whole table `.$relatedQuery()` method

@@ -196,2 +271,11 @@ can be used to query a single relation. `.$relatedQuery()` returns an instance of [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html)

```sql
select * from "Person" where "firstName" = 'Jennifer'
select * from "Animal"
where "species" = 'dog'
and "Animal"."ownerId" = 1
order by "name" asc
```
Insert a related model:

@@ -202,3 +286,3 @@

.query()
.where('id', 100)
.where('id', 1)
.first()

@@ -209,3 +293,3 @@ .then(function (person) {

.then(function (fluffy) {
console.log(fully.id);
console.log(fluffy.id);
})

@@ -218,6 +302,12 @@ .catch(function (err) {

```sql
select * from "Person" where "id" = 1
insert into "Animal" ("name", "ownerId") values ('Fluffy', 1)
```
# Eager queries
Okay I said there is no custom DSL but actually we have teeny-tiny one for fetching relations eagerly. The following
examples demonstrate how to use it:
Okay I said there is no custom DSL but actually we have teeny-tiny one for fetching relations eagerly, as it isn't
something that can be done easily using SQL. The following examples demonstrate how to use it:

@@ -287,5 +377,17 @@ Fetch one relation:

The example above allows `req.query.eager` to be one of `'pets'`, `'children'`, `'children.pets'`, `'[pets, children]'` and
`'[pets, children.pets]'`. Examples of failing eager expressions are `'movies'`, `'children.children'` and `'notEvenAnExistingRelation'`.
The example above allows `req.query.eager` to be one of:
* `'pets'`
* `'children'`
* `'children.pets'`
* `'[pets, children]'`
* `'[pets, children.pets]'`.
Examples of failing eager expressions are:
* `'movies'`
* `'children.children'`
* `'[pets, children.children]'`
* `'notEvenAnExistingRelation'`.
In addition to the `.eager` method, relations can be fetched using the `loadRelated` and `$loadRelated` methods of

@@ -442,5 +544,30 @@ [Model](http://vincit.github.io/objection.js/Model.html).

## Minimal model
A working model with minimal amount of code:
```js
var Model = require('objection').Model;
function MinimalModel() {
Model.apply(this, arguments);
}
// Inherit `Model`. This gives your model all those methods like `MinimalModel.query()`
// and `MinimalModel.fromJson()`.
Model.extend(MinimalModel);
// After the js class boilerplate, all you need to do is set the table name.
MinimalModel.tableName = 'SomeTableName';
module.exports = MinimalModel;
```
## A model with custom methods, json schema validation and relations
This is the model used in the examples:
```js
var Model = require('objection').Model;
function Person() {

@@ -515,2 +642,11 @@ Model.apply(this, arguments);

parent: {
relation: Model.OneToOneRelation,
modelClass: Person,
join: {
from: 'Person.parentId',
to: 'Person.id'
}
},
children: {

@@ -534,1 +670,25 @@ relation: Model.OneToManyRelation,

# Changelog
## 0.2.0
### New features
* New name `objection.js`.
* `$beforeInsert`, `$afterInsert`, `$beforeUpdate` and `$afterUpdate` hooks for `Model`.
* Postgres jsonb query methods: `whereJsonEquals`, `whereJsonSupersetOf`, `whereJsonSubsetOf` and friends.
* `whereRef` query method.
* Expose `knex.raw()` through `Model.raw()`.
* Expose `knex.client.formatter()` through `Model.formatter()`.
* `QueryBuilder` can be used to make sub queries just like knex's `QueryBuilder`.
* Possibility to use a custom `QueryBuilder` subclass by overriding `Model.QueryBuilder`.
* Filter queries/objects for relations.
* A pile of bug fixes.
### Breaking changes
* Project was renamed to objection.js. Migrate simply by replacing `moron` with `objection`.
## 0.1.0
First release.
SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc