Socket
Socket
Sign inDemoInstall

objection

Package Overview
Dependencies
Maintainers
2
Versions
200
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

objection - npm Package Compare versions

Comparing version 0.3.3 to 0.4.0-rc.1

lib/objection.js

3

lib/model/inheritModel/index.js

@@ -19,2 +19,3 @@ 'use strict';

*/
try {

@@ -24,2 +25,2 @@ module.exports = require('./inheritModelEs6');

module.exports = require('./inheritModelEs5');
}
}

@@ -10,3 +10,2 @@ 'use strict';

return AnonymousModelSubclass;
};
};
'use strict';
var _ = require('lodash')
, utils = require('./../utils')
, ModelBase = require('./ModelBase')
, QueryBuilder = require('./../queryBuilder/QueryBuilder')
, inheritModel = require('./inheritModel')
, RelationExpression = require('./../queryBuilder/RelationExpression')
, ValidationError = require('./../ValidationError')
, EagerFetcher = require('./../queryBuilder/EagerFetcher')
, Relation = require('./../relations/Relation')
, OneToOneRelation = require('./../relations/OneToOneRelation')
, OneToManyRelation = require('./../relations/OneToManyRelation')
, ManyToManyRelation = require('./../relations/ManyToManyRelation');
var _class, _temp;
/**
* Subclasses of this class represent database tables.
*
* Subclass can be created like this:
*
* ```js
var Model = require('objection').Model;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
function Person() {
Model.apply(this, arguments);
}
var _create = require('babel-runtime/core-js/object/create');
Model.extend(Person);
module.exports = Person;
var _create2 = _interopRequireDefault(_create);
// Table name is the only required property.
Person.tableName = 'Person';
var _stringify = require('babel-runtime/core-js/json/stringify');
// This is not the database schema! Nothing is generated based on this. Whenever a
// Person object is created from a JSON object, the JSON is checked against this
// schema. For example when you call Person.fromJson({firstName: 'Jennifer'});
Person.jsonSchema = {
type: 'object',
required: ['firstName', 'lastName'],
var _stringify2 = _interopRequireDefault(_stringify);
properties: {
id: {type: 'integer'},
parentId: {type: ['integer', 'null']},
firstName: {type: 'string', minLength: 1, maxLength: 255},
lastName: {type: 'string', minLength: 1, maxLength: 255},
age: {type: 'number'},
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
address: {
type: 'object',
properties: {
street: {type: 'string'},
city: {type: 'string'},
zipCode: {type: 'string'}
}
}
}
};
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
// This object defines the relations to other models.
Person.relationMappings = {
pets: {
relation: Model.OneToManyRelation,
// The related model. This can be either a Model subclass constructor or an
// absolute file path to a module that exports one. We use the file path version
// here to prevent require loops.
modelClass: __dirname + '/Animal',
join: {
from: 'Person.id',
to: 'Animal.ownerId'
}
},
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
movies: {
relation: Model.ManyToManyRelation,
modelClass: __dirname + '/Movie',
join: {
from: 'Person.id',
// ManyToMany relation needs the `through` object to describe the join table.
through: {
from: 'Person_Movie.personId',
to: 'Person_Movie.movieId'
},
to: 'Movie.id'
}
},
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
children: {
relation: Model.OneToManyRelation,
modelClass: Person,
join: {
from: 'Person.id',
to: 'Person.parentId'
}
}
};
* ```
*
* @extends ModelBase
* @constructor
*/
function Model() {
ModelBase.apply(this, arguments);
}
var _createClass2 = require('babel-runtime/helpers/createClass');
ModelBase.extend(Model);
var _createClass3 = _interopRequireDefault(_createClass2);
/**
* Returns or sets the identifier of a model instance.
*
* ```js
* // Returns the id.
* model.$id();
* // Sets the id.
* model.$id(100);
* ```
*
* The identifier property does not have to be accessed or set using this method.
* If the identifier property is known it can be accessed or set just like any
* other property:
*
* ```js
* console.log(model.id);
* model.id = 100;
* ```
*
* This method is just a helper for the cases where the id property is not known.
*
* @param {*=} id
* @returns {*}
*/
Model.prototype.$id = function () {
var ModelClass = this.constructor;
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
if (arguments.length > 0) {
this[ModelClass.getIdProperty()] = arguments[0];
} else {
return this[ModelClass.getIdProperty()];
}
};
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _get2 = require('babel-runtime/helpers/get');
var _get3 = _interopRequireDefault(_get2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _utils = require('../utils');
var _utils2 = _interopRequireDefault(_utils);
var _ModelBase2 = require('./ModelBase');
var _ModelBase3 = _interopRequireDefault(_ModelBase2);
var _QueryBuilder = require('../queryBuilder/QueryBuilder');
var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder);
var _inheritModel = require('./inheritModel');
var _inheritModel2 = _interopRequireDefault(_inheritModel);
var _RelationExpression = require('../queryBuilder/RelationExpression');
var _RelationExpression2 = _interopRequireDefault(_RelationExpression);
var _ValidationError = require('../ValidationError');
var _ValidationError2 = _interopRequireDefault(_ValidationError);
var _EagerFetcher = require('../queryBuilder/EagerFetcher');
var _EagerFetcher2 = _interopRequireDefault(_EagerFetcher);
var _Relation = require('../relations/Relation');
var _Relation2 = _interopRequireDefault(_Relation);
var _OneToOneRelation = require('../relations/OneToOneRelation');
var _OneToOneRelation2 = _interopRequireDefault(_OneToOneRelation);
var _OneToManyRelation = require('../relations/OneToManyRelation');
var _OneToManyRelation2 = _interopRequireDefault(_OneToManyRelation);
var _ManyToManyRelation = require('../relations/ManyToManyRelation');
var _ManyToManyRelation2 = _interopRequireDefault(_ManyToManyRelation);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Creates a query builder for this model instance.
* Subclasses of this class represent database tables.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
* Subclass can be created like this:
*
* All queries built using the returned builder only affect this instance.
* ```js
* var Model = require('objection').Model;
*
* Examples:
* function Person() {
* Model.apply(this, arguments);
* }
*
* Re-fetch the instance from the database:
* Model.extend(Person);
* module.exports = Person;
*
* ```js
* person.$query().then(function (person) {
* console.log(person);
* });
* ```
* // Table name is the only required property.
* Person.tableName = 'Person';
*
* Insert a new model to database:
* // This is not the database schema! Nothing is generated based on this. Whenever a
* // Person object is created from a JSON object, the JSON is checked against this
* // schema. For example when you call Person.fromJson({firstName: 'Jennifer'});
* Person.jsonSchema = {
* type: 'object',
* required: ['firstName', 'lastName'],
*
* ```js
* Person.fromJson({firstName: 'Jennifer'}).$query().insert().then(function (jennifer) {
* console.log(jennifer.id);
* });
* ```
* properties: {
* id: {type: 'integer'},
* parentId: {type: ['integer', 'null']},
* firstName: {type: 'string', minLength: 1, maxLength: 255},
* lastName: {type: 'string', minLength: 1, maxLength: 255},
* age: {type: 'number'},
*
* Patch a model:
* address: {
* type: 'object',
* properties: {
* street: {type: 'string'},
* city: {type: 'string'},
* zipCode: {type: 'string'}
* }
* }
* }
* };
*
* ```js
* person.$query().patch({lastName: 'Cooper'}).then(function (person) {
* console.log(person.lastName); // --> 'Cooper'.
* });
* ```
* // This object defines the relations to other models.
* Person.relationMappings = {
* pets: {
* relation: Model.OneToManyRelation,
* // The related model. This can be either a Model subclass constructor or an
* // absolute file path to a module that exports one. We use the file path version
* // here to prevent require loops.
* modelClass: __dirname + '/Animal',
* join: {
* from: 'Person.id',
* to: 'Animal.ownerId'
* }
* },
*
* Delete a model.
* movies: {
* relation: Model.ManyToManyRelation,
* modelClass: __dirname + '/Movie',
* join: {
* from: 'Person.id',
* // ManyToMany relation needs the `through` object to describe the join table.
* through: {
* from: 'Person_Movie.personId',
* to: 'Person_Movie.movieId'
* },
* to: 'Movie.id'
* }
* },
*
* ```js
* person.$query().delete().then(function () {
* console.log('person deleted');
* });
* children: {
* relation: Model.OneToManyRelation,
* modelClass: Person,
* join: {
* from: 'Person.id',
* to: 'Person.parentId'
* }
* }
* };
* ```
*
* @returns {QueryBuilder}
* @extends ModelBase
* @constructor
*/
Model.prototype.$query = function () {
var ModelClass = this.constructor;
var self = this;
var Model = (_temp = _class = (function (_ModelBase) {
(0, _inherits3.default)(Model, _ModelBase);
return ModelClass.QueryBuilder
.forClass(ModelClass)
.findImpl(function () {
this.first();
this.onBuild(function (builder) {
builder.where(ModelClass.getFullIdColumn(), self.$id());
});
})
.insertImpl(function (insertion) {
insertion.setData(self);
function Model() {
(0, _classCallCheck3.default)(this, Model);
return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(Model).apply(this, arguments));
}
this.onBuild(function (builder) {
builder.$$insert(insertion);
});
})
.updateImpl(function (update) {
if (!update.model()) {
update.setData(self);
(0, _createClass3.default)(Model, [{
key: '$id',
/**
* Returns or sets the identifier of a model instance.
*
* ```js
* // Returns the id.
* model.$id();
* // Sets the id.
* model.$id(100);
* ```
*
* The identifier property does not have to be accessed or set using this method.
* If the identifier property is known it can be accessed or set just like any
* other property:
*
* ```js
* console.log(model.id);
* model.id = 100;
* ```
*
* This method is just a helper for the cases where the id property is not known.
*
* @param {*=} id
* @returns {*}
*/
/**
* @private
*/
/**
* Properties that should be saved to database as JSON strings.
*
* The properties listed here are serialized to JSON strings upon insertion to the database
* and parsed back to objects when models are read from the database. Combined with the
* postgresql's json or jsonb data type, this is a powerful way of representing documents
* as single database rows.
*
* If this property is left unset all properties declared as objects or arrays in the
* `jsonSchema` are implicitly added to this list.
*
* Example:
*
* ```js
* Person.jsonAttributes = ['address'];
*
* var jennifer = Person.fromJson({
* name: 'Jennifer',
* address: {
* address: 'Someroad 10',
* zipCode: '1234',
* city: 'Los Angeles'
* }
* });
*
* var dbRow = jennifer.$toDatabaseJson();
* console.log(dbRow);
* // --> {name: 'Jennifer', address: '{"address":"Someroad 10","zipCode":"1234","city":"Los Angeles"}'}
* ```
*
* @type {Array.<string>}
*/
/**
* Name of the property used to store a reference to a `uidProp`.
*
* Defaults to '#ref'.
*
* @type {string}
*/
/**
* Name of the primary key column in the database table.
*
* Defaults to 'id'.
*
* @type {string}
*/
/**
* may-to-many relation type.
*
* @type {ManyToManyRelation}
*/
/**
* one-to-many relation type.
*
* @type {OneToOneRelation}
*/
/**
* QueryBuilder subclass to use in `query()` or `$query()` methods.
*
* This constructor is used whenever a query builder is created using `query()` or `$query()` methods.
* You can override this to use your own `QueryBuilder` subclass.
*/
value: function $id() {
var ModelClass = this.constructor;
if (arguments.length > 0) {
this[ModelClass.getIdProperty()] = arguments[0];
} else {
return this[ModelClass.getIdProperty()];
}
}
this.onBuild(function (builder) {
builder.$$update(update).where(ModelClass.getFullIdColumn(), self.$id());
/**
* Creates a query builder for this model instance.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
*
* All queries built using the returned builder only affect this instance.
*
* Examples:
*
* Re-fetch the instance from the database:
*
* ```js
* person.$query().then(function (person) {
* console.log(person);
* });
* ```
*
* Insert a new model to database:
*
* ```js
* Person.fromJson({firstName: 'Jennifer'}).$query().insert().then(function (jennifer) {
* console.log(jennifer.id);
* });
* ```
*
* Patch a model:
*
* ```js
* person.$query().patch({lastName: 'Cooper'}).then(function (person) {
* console.log(person.lastName); // --> 'Cooper'.
* });
* ```
*
* Delete a model.
*
* ```js
* person.$query().delete().then(function () {
* console.log('person deleted');
* });
* ```
*
* @returns {QueryBuilder}
*/
/**
* @private
*/
/**
* This property defines the relations to other models.
*
* Relations to other models can be defined by setting this property. The best way to explain how to
* do this is by example:
*
* ```js
* Person.relationMappings = {
* pets: {
* relation: Model.OneToManyRelation,
* modelClass: Animal,
* join: {
* from: 'Person.id',
* to: 'Animal.ownerId'
* }
* },
*
* father: {
* relation: Model.OneToOneRelation,
* modelClass: Person,
* join: {
* from: 'Person.fatherId',
* to: 'Person.id'
* }
* },
*
* movies: {
* relation: Model.ManyToManyRelation,
* modelClass: Movie,
* join: {
* from: 'Person.id',
* through: {
* from: 'Person_Movie.actorId',
* to: 'Person_Movie.movieId'
* },
* to: 'Movie.id'
* }
* }
* };
* ```
*
* relationMappings is an object whose keys are relation names and values define the relation. The
* `join` property in addition to the relation type define how the models are related to one another.
* The `from` and `to` properties of the `join` object define the database columns through which the
* models are associated. Note that neither of these columns need to be primary keys. They can be any
* columns!. In the case of ManyToManyRelation also the join table needs to be defined. This is
* done using the `through` object.
*
* The `modelClass` passed to the relation mappings is the class of the related model. It can be either
* a Model subclass constructor or an absolute path to a module that exports one. Using file paths
* is a handy way to prevent require loops.
*
* @type {Object.<string, RelationMapping>}
*/
/**
* Regular expression for parsing a reference to a property.
*
* @type {RegExp}
*/
/**
* Name of the property used to store a temporary non-db identifier for the model.
*
* Defaults to '#id'.
*
* @type {string}
*/
/**
* Name of the database table of this model.
*
* @type {string}
*/
/**
* one-to-many relation type.
*
* @type {OneToManyRelation}
*/
/**
* QueryBuilder subclass to use in `$relatedQuery()` method.
*
* This constructor is used whenever a query builder is created using the `$relatedQuery()` method.
* You can override this to use your own `QueryBuilder` subclass.
*/
}, {
key: '$query',
value: function $query() {
var _this2 = this;
var ModelClass = this.constructor;
return ModelClass.QueryBuilder.forClass(ModelClass).findImpl(function (builder) {
builder.first();
builder.onBuild(function (builder) {
builder.where(ModelClass.getFullIdColumn(), _this2.$id());
});
}).insertImpl(function (insertion, builder) {
insertion.setData(_this2);
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
}).updateImpl(function (update, builder) {
if (!update.model()) {
update.setData(_this2);
}
builder.onBuild(function (builder) {
builder.$$update(update).where(ModelClass.getFullIdColumn(), _this2.$id());
});
}).patchImpl(function (patch, builder) {
if (!patch.model()) {
patch.setData(_this2);
}
builder.onBuild(function (builder) {
builder.$$update(patch).where(ModelClass.getFullIdColumn(), _this2.$id());
});
}).deleteImpl(function (builder) {
builder.onBuild(function (builder) {
builder.$$delete().where(ModelClass.getFullIdColumn(), _this2.$id());
});
}).relateImpl(function () {
throw new Error('`relate` makes no sense in this context');
}).unrelateImpl(function () {
throw new Error('`unrelate` makes no sense in this context');
});
})
.patchImpl(function (patch) {
if (!patch.model()) {
patch.setData(self);
}
}
this.onBuild(function (builder) {
builder.$$update(patch).where(ModelClass.getFullIdColumn(), self.$id());
/**
* Use this to build a query that only affects the models related to this instance through a relation.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
*
* Examples:
*
* Fetch all models related to this instance through a relation. The fetched models are
* also stored to the owner model's property named after the relation:
*
* ```js
* jennifer.$relatedQuery('pets').then(function (pets) {
* console.log('jennifer has', pets.length, 'pets');
* console.log(jennifer.pets === pets); // --> true
* });
* ```
*
* The related query is just like any other query. All *knex* methods are available:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .select('Animal.*', 'Person.name as ownerName')
* .where('species', '=', 'dog')
* .orWhere('breed', '=', 'cat')
* .innerJoin('Person', 'Person.id', 'Animal.ownerId')
* .orderBy('Animal.name')
* .then(function (dogsAndCats) {
* // All the dogs and cats have the owner's name "Jennifer" joined as the `ownerName` property.
* console.log(dogsAndCats);
* });
* ```
*
* This inserts a new model to the database and binds it to the owner model as defined
* by the relation:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .insert({species: 'dog', name: 'Fluffy'})
* .then(function (waldo) {
* console.log(waldo.id);
* });
* ```
*
* To add an existing model to a relation the `relate` method can be used. In this example
* the dog `fluffy` already exists in the database but it isn't related to `jennifer` through
* the `pets` relation. We can make the connection like this:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .relate(fluffy.id)
* .then(function () {
* console.log('fluffy is now related to jennifer through pets relation');
* });
* ```
*
* The connection can be removed using the `unrelate` method. Again, this doesn't delete the
* related model. Only the connection is removed. For example in the case of ManyToMany relation
* the join table entries are deleted.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .unrelate()
* .where('id', fluffy.id)
* .then(function () {
* console.log('jennifer no longer has fluffy as a pet');
* });
* ```
*
* Related models can be deleted using the delete method. Note that in the case of ManyToManyRelation
* the join table entries are not deleted. Naturally the delete query can be chained with any *knex*
* methods.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .delete()
* .where('species', 'cat')
* .then(function () {
* console.log('jennifer no longer has any cats');
* });
* ```
*
* `update` and `patch` can be used to update related models. Only difference between the mentioned
* methods is that `update` validates the input objects using the related model class's full schema
* and `patch` ignores the `required` property of the schema. Use `update` when you want to update
* _all_ properties of a model and `patch` when only a subset should be updated.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .update({species: 'dog', name: 'Fluffy the great', vaccinated: false})
* .where('id', fluffy.id)
* .then(function (updatedFluffy) {
* console.log('fluffy\'s new name is', updatedFluffy.name);
* });
*
* // This will throw assuming that `name` or `species` is a required property for an Animal.
* jennifer.$relatedQuery('pets').patch({vaccinated: true});
*
* // This will _not_ throw.
* jennifer
* .$relatedQuery('pets')
* .patch({vaccinated: true})
* .where('species', 'dog')
* .then(function () {
* console.log('jennifer just got all her dogs vaccinated');
* });
* ```
*
* @param {string} relationName
* Name of the relation.
*
* @returns {QueryBuilder}
*/
}, {
key: '$relatedQuery',
value: function $relatedQuery(relationName) {
var _this3 = this;
var relation = this.constructor.getRelation(relationName);
var ModelClass = relation.relatedModelClass;
return ModelClass.RelatedQueryBuilder.forClass(ModelClass).findImpl(function (builder) {
relation.find(builder, [_this3]);
}).insertImpl(function (insert, builder) {
relation.insert(builder, _this3, insert);
}).updateImpl(function (update, builder) {
relation.update(builder, _this3, update);
}).patchImpl(function (patch, builder) {
relation.patch(builder, _this3, patch);
}).deleteImpl(function (builder) {
relation.delete(builder, _this3);
}).relateImpl(function (ids, builder) {
relation.relate(builder, _this3, ids);
}).unrelateImpl(function (builder) {
relation.unrelate(builder, _this3);
});
})
.deleteImpl(function () {
this.onBuild(function (builder) {
builder.$$delete().where(ModelClass.getFullIdColumn(), self.$id());
});
})
.relateImpl(function () {
throw new Error('`relate` makes no sense in this context');
})
.unrelateImpl(function () {
throw new Error('`unrelate` makes no sense in this context');
});
};
}
/**
* Use this to build a query that only affects the models related to this instance through a relation.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
*
* Examples:
*
* Fetch all models related to this instance through a relation. The fetched models are
* also stored to the owner model's property named after the relation:
*
* ```js
* jennifer.$relatedQuery('pets').then(function (pets) {
* console.log('jennifer has', pets.length, 'pets');
* console.log(jennifer.pets === pets); // --> true
* });
* ```
*
* The related query is just like any other query. All *knex* methods are available:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .select('Animal.*', 'Person.name as ownerName')
* .where('species', '=', 'dog')
* .orWhere('breed', '=', 'cat')
* .innerJoin('Person', 'Person.id', 'Animal.ownerId')
* .orderBy('Animal.name')
* .then(function (dogsAndCats) {
* // All the dogs and cats have the owner's name "Jennifer" joined as the `ownerName` property.
* console.log(dogsAndCats);
* });
* ```
*
* This inserts a new model to the database and binds it to the owner model as defined
* by the relation:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .insert({species: 'dog', name: 'Fluffy'})
* .then(function (waldo) {
* console.log(waldo.id);
* });
* ```
*
* To add an existing model to a relation the `relate` method can be used. In this example
* the dog `fluffy` already exists in the database but it isn't related to `jennifer` through
* the `pets` relation. We can make the connection like this:
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .relate(fluffy.id)
* .then(function () {
* console.log('fluffy is now related to jennifer through pets relation');
* });
* ```
*
* The connection can be removed using the `unrelate` method. Again, this doesn't delete the
* related model. Only the connection is removed. For example in the case of ManyToMany relation
* the join table entries are deleted.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .unrelate()
* .where('id', fluffy.id)
* .then(function () {
* console.log('jennifer no longer has fluffy as a pet');
* });
* ```
*
* Related models can be deleted using the delete method. Note that in the case of ManyToManyRelation
* the join table entries are not deleted. Naturally the delete query can be chained with any *knex*
* methods.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .delete()
* .where('species', 'cat')
* .then(function () {
* console.log('jennifer no longer has any cats');
* });
* ```
*
* `update` and `patch` can be used to update related models. Only difference between the mentioned
* methods is that `update` validates the input objects using the related model class's full schema
* and `patch` ignores the `required` property of the schema. Use `update` when you want to update
* _all_ properties of a model and `patch` when only a subset should be updated.
*
* ```js
* jennifer
* .$relatedQuery('pets')
* .update({species: 'dog', name: 'Fluffy the great', vaccinated: false})
* .where('id', fluffy.id)
* .then(function (updatedFluffy) {
* console.log('fluffy\'s new name is', updatedFluffy.name);
* });
*
* // This will throw assuming that `name` or `species` is a required property for an Animal.
* jennifer.$relatedQuery('pets').patch({vaccinated: true});
*
* // This will _not_ throw.
* jennifer
* .$relatedQuery('pets')
* .patch({vaccinated: true})
* .where('species', 'dog')
* .then(function () {
* console.log('jennifer just got all her dogs vaccinated');
* });
* ```
*
* @param {String} relationName
* Name of the relation.
*
* @returns {QueryBuilder}
*/
Model.prototype.$relatedQuery = function (relationName) {
var relation = this.constructor.getRelation(relationName);
var ModelClass = relation.relatedModelClass;
var self = this;
/**
* Loads related models using a {@link RelationExpression}.
*
* Example:
*
* ```js
* jennifer.$loadRelated('[pets, children.[pets, father]]').then(function (jennifer) {
* console.log('Jennifer has', jennifer.pets.length, 'pets');
* console.log('Jennifer has', jennifer.children.length, 'children');
* console.log('Jennifer\'s first child has', jennifer.children[0].pets.length, 'pets');
* console.log('Jennifer had her first child with', jennifer.children[0].father.name);
* });
* ```
*
* Relations can be filtered by giving named filter functions as arguments
* to the relations:
*
* ```js
* jennifer
* .$loadRelated('children(orderByAge).[pets(onlyDogs, orderByName), movies]', {
* orderByAge: function (builder) {
* builder.orderBy('age')
* },
* orderByName: function (builder) {
* builder.orderBy('name');
* },
* onlyDogs: function (builder) {
* builder.where('species', 'dog')
* }
* })
* .then(function (jennifer) {
* console.log(jennifer.children.pets[0]);
* });
* ```
*
* @see {@link RelationExpression} for more examples on relation expressions.
*
* @param {string|RelationExpression} relationExpression
* @param {Object.<string, function(QueryBuilder)>=} filters
* @returns {Promise}
*/
return ModelClass.RelatedQueryBuilder
.forClass(ModelClass)
.findImpl(function () {
var queryBuilder = this;
relation.find(queryBuilder, [self]);
})
.insertImpl(function (insert) {
var queryBuilder = this;
relation.insert(queryBuilder, self, insert);
})
.updateImpl(function (update) {
var queryBuilder = this;
relation.update(queryBuilder, self, update);
})
.patchImpl(function (patch) {
var queryBuilder = this;
relation.patch(queryBuilder, self, patch);
})
.deleteImpl(function () {
var queryBuilder = this;
relation.delete(queryBuilder, self);
})
.relateImpl(function (ids) {
var queryBuilder = this;
relation.relate(queryBuilder, self, ids);
})
.unrelateImpl(function () {
var queryBuilder = this;
relation.unrelate(queryBuilder, self);
});
};
}, {
key: '$loadRelated',
value: function $loadRelated(relationExpression, filters) {
return this.constructor.loadRelated(this, relationExpression, filters);
}
/**
* Loads related models using a {@link RelationExpression}.
*
* Example:
*
* ```js
* jennifer.$loadRelated('[pets, children.[pets, father]]').then(function (jennifer) {
* console.log('Jennifer has', jennifer.pets.length, 'pets');
* console.log('Jennifer has', jennifer.children.length, 'children');
* console.log('Jennifer\'s first child has', jennifer.children[0].pets.length, 'pets');
* console.log('Jennifer had her first child with', jennifer.children[0].father.name);
* });
* ```
*
* Relations can be filtered by giving named filter functions as arguments
* to the relations:
*
* ```js
* jennifer
* .$loadRelated('children(orderByAge).[pets(onlyDogs, orderByName), movies]', {
* orderByAge: function (builder) {
* builder.orderBy('age')
* },
* orderByName: function (builder) {
* builder.orderBy('name');
* },
* onlyDogs: function (builder) {
* builder.where('species', 'dog')
* }
* })
* .then(function (jennifer) {
* console.log(jennifer.children.pets[0]);
* });
* ```
*
* @see {@link RelationExpression} for more examples on relation expressions.
*
* @param {String|RelationExpression} relationExpression
* @param {Object.<String, Function(QueryBuilder)>=} filters
* @returns {Promise}
*/
Model.prototype.$loadRelated = function (relationExpression, filters) {
return this.constructor.loadRelated(this, relationExpression, filters);
};
/**
* Shortcut for `Model.traverse(filterConstructor, this, callback)`.
*
* See the static method `Model.traverse` for more info.
*
* @param {function=} filterConstructor
* @param {function(Model)} callback
* @return {Model}
*/
/**
* Shortcut for `Model.traverse(filterConstructor, this, callback)`.
*
* See the static method `Model.traverse` for more info.
*
* @param {Function=} filterConstructor
* @param {Function(Model)} callback
* @return {Model}
*/
Model.prototype.$traverse = function (filterConstructor, callback) {
if (_.isUndefined(callback)) {
callback = filterConstructor;
filterConstructor = null;
}
}, {
key: '$traverse',
value: function $traverse(filterConstructor, callback) {
if (_lodash2.default.isUndefined(callback)) {
callback = filterConstructor;
filterConstructor = null;
}
this.constructor.traverse(filterConstructor, this, callback);
return this;
};
this.constructor.traverse(filterConstructor, this, callback);
return this;
}
/**
* @override
*/
Model.prototype.$parseDatabaseJson = function (json) {
var ModelClass = this.constructor;
var jsonAttr = ModelClass.$$getJsonAttributes();
/**
* @override
*/
if (jsonAttr.length) {
for (var i = 0, l = jsonAttr.length; i < l; ++i) {
var attr = jsonAttr[i];
var value = json[attr];
}, {
key: '$parseDatabaseJson',
value: function $parseDatabaseJson(json) {
var ModelClass = this.constructor;
var jsonAttr = ModelClass.$$getJsonAttributes();
if (_.isString(value)) {
json[attr] = JSON.parse(value);
if (jsonAttr.length) {
for (var i = 0, l = jsonAttr.length; i < l; ++i) {
var attr = jsonAttr[i];
var value = json[attr];
if (_lodash2.default.isString(value)) {
json[attr] = JSON.parse(value);
}
}
}
return json;
}
}
return json;
};
/**
* @override
*/
/**
* @override
*/
Model.prototype.$formatDatabaseJson = function (json) {
var ModelClass = this.constructor;
var jsonAttr = ModelClass.$$getJsonAttributes();
}, {
key: '$formatDatabaseJson',
value: function $formatDatabaseJson(json) {
var ModelClass = this.constructor;
var jsonAttr = ModelClass.$$getJsonAttributes();
if (jsonAttr.length) {
for (var i = 0, l = jsonAttr.length; i < l; ++i) {
var attr = jsonAttr[i];
var value = json[attr];
if (jsonAttr.length) {
for (var i = 0, l = jsonAttr.length; i < l; ++i) {
var attr = jsonAttr[i];
var value = json[attr];
if (_.isObject(value)) {
json[attr] = JSON.stringify(value);
if (_lodash2.default.isObject(value)) {
json[attr] = (0, _stringify2.default)(value);
}
}
}
return json;
}
}
return json;
};
/**
* @override
*/
/**
* @override
*/
Model.prototype.$setJson = function (json, options) {
ModelBase.prototype.$setJson.call(this, json, options);
}, {
key: '$setJson',
value: function $setJson(json, options) {
(0, _get3.default)((0, _getPrototypeOf2.default)(Model.prototype), '$setJson', this).call(this, json, options);
if (!_.isObject(json)) {
return;
}
if (!_lodash2.default.isObject(json)) {
return;
}
var relations = this.constructor.getRelations();
// Parse relations into Model instances.
for (var relationName in relations) {
if (_.has(json, relationName)) {
var relationJson = json[relationName];
var relation = relations[relationName];
var relations = this.constructor.getRelations();
// Parse relations into Model instances.
for (var relationName in relations) {
if (_lodash2.default.has(json, relationName)) {
var relationJson = json[relationName];
var relation = relations[relationName];
if (_.isArray(relationJson)) {
this[relationName] = relation.relatedModelClass.ensureModelArray(relationJson, options);
} else if (relationJson) {
this[relationName] = relation.relatedModelClass.ensureModel(relationJson, options);
if (_lodash2.default.isArray(relationJson)) {
this[relationName] = relation.relatedModelClass.ensureModelArray(relationJson, options);
} else if (relationJson) {
this[relationName] = relation.relatedModelClass.ensureModel(relationJson, options);
} else {
this[relationName] = null;
}
}
}
}
/**
* @override
*
* @param {boolean} shallow
* If true the relations are omitted from the json.
*/
}, {
key: '$toJson',
value: function $toJson(shallow) {
if (shallow) {
return this.$$toJson(false, this.constructor.getRelations(), null);
} else {
this[relationName] = null;
return this.$$toJson(false, null, null);
}
}
}
};
/**
* @override
*
* @param {Boolean} shallow
* If true the relations are omitted from the json.
*/
Model.prototype.$toJson = function (shallow) {
if (shallow) {
return this.$$toJson(false, this.constructor.getRelations(), null);
} else {
return this.$$toJson(false, null, null);
}
};
/**
* @override
*/
/**
* @override
*/
Model.prototype.$toDatabaseJson = function () {
var jsonSchema = this.constructor.jsonSchema;
var pick = jsonSchema && jsonSchema.properties;
var omit;
}, {
key: '$toDatabaseJson',
value: function $toDatabaseJson() {
var jsonSchema = this.constructor.jsonSchema;
var pick = jsonSchema && jsonSchema.properties;
var omit = undefined;
if (!pick) {
omit = this.constructor.getRelations();
}
if (!pick) {
omit = this.constructor.getRelations();
}
return this.$$toJson(true, omit, pick);
};
return this.$$toJson(true, omit, pick);
}
/**
* Called before a model is inserted into the database.
*
* You can return a promise from this function if you need to do asynchronous stuff. You can
* also throw an exception to abort the insert and reject the query. This can be useful if
* you need to do insert specific validation.
*
* @returns {Promise|*}
*/
Model.prototype.$beforeInsert = function () {
/**
* Called before a model is inserted into the database.
*
* You can return a promise from this function if you need to do asynchronous stuff. You can
* also throw an exception to abort the insert and reject the query. This can be useful if
* you need to do insert specific validation.
*
* @param {Object} queryContext
* The context object of the insert query. See {@link QueryBuilder#context}.
*
* @returns {Promise|*}
*/
};
}, {
key: '$beforeInsert',
value: function $beforeInsert(queryContext) {}
/**
* Called after a model has been inserted into the database.
*
* You can return a promise from this function if you need to do asynchronous stuff.
*
* @returns {Promise|*}
*/
Model.prototype.$afterInsert = function () {
/**
* Called after a model has been inserted into the database.
*
* You can return a promise from this function if you need to do asynchronous stuff.
*
* @param {Object} queryContext
* The context object of the insert query. See {@link QueryBuilder#context}.
*
* @returns {Promise|*}
*/
};
}, {
key: '$afterInsert',
value: function $afterInsert(queryContext) {}
/**
* Called before a model is updated.
*
* You can return a promise from this function if you need to do asynchronous stuff. You can
* also throw an exception to abort the update and reject the query. This can be useful if
* you need to do update specific validation.
*
* This method is also called before a model is patched. Therefore all the model's properties
* may not exist. You can check if the update operation is a patch by checking the `opt.patch`
* boolean.
*
* Also note that this method is called only once when you do something like this:
*
* ```js
* Person
* .$query()
* .patch({firstName: 'Jennifer'})
* .where('gender', 'female')
* .then(function () {
* ...
* });
* ```
*
* The example above updates all rows whose `gender` equals `female` but the `$beforeUpdate`
* method is called only once for the `{firstName: 'Jennifer'}` model. This is because the
* updating is done completely in the database and the affected models are never fetched
* to the javascript side.
*
* @param {ModelOptions} opt
* @returns {Promise|*}
*/
Model.prototype.$beforeUpdate = function (opt) {
_.noop(opt);
};
/**
* Called before a model is updated.
*
* You can return a promise from this function if you need to do asynchronous stuff. You can
* also throw an exception to abort the update and reject the query. This can be useful if
* you need to do update specific validation.
*
* This method is also called before a model is patched. Therefore all the model's properties
* may not exist. You can check if the update operation is a patch by checking the `opt.patch`
* boolean.
*
* Also note that this method is called only once when you do something like this:
*
* ```js
* Person
* .$query()
* .patch({firstName: 'Jennifer'})
* .where('gender', 'female')
* .then(function () {
* ...
* });
* ```
*
* The example above updates all rows whose `gender` equals `female` but the `$beforeUpdate`
* method is called only once for the `{firstName: 'Jennifer'}` model. This is because the
* updating is done completely in the database and the affected models are never fetched
* to the javascript side.
*
* @param {ModelOptions} opt
*
* @param {Object} queryContext
* The context object of the update query. See {@link QueryBuilder#context}.
*
* @returns {Promise|*}
*/
/**
* Called after a model is updated.
*
* You can return a promise from this function if you need to do asynchronous stuff.
*
* This method is also called after a model is patched. Therefore all the model's properties
* may not exist. You can check if the update operation is a patch by checking the `opt.patch`
* boolean.
*
* Also note that this method is called only once when you do something like this:
*
* ```js
* Person
* .$query()
* .patch({firstName: 'Jennifer'})
* .where('gender', 'female')
* .then(function () {
* ...
* });
* ```
*
* The example above updates all rows whose `gender` equals `female` but the `$beforeUpdate`
* method is called only once for the `{firstName: 'Jennifer'}` model. This is because the
* updating is done completely in the database and the affected models are never fetched
* to the javascript side.
*
* @param {ModelOptions} opt
* @returns {Promise|*}
*/
Model.prototype.$afterUpdate = function (opt) {
_.noop(opt);
};
}, {
key: '$beforeUpdate',
value: function $beforeUpdate(opt, queryContext) {}
/**
* QueryBuilder subclass to use in `query()` or `$query()` methods.
*
* This constructor is used whenever a query builder is created using `query()` or `$query()` methods.
* You can override this to use your own `QueryBuilder` subclass.
*/
Model.QueryBuilder = QueryBuilder;
/**
* Called after a model is updated.
*
* You can return a promise from this function if you need to do asynchronous stuff.
*
* This method is also called after a model is patched. Therefore all the model's properties
* may not exist. You can check if the update operation is a patch by checking the `opt.patch`
* boolean.
*
* Also note that this method is called only once when you do something like this:
*
* ```js
* Person
* .$query()
* .patch({firstName: 'Jennifer'})
* .where('gender', 'female')
* .then(function () {
* ...
* });
* ```
*
* The example above updates all rows whose `gender` equals `female` but the `$beforeUpdate`
* method is called only once for the `{firstName: 'Jennifer'}` model. This is because the
* updating is done completely in the database and the affected models are never fetched
* to the javascript side.
*
* @param {ModelOptions} opt
*
* @param {Object} queryContext
* The context object of the update query. See {@link QueryBuilder#context}.
*
* @returns {Promise|*}
*/
/**
* QueryBuilder subclass to use in `$relatedQuery()` method.
*
* This constructor is used whenever a query builder is created using the `$relatedQuery()` method.
* You can override this to use your own `QueryBuilder` subclass.
*/
Model.RelatedQueryBuilder = QueryBuilder;
}, {
key: '$afterUpdate',
value: function $afterUpdate(opt, queryContext) {}
/**
* one-to-many relation type.
*
* @type {OneToOneRelation}
*/
Model.OneToOneRelation = OneToOneRelation;
/**
* Creates a query builder for this table.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
*
* Examples:
*
* Read models from the database:
*
* ```js
* // Get all rows.
* Person.query().then(function(allPersons) {
* console.log('there are', allPersons.length, 'persons in the database');
* });
*
* // Example of a more complex WHERE clause. This generates:
* // SELECT * FROM "Person" WHERE ("firstName" = 'Jennifer' AND "age" < 30) OR ("firstName" = "Mark" AND "age" > 30)
* Person
* .query()
* .where(function () {
* this.where('firstName', 'Jennifer').where('age', '<', 30);
* })
* .orWhere(function () {
* this.where('firstName', 'Mark').where('age', '>', 30);
* })
* .then(function (marksAndJennifers) {
* console.log(marksAndJennifers);
* });
*
* // Get a subset of rows and fetch related models for each row.
* Person
* .query()
* .where('age', '>', 60)
* .eager('children.children.movies')
* .then(function (oldPeople) {
* console.log('some old person\'s grand child has appeared in',
* oldPeople[0].children[0].children[0].movies.length,
* 'movies');
* });
* ```
*
* Insert models to the database:
*
* ```js
* Person.query().insert({firstName: 'Sylvester', lastName: 'Stallone'}).then(function (sylvester) {
* console.log(sylvester.fullName()); // --> 'Sylvester Stallone'.
* });
*
* // Batch insert. This only works on Postgresql as it is the only database that returns the
* // identifiers of _all_ inserted rows. If you need to do batch inserts on other databases use
* // *knex* directly. (See .knexQuery() method).
* Person
* .query()
* .insert([
* {firstName: 'Arnold', lastName: 'Schwarzenegger'},
* {firstName: 'Sylvester', lastName: 'Stallone'}
* ])
* .then(function (inserted) {
* console.log(inserted[0].fullName()); // --> 'Arnold Schwarzenegger'
* });
* ```
*
* `update` and `patch` can be used to update models. Only difference between the mentioned methods
* is that `update` validates the input objects using the model class's full jsonSchema and `patch`
* ignores the `required` property of the schema. Use `update` when you want to update _all_ properties
* of a model and `patch` when only a subset should be updated.
*
* ```js
* Person
* .query()
* .update({firstName: 'Jennifer', lastName: 'Lawrence', age: 35})
* .where('id', jennifer.id)
* .then(function (updatedJennifer) {
* console.log('Jennifer is now', updatedJennifer.age, 'years old');
* });
*
* // This will throw assuming that `firstName` or `lastName` is a required property for a Person.
* Person.query().patch({age: 100});
*
* // This will _not_ throw.
* Person
* .query()
* .patch({age: 100})
* .then(function () {
* console.log('Everyone is now 100 years old');
* });
* ```
*
* Models can be deleted using the delete method. Naturally the delete query can be chained with
* any *knex* methods:
*
* ```js
* Person
* .query()
* .delete()
* .where('age', '>', 90)
* .then(function () {
* console.log('anyone over 90 is now removed from the database');
* });
* ```
*
* @returns {QueryBuilder}
*/
/**
* one-to-many relation type.
*
* @type {OneToManyRelation}
*/
Model.OneToManyRelation = OneToManyRelation;
}], [{
key: 'query',
value: function query() {
var ModelClass = this;
/**
* may-to-many relation type.
*
* @type {ManyToManyRelation}
*/
Model.ManyToManyRelation = ManyToManyRelation;
return ModelClass.QueryBuilder.forClass(ModelClass).relateImpl(function () {
throw new Error('`relate` makes no sense in this context');
}).unrelateImpl(function () {
throw new Error('`unrelate` makes no sense in this context');
});
}
/**
* Name of the database table of this model.
*
* @type {String}
*/
Model.tableName = null;
/**
* Get/Set the knex instance for this model class.
*
* Subclasses inherit the connection. A system-wide knex instance can thus be set by calling
* `Model.knex(knex)`. This works even after subclasses have been created.
*
* ```js
* var knex = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database.db'
* }
* });
*
* Model.knex(knex);
* knex = Model.knex();
* ```
*
* @param {knex=} knex
* The knex to set.
*
* @returns {knex|undefined}
*/
/**
* Name of the primary key column in the database table.
*
* Defaults to 'id'.
*
* @type {String}
*/
Model.idColumn = 'id';
}, {
key: 'knex',
value: function knex(_knex) {
if (arguments.length) {
this.$$knex = _knex;
} else {
var modelClass = this;
/**
* Name of the property used to store a temporary non-db identifier for the model.
*
* Defaults to '#id'.
*
* @type {String}
*/
Model.uidProp = '#id';
while (modelClass && !modelClass.$$knex) {
var proto = modelClass.prototype.__proto__;
modelClass = proto && proto.constructor;
}
/**
* Name of the property used to store a reference to a `uidProp`.
*
* Defaults to '#ref'.
*
* @type {String}
*/
Model.uidRefProp = '#ref';
return modelClass && modelClass.$$knex;
}
}
/**
* Regular expression for parsing a reference to a property.
*
* @type {RegExp}
*/
Model.propRefRegex = /#ref{([^\.]+)\.([^}]+)}/g;
/**
* Shortcut for `SomeModel.knex().raw()`.
*/
/**
* Properties that should be saved to database as JSON strings.
*
* The properties listed here are serialized to JSON strings upon insertion to the database
* and parsed back to objects when models are read from the database. Combined with the
* postgresql's json or jsonb data type, this is a powerful way of representing documents
* as single database rows.
*
* If this property is left unset all properties declared as objects or arrays in the
* `jsonSchema` are implicitly added to this list.
*
* Example:
*
* ```js
* Person.jsonAttributes = ['address'];
*
* var jennifer = Person.fromJson({
* name: 'Jennifer',
* address: {
* address: 'Someroad 10',
* zipCode: '1234',
* city: 'Los Angeles'
* }
* });
*
* var dbRow = jennifer.$toDatabaseJson();
* console.log(dbRow);
* // --> {name: 'Jennifer', address: '{"address":"Someroad 10","zipCode":"1234","city":"Los Angeles"}'}
* ```
*
* @type {Array.<String>}
*/
Model.jsonAttributes = null;
}, {
key: 'raw',
value: function raw() {
var knex = this.knex();
return knex.raw.apply(knex, arguments);
}
/**
* This property defines the relations to other models.
*
* Relations to other models can be defined by setting this property. The best way to explain how to
* do this is by example:
*
* ```js
* Person.relationMappings = {
* pets: {
* relation: Model.OneToManyRelation,
* modelClass: Animal,
* join: {
* from: 'Person.id',
* to: 'Animal.ownerId'
* }
* },
*
* father: {
* relation: Model.OneToOneRelation,
* modelClass: Person,
* join: {
* from: 'Person.fatherId',
* to: 'Person.id'
* }
* },
*
* movies: {
* relation: Model.ManyToManyRelation,
* modelClass: Movie,
* join: {
* from: 'Person.id',
* through: {
* from: 'Person_Movie.actorId',
* to: 'Person_Movie.movieId'
* },
* to: 'Movie.id'
* }
* }
* };
* ```
*
* relationMappings is an object whose keys are relation names and values define the relation. The
* `join` property in addition to the relation type define how the models are related to one another.
* The `from` and `to` properties of the `join` object define the database columns through which the
* models are associated. Note that neither of these columns need to be primary keys. They can be any
* columns!. In the case of ManyToManyRelation also the join table needs to be defined. This is
* done using the `through` object.
*
* The `modelClass` passed to the relation mappings is the class of the related model. It can be either
* a Model subclass constructor or an absolute path to a module that exports one. Using file paths
* is a handy way to prevent require loops.
*
* @type {Object.<String, RelationMapping>}
*/
Model.relationMappings = null;
/**
* Shortcut for `SomeModel.knex().fn`.
*/
/**
* @private
*/
Model.$$knex = null;
}, {
key: 'fn',
value: function fn() {
var knex = this.knex();
return knex.fn;
}
/**
* @private
*/
Model.$$relations = null;
/**
* Shortcut for `SomeModel.knex().client.formatter()`.
*
* @return {Formatter}
*/
/**
* Creates a query builder for this table.
*
* The returned query builder has all the methods a *knex* query builder has. See
* {@link QueryBuilder} and <a href="http://knexjs.org/#Builder">knexjs.org</a>
* for more information.
*
* Examples:
*
* Read models from the database:
*
* ```js
* // Get all rows.
* Person.query().then(function(allPersons) {
* console.log('there are', allPersons.length, 'persons in the database');
* });
*
* // Example of a more complex WHERE clause. This generates:
* // SELECT * FROM "Person" WHERE ("firstName" = 'Jennifer' AND "age" < 30) OR ("firstName" = "Mark" AND "age" > 30)
* Person
* .query()
* .where(function () {
* this.where('firstName', 'Jennifer').where('age', '<', 30);
* })
* .orWhere(function () {
* this.where('firstName', 'Mark').where('age', '>', 30);
* })
* .then(function (marksAndJennifers) {
* console.log(marksAndJennifers);
* });
*
* // Get a subset of rows and fetch related models for each row.
* Person
* .query()
* .where('age', '>', 60)
* .eager('children.children.movies')
* .then(function (oldPeople) {
* console.log('some old person\'s grand child has appeared in',
* oldPeople[0].children[0].children[0].movies.length,
* 'movies');
* });
* ```
*
* Insert models to the database:
*
* ```js
* Person.query().insert({firstName: 'Sylvester', lastName: 'Stallone'}).then(function (sylvester) {
* console.log(sylvester.fullName()); // --> 'Sylvester Stallone'.
* });
*
* // Batch insert. This only works on Postgresql as it is the only database that returns the
* // identifiers of _all_ inserted rows. If you need to do batch inserts on other databases use
* // *knex* directly. (See .knexQuery() method).
* Person
* .query()
* .insert([
* {firstName: 'Arnold', lastName: 'Schwarzenegger'},
* {firstName: 'Sylvester', lastName: 'Stallone'}
* ])
* .then(function (inserted) {
* console.log(inserted[0].fullName()); // --> 'Arnold Schwarzenegger'
* });
* ```
*
* `update` and `patch` can be used to update models. Only difference between the mentioned methods
* is that `update` validates the input objects using the model class's full jsonSchema and `patch`
* ignores the `required` property of the schema. Use `update` when you want to update _all_ properties
* of a model and `patch` when only a subset should be updated.
*
* ```js
* Person
* .query()
* .update({firstName: 'Jennifer', lastName: 'Lawrence', age: 35})
* .where('id', jennifer.id)
* .then(function (updatedJennifer) {
* console.log('Jennifer is now', updatedJennifer.age, 'years old');
* });
*
* // This will throw assuming that `firstName` or `lastName` is a required property for a Person.
* Person.query().patch({age: 100});
*
* // This will _not_ throw.
* Person
* .query()
* .patch({age: 100})
* .then(function () {
* console.log('Everyone is now 100 years old');
* });
* ```
*
* Models can be deleted using the delete method. Naturally the delete query can be chained with
* any *knex* methods:
*
* ```js
* Person
* .query()
* .delete()
* .where('age', '>', 90)
* .then(function () {
* console.log('anyone over 90 is now removed from the database');
* });
* ```
*
* @returns {QueryBuilder}
*/
Model.query = function () {
var ModelClass = this;
}, {
key: 'formatter',
value: function formatter() {
return this.knex().client.formatter();
}
return ModelClass.QueryBuilder
.forClass(ModelClass)
.relateImpl(function () {
throw new Error('`relate` makes no sense in this context');
})
.unrelateImpl(function () {
throw new Error('`unrelate` makes no sense in this context');
});
};
/**
* Shortcut for `SomeModel.knex().table(SomeModel.tableName)`.
*
* @returns {knex.QueryBuilder}
*/
/**
* Get/Set the knex instance for this model class.
*
* Subclasses inherit the connection. A system-wide knex instance can thus be set by calling
* `Model.knex(knex)`. This works even after subclasses have been created.
*
* ```js
* var knex = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database.db'
* }
* });
*
* Model.knex(knex);
* knex = Model.knex();
* ```
*
* @param {knex=} knex
* The knex to set.
*
* @returns {knex|undefined}
*/
Model.knex = function (knex) {
if (arguments.length) {
this.$$knex = knex;
} else {
var modelClass = this;
while (modelClass && !modelClass.$$knex) {
var proto = modelClass.prototype.__proto__;
modelClass = proto && proto.constructor;
}, {
key: 'knexQuery',
value: function knexQuery() {
return this.knex().table(this.tableName);
}
return modelClass && modelClass.$$knex;
}
};
/**
* Creates a subclass of this class that is bound to the given knex.
*
* This method can be used to bind a Model subclass to multiple databases for example in
* a multi tenant system.
*
* Example:
*
* ```js
* var knex1 = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database1.db'
* }
* });
*
* var knex2 = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database2.db'
* }
* });
*
* SomeModel.knex(null);
*
* var BoundModel1 = SomeModel.bindKnex(knex1);
* var BoundModel2 = SomeModel.bindKnex(knex2);
*
* // Throws since the knex instance is null.
* SomeModel.query().then();
*
* // Works.
* BoundModel1.query().then(function (models) {
* console.log(models[0] instanceof SomeModel); // --> true
* console.log(models[0] instanceof BoundModel1); // --> true
* });
*
* // Works.
* BoundModel2.query().then(function (models) {
* console.log(models[0] instanceof SomeModel); // --> true
* console.log(models[0] instanceof BoundModel2); // --> true
* });
*
* ```
*
* @param {knex} knex
* @returns {Model}
*/
/**
* Shortcut for `SomeModel.knex().raw()`.
*/
Model.raw = function () {
var knex = this.knex();
return knex.raw.apply(knex, arguments);
};
}, {
key: 'bindKnex',
value: function bindKnex(knex) {
var ModelClass = this;
/**
* Shortcut for `SomeModel.knex().fn`.
*/
Model.fn = function () {
var knex = this.knex();
return knex.fn;
};
if (!knex.$$objection) {
knex.$$objection = {};
knex.$$objection.id = _lodash2.default.uniqueId();
knex.$$objection.boundModels = (0, _create2.default)(null);
}
/**
* Shortcut for `SomeModel.knex().client.formatter()`.
*
* @return {Formatter}
*/
Model.formatter = function () {
return this.knex().client.formatter();
};
// Check if this model class has already been bound to the given knex.
if (knex.$$objection.boundModels[ModelClass.tableName]) {
return knex.$$objection.boundModels[ModelClass.tableName];
}
/**
* Shortcut for `SomeModel.knex().table(SomeModel.tableName)`.
*
* @returns {knex.QueryBuilder}
*/
Model.knexQuery = function () {
return this.knex().table(this.tableName);
};
// Create a new subclass of this class.
var BoundModelClass = (0, _inheritModel2.default)(ModelClass);
/**
* Creates a subclass of this class that is bound to the given knex.
*
* This method can be used to bind a Model subclass to multiple databases for example in
* a multi tenant system.
*
* Example:
*
* ```js
* var knex1 = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database1.db'
* }
* });
*
* var knex2 = require('knex')({
* client: 'sqlite3',
* connection: {
* filename: 'database2.db'
* }
* });
*
* SomeModel.knex(null);
*
* var BoundModel1 = SomeModel.bindKnex(knex1);
* var BoundModel2 = SomeModel.bindKnex(knex2);
*
* // Throws since the knex instance is null.
* SomeModel.query().then();
*
* // Works.
* BoundModel1.query().then(function (models) {
* console.log(models[0] instanceof SomeModel); // --> true
* console.log(models[0] instanceof BoundModel1); // --> true
* });
*
* // Works.
* BoundModel2.query().then(function (models) {
* console.log(models[0] instanceof SomeModel); // --> true
* console.log(models[0] instanceof BoundModel2); // --> true
* });
*
* ```
*
* @param {knex} knex
* @returns {Model}
*/
Model.bindKnex = function (knex) {
var ModelClass = this;
BoundModelClass.knex(knex);
knex.$$objection.boundModels[ModelClass.tableName] = BoundModelClass;
if (!knex.$$objection) {
knex.$$objection = {};
knex.$$objection.id = _.uniqueId();
knex.$$objection.boundModels = Object.create(null);
}
BoundModelClass.$$relations = _lodash2.default.reduce(ModelClass.getRelations(), function (relations, relation, relationName) {
relations[relationName] = relation.bindKnex(knex);
return relations;
}, (0, _create2.default)(null));
// Check if this model class has already been bound to the given knex.
if (knex.$$objection.boundModels[ModelClass.tableName]) {
return knex.$$objection.boundModels[ModelClass.tableName];
}
return BoundModelClass;
}
// Create a new subclass of this class.
var BoundModelClass = inheritModel(ModelClass);
/**
* Alias for bindKnex.
*
* ```js
* var Person = require('./models/Person');
* var transaction;
*
* objection.transaction.start(Person).then(function (trx) {
* transaction = trx;
* return Person
* .bindTransaction(transaction)
* .query()
* .insert({firstName: 'Jennifer'});
* }).then(function (jennifer) {
* return Person
* .bindTransaction(transaction)
* .query()
* .patch({lastName: 'Lawrence'})
* .where('id', jennifer.id);
* }).then(function () {
* return transaction.commit();
* }).catch(function () {
* return transaction.rollback();
* });
* ```
*
* @param trx
* @returns {Model}
*/
BoundModelClass.knex(knex);
knex.$$objection.boundModels[ModelClass.tableName] = BoundModelClass;
}, {
key: 'bindTransaction',
value: function bindTransaction(trx) {
return this.bindKnex(trx);
}
BoundModelClass.$$relations = _.reduce(ModelClass.getRelations(), function (relations, relation, relationName) {
relations[relationName] = relation.bindKnex(knex);
return relations;
}, Object.create(null));
/**
* Ensures that the given model is an instance of this class.
*
* If `model` is already an instance of this class, nothing is done.
*
* @param {Model|Object} model
* @param {ModelOptions=} options
* @returns {Model}
*/
return BoundModelClass;
};
}, {
key: 'ensureModel',
value: function ensureModel(model, options) {
var ModelClass = this;
/**
* Alias for bindKnex.
*
* ```js
* var Person = require('./models/Person');
* var transaction;
*
* objection.transaction.start(Person).then(function (trx) {
* transaction = trx;
* return Person
* .bindTransaction(transaction)
* .query()
* .insert({firstName: 'Jennifer'});
* }).then(function (jennifer) {
* return Person
* .bindTransaction(transaction)
* .query()
* .patch({lastName: 'Lawrence'})
* .where('id', jennifer.id);
* }).then(function () {
* return transaction.commit();
* }).catch(function () {
* return transaction.rollback();
* });
* ```
*
* @param trx
* @returns {Model}
*/
Model.bindTransaction = function (trx) {
return this.bindKnex(trx);
};
if (!model) {
return null;
}
/**
* Ensures that the given model is an instance of this class.
*
* If `model` is already an instance of this class, nothing is done.
*
* @param {Model|Object} model
* @param {ModelOptions=} options
* @returns {Model}
*/
Model.ensureModel = function (model, options) {
var ModelClass = this;
if (model instanceof ModelClass) {
return model;
} else if (model instanceof Model) {
throw new Error('model is already an instance of another Model');
} else {
return ModelClass.fromJson(model, options);
}
}
if (!model) {
return null;
}
/**
* Ensures that each element in the given array is an instance of this class.
*
* If an element is already an instance of this class, nothing is done for it.
*
* @param {Array.<Model|Object>} input
* @param {ModelOptions=} options
* @returns {Array.<Model>}
*/
if (model instanceof ModelClass) {
return model;
} else if (model instanceof Model) {
throw new Error('model is already an instance of another Model');
} else {
return ModelClass.fromJson(model, options);
}
};
}, {
key: 'ensureModelArray',
value: function ensureModelArray(input, options) {
var ModelClass = this;
/**
* Ensures that each element in the given array is an instance of this class.
*
* If an element is already an instance of this class, nothing is done for it.
*
* @param {Array.<Model|Object>} input
* @param {ModelOptions=} options
* @returns {Array.<Model>}
*/
Model.ensureModelArray = function (input, options) {
var ModelClass = this;
if (!input) {
return [];
}
if (!input) {
return [];
}
if (_lodash2.default.isArray(input)) {
var models = new Array(input.length);
if (_.isArray(input)) {
var models = new Array(input.length);
for (var i = 0, l = input.length; i < l; ++i) {
models[i] = ModelClass.ensureModel(input[i], options);
}
for (var i = 0, l = input.length; i < l; ++i) {
models[i] = ModelClass.ensureModel(input[i], options);
return models;
} else {
return [ModelClass.ensureModel(input, options)];
}
}
return models;
} else {
return [ModelClass.ensureModel(input, options)];
}
};
/**
* Returns the name of the identifier property.
*
* The identifier property is equal to the `idColumn` if `$parseDatabaseJson` is not
* implemented. If `$parseDatabaseJson` is implemented it may change the id property's
* name. This method passes the `idColumn` through `$parseDatabaseJson`.
*
* @returns {string}
*/
/**
* Returns the name of the identifier property.
*
* The identifier property is equal to the `idColumn` if `$parseDatabaseJson` is not
* implemented. If `$parseDatabaseJson` is implemented it may change the id property's
* name. This method passes the `idColumn` through `$parseDatabaseJson`.
*
* @returns {String}
*/
Model.getIdProperty = function () {
var idProperty = this.columnNameToPropertyName(this.idColumn);
}, {
key: 'getIdProperty',
value: function getIdProperty() {
var idProperty = this.columnNameToPropertyName(this.idColumn);
if (!idProperty) {
throw new Error(this.name +
'.$parseDatabaseJson probably changes the value of the id column `' + this.idColumn +
'` which is a no-no.');
}
if (!idProperty) {
throw new Error(this.name + '.$parseDatabaseJson probably changes the value of the id column `' + this.idColumn + '` which is a no-no.');
}
return idProperty;
};
return idProperty;
}
/**
* Full identifier column name like 'SomeTable.id'.
*
* @returns {String}
*/
Model.getFullIdColumn = function () {
return this.tableName + '.' + this.idColumn;
};
/**
* Full identifier column name like 'SomeTable.id'.
*
* @returns {string}
*/
/**
* All relations of this class.
*
* @return {Object.<String, Relation>}
*/
Model.getRelations = function () {
var ModelClass = this;
}, {
key: 'getFullIdColumn',
value: function getFullIdColumn() {
return this.tableName + '.' + this.idColumn;
}
if (!this.$$relations) {
// Lazy-load the relations to prevent require loops.
this.$$relations = _.reduce(this.relationMappings, function (relations, mapping, relationName) {
relations[relationName] = new mapping.relation(relationName, ModelClass);
relations[relationName].setMapping(mapping);
return relations;
}, Object.create(null));
}
/**
* All relations of this class.
*
* @return {Object.<string, Relation>}
*/
return this.$$relations;
};
}, {
key: 'getRelations',
value: function getRelations() {
var ModelClass = this;
/**
* Get a relation by name.
*
* This should not be used to make queries. Use `$relatedQuery` or `loadRelated` instead.
*
* @return {Relation}
*/
Model.getRelation = function (name) {
var relation = this.getRelations()[name];
if (!this.$$relations) {
// Lazy-load the relations to prevent require loops.
this.$$relations = _lodash2.default.reduce(this.relationMappings, function (relations, mapping, relationName) {
relations[relationName] = new mapping.relation(relationName, ModelClass);
relations[relationName].setMapping(mapping);
return relations;
}, (0, _create2.default)(null));
}
if (!relation) {
throw new Error("model class '" + this.name + "' doesn't have relation '" + name + "'");
}
return this.$$relations;
}
return relation;
};
/**
* Get a relation by name.
*
* This should not be used to make queries. Use `$relatedQuery` or `loadRelated` instead.
*
* @return {Relation}
*/
/**
* Exactly like $loadRelated but for multiple instances.
*
* ```js
* Person.loadRelated([person1, person2], 'children.pets').then(function (persons) {
* var person1 = persons[0];
* var person2 = persons[1];
* });
* ```
*
* Relations can be filtered by giving named filter functions as arguments
* to the relations:
*
* ```js
* Person
* .loadRelated([person1, person2], 'children(orderByAge).[pets(onlyDogs, orderByName), movies]', {
* orderByAge: function (builder) {
* builder.orderBy('age')
* },
* orderByName: function (builder) {
* builder.orderBy('name');
* },
* onlyDogs: function (builder) {
* builder.where('species', 'dog')
* }
* })
* .then(function (persons) {
* console.log(persons[1].children.pets[0]);
* });
* ```
*
* @param {Array.<Model|Object>} $models
* @param {String|RelationExpression} expression
* @param {Object.<String, Function(QueryBuilder)>=} filters
* @returns {Promise}
*/
Model.loadRelated = function ($models, expression, filters) {
if (!(expression instanceof RelationExpression)) {
expression = RelationExpression.parse(expression);
}
}, {
key: 'getRelation',
value: function getRelation(name) {
var relation = this.getRelations()[name];
if (!expression) {
throw new Error('invalid expression ' + expression);
}
if (!relation) {
throw new Error("model class '" + this.name + "' doesn't have relation '" + name + "'");
}
return new EagerFetcher({
modelClass: this,
models: this.ensureModelArray($models),
eager: expression,
filters: filters
}).fetch().then(function (models) {
return _.isArray($models) ? models : models[0];
});
};
return relation;
}
/**
* Traverses the relation tree of a list of models.
*
* Calls the callback for each related model recursively. The callback is called
* also for the input models themselves.
*
* There are two ways to call this method:
*
* ```js
* Model.traverse(models, function (model, parentModel, relationName) {
* doSomething(model);
* });
* ```
*
* and
*
* ```js
* Model.traverse(Person, models, function (person, parentModel, relationName) {
* doSomethingForPerson(person);
* });
* ```
*
* In the second example the traverser function is only called for `Person` instances.
*
* @param {Function=} filterConstructor
* If this optional constructor is given, the `traverser` is only called for
* models for which `model instanceof filterConstructor` returns true.
*
* @param {Model|Array.<Model>} models
* The model(s) whose relation trees to traverse.
*
* @param {function(Model, Model, String)} traverser
* The traverser function that is called for each model. The first argument
* is the model itself. If the model is in a relation of some other model
* the second argument is the parent model and the third argument is the
* name of the relation.
*
* @return {Model}
*/
Model.traverse = function (filterConstructor, models, traverser) {
filterConstructor = filterConstructor || null;
/**
* Exactly like $loadRelated but for multiple instances.
*
* ```js
* Person.loadRelated([person1, person2], 'children.pets').then(function (persons) {
* var person1 = persons[0];
* var person2 = persons[1];
* });
* ```
*
* Relations can be filtered by giving named filter functions as arguments
* to the relations:
*
* ```js
* Person
* .loadRelated([person1, person2], 'children(orderByAge).[pets(onlyDogs, orderByName), movies]', {
* orderByAge: function (builder) {
* builder.orderBy('age')
* },
* orderByName: function (builder) {
* builder.orderBy('name');
* },
* onlyDogs: function (builder) {
* builder.where('species', 'dog')
* }
* })
* .then(function (persons) {
* console.log(persons[1].children.pets[0]);
* });
* ```
*
* @param {Array.<Model|Object>} $models
* @param {string|RelationExpression} expression
* @param {Object.<string, function(QueryBuilder)>=} filters
* @returns {Promise}
*/
if (_.isUndefined(traverser)) {
traverser = models;
models = filterConstructor;
filterConstructor = null;
}
}, {
key: 'loadRelated',
value: function loadRelated($models, expression, filters) {
if (!(expression instanceof _RelationExpression2.default)) {
expression = _RelationExpression2.default.parse(expression);
}
if (!_.isFunction(traverser)) {
throw new Error('traverser must be a function');
}
return new _EagerFetcher2.default({
modelClass: this,
models: this.ensureModelArray($models),
eager: expression,
filters: filters
}).fetch().then(function (models) {
return _lodash2.default.isArray($models) ? models : models[0];
});
}
traverse(models, null, null, filterConstructor, traverser);
return this;
};
/**
* Traverses the relation tree of a list of models.
*
* Calls the callback for each related model recursively. The callback is called
* also for the input models themselves.
*
* There are two ways to call this method:
*
* ```js
* Model.traverse(models, function (model, parentModel, relationName) {
* doSomething(model);
* });
* ```
*
* and
*
* ```js
* Model.traverse(Person, models, function (person, parentModel, relationName) {
* doSomethingForPerson(person);
* });
* ```
*
* In the second example the traverser function is only called for `Person` instances.
*
* @param {function=} filterConstructor
* If this optional constructor is given, the `traverser` is only called for
* models for which `model instanceof filterConstructor` returns true.
*
* @param {Model|Array.<Model>} models
* The model(s) whose relation trees to traverse.
*
* @param {function(Model, Model, string)} traverser
* The traverser function that is called for each model. The first argument
* is the model itself. If the model is in a relation of some other model
* the second argument is the parent model and the third argument is the
* name of the relation.
*
* @return {Model}
*/
/**
* @protected
* @returns {Array.<String>}
*/
Model.$$getJsonAttributes = function () {
var self = this;
}, {
key: 'traverse',
value: function traverse(filterConstructor, models, traverser) {
filterConstructor = filterConstructor || null;
// If the jsonAttributes property is not set, try to create it based
// on the jsonSchema. All properties that are objects or arrays must
// be converted to JSON.
if (!this.jsonAttributes && this.jsonSchema) {
this.jsonAttributes = [];
if (_lodash2.default.isUndefined(traverser)) {
traverser = models;
models = filterConstructor;
filterConstructor = null;
}
_.each(this.jsonSchema.properties, function (prop, propName) {
var types = _.compact(ensureArray(prop.type));
if (types.length === 0 && _.isArray(prop.anyOf)) {
types = _.flattenDeep(_.pluck(prop.anyOf, 'type'));
if (!_lodash2.default.isFunction(traverser)) {
throw new Error('traverser must be a function');
}
if (types.length === 0 && _.isArray(prop.oneOf)) {
types = _.flattenDeep(_.pluck(prop.oneOf, 'type'));
_traverse(models, null, null, filterConstructor, traverser);
return this;
}
/**
* @protected
* @returns {Array.<string>}
*/
}, {
key: '$$getJsonAttributes',
value: function $$getJsonAttributes() {
var _this4 = this;
// If the jsonAttributes property is not set, try to create it based
// on the jsonSchema. All properties that are objects or arrays must
// be converted to JSON.
if (!this.jsonAttributes && this.jsonSchema) {
this.jsonAttributes = [];
_lodash2.default.each(this.jsonSchema.properties, function (prop, propName) {
var types = _lodash2.default.compact(ensureArray(prop.type));
if (types.length === 0 && _lodash2.default.isArray(prop.anyOf)) {
types = _lodash2.default.flattenDeep(_lodash2.default.pluck(prop.anyOf, 'type'));
}
if (types.length === 0 && _lodash2.default.isArray(prop.oneOf)) {
types = _lodash2.default.flattenDeep(_lodash2.default.pluck(prop.oneOf, 'type'));
}
if (_lodash2.default.contains(types, 'object') || _lodash2.default.contains(types, 'array')) {
_this4.jsonAttributes.push(propName);
}
});
}
if (_.contains(types, 'object') || _.contains(types, 'array')) {
self.jsonAttributes.push(propName);
if (!_lodash2.default.isArray(this.jsonAttributes)) {
this.jsonAttributes = [];
}
});
}
if (!_.isArray(this.jsonAttributes)) {
this.jsonAttributes = [];
}
return this.jsonAttributes;
}
}]);
return Model;
})(_ModelBase3.default), _class.QueryBuilder = _QueryBuilder2.default, _class.RelatedQueryBuilder = _QueryBuilder2.default, _class.OneToOneRelation = _OneToOneRelation2.default, _class.OneToManyRelation = _OneToManyRelation2.default, _class.ManyToManyRelation = _ManyToManyRelation2.default, _class.tableName = null, _class.idColumn = 'id', _class.uidProp = '#id', _class.uidRefProp = '#ref', _class.propRefRegex = /#ref{([^\.]+)\.([^}]+)}/g, _class.jsonAttributes = null, _class.relationMappings = null, _class.$$knex = null, _class.$$relations = null, _temp);
return this.jsonAttributes;
};
/**
* @private
*/
exports.default = Model;
function ensureArray(obj) {
if (_.isArray(obj)) {
if (_lodash2.default.isArray(obj)) {
return obj;

@@ -1408,8 +1545,8 @@ } else {

*/
function traverse(models, parent, relationName, modelClass, callback) {
if (!_.isObject(models)) {
function _traverse(models, parent, relationName, modelClass, callback) {
if (!_lodash2.default.isObject(models)) {
return;
}
if (_.isArray(models)) {
if (_lodash2.default.isArray(models)) {
for (var i = 0, l = models.length; i < l; ++i) {

@@ -1419,3 +1556,3 @@ traverseOne(models[i], parent, relationName, modelClass, callback);

} else {
traverseOne(models, parent, relationName, modelClass, callback)
traverseOne(models, parent, relationName, modelClass, callback);
}

@@ -1437,8 +1574,6 @@ }

for (var relName in model.constructor.getRelations()) {
if (_.has(model, relName)) {
traverse(model[relName], model, relName, modelClass, callback);
if (_lodash2.default.has(model, relName)) {
_traverse(model[relName], model, relName, modelClass, callback);
}
}
}
module.exports = Model;
}

@@ -1,17 +0,48 @@

"use strict";
'use strict';
var _ = require('lodash')
, tv4 = require('tv4')
, tv4Formats = require('tv4-formats')
, utils = require('./../utils')
, ValidationError = require('./../ValidationError');
var _class, _temp;
// Add validation formats, so that for example the following schema validation works:
// createTime: {type: 'string', format: 'date-time'}
tv4.addFormat(tv4Formats);
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _tv = require('tv4');
var _tv2 = _interopRequireDefault(_tv);
var _tv4Formats = require('tv4-formats');
var _tv4Formats2 = _interopRequireDefault(_tv4Formats);
var _utils = require('../utils');
var _utils2 = _interopRequireDefault(_utils);
var _ValidationError = require('../ValidationError');
var _ValidationError2 = _interopRequireDefault(_ValidationError);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @typedef {Object} ModelOptions
*
* @property {Boolean} [patch]
* @property {boolean} [patch]
* If true the json is treated as a patch and the `required` field of the json schema is

@@ -21,3 +52,3 @@ * ignored in the validation. This allows us to create models with a subset of required

*
* @property {Boolean} [skipValidation]
* @property {boolean} [skipValidation]
* If true the json schema validation is skipped.

@@ -104,589 +135,638 @@ */

*/
function ModelBase() {
// Nothing to do here.
}
var ModelBase = (_temp = _class = (function () {
function ModelBase() {
(0, _classCallCheck3.default)(this, ModelBase);
}
/**
* This is called before validation.
*
* Here you can dynamically edit the jsonSchema if needed.
*
* @param {Object} jsonSchema
* A deep clone of this class's jsonSchema.
*
* @param {Object} json
* The JSON object to be validated.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The (possibly) modified jsonSchema.
*/
ModelBase.prototype.$beforeValidate = function (jsonSchema, json, options) {
/* istanbul ignore next */
return jsonSchema;
};
(0, _createClass3.default)(ModelBase, [{
key: '$beforeValidate',
/**
* Validates the given JSON object.
*
* Calls `$beforeValidation` and `$afterValidation` methods. This method is called
* automatically from `fromJson` and `$setJson` methods. This method can also be
* called explicitly when needed.
*
* @throws {ValidationError}
* If validation fails.
*
* @param {Object=} json
* If not given ==> this.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The input json
*/
ModelBase.prototype.$validate = function (json, options) {
var ModelClass = this.constructor;
var jsonSchema = ModelClass.jsonSchema;
var required;
/**
* This is called before validation.
*
* Here you can dynamically edit the jsonSchema if needed.
*
* @param {Object} jsonSchema
* A deep clone of this class's jsonSchema.
*
* @param {Object} json
* The JSON object to be validated.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The (possibly) modified jsonSchema.
*/
options = options || {};
json = json || this;
/**
* @private
*/
value: function $beforeValidate(jsonSchema, json, options) {
/* istanbul ignore next */
return jsonSchema;
}
if (!jsonSchema || options.skipValidation) {
return json;
}
/**
* Validates the given JSON object.
*
* Calls `$beforeValidation` and `$afterValidation` methods. This method is called
* automatically from `fromJson` and `$setJson` methods. This method can also be
* called explicitly when needed.
*
* @throws {ValidationError}
* If validation fails.
*
* @param {Object=} json
* If not given ==> this.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The input json
*/
// No need to call $beforeValidate (and clone the jsonSchema) if $beforeValidate has not been overwritten.
if (this.$beforeValidate !== ModelBase.prototype.$beforeValidate) {
jsonSchema = _.cloneDeep(jsonSchema);
jsonSchema = this.$beforeValidate(jsonSchema, json, options);
}
/**
* @private
*/
var report = tryValidate(jsonSchema, json, options);
var validationError = parseValidationError(report);
/**
* The optional schema against which the JSON is validated.
*
* The jsonSchema can be dynamically modified in the `$beforeValidate` method.
*
* Must follow http://json-schema.org specification. If null no validation is done.
*
* @see $beforeValidate()
* @see $validate()
* @see $afterValidate()
*
* @type {Object}
*/
if (validationError) {
throw validationError;
}
}, {
key: '$validate',
value: function $validate() {
var json = arguments.length <= 0 || arguments[0] === undefined ? this : arguments[0];
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
this.$afterValidate(json, options);
return json;
};
var ModelClass = this.constructor;
var jsonSchema = ModelClass.jsonSchema;
/**
* This is called after successful validation.
*
* You can do further validation here and throw a ValidationError if something goes wrong.
*
* @param {Object=} json
* The JSON object to validate.
*
* @param {ModelOptions=} options
* Optional options.
*/
ModelBase.prototype.$afterValidate = function (json, options) {
// Do nothing by default.
};
if (!jsonSchema || options.skipValidation) {
return json;
}
/**
* This is called when a ModelBase is created from a database JSON object.
*
* Converts the JSON object from the database format to the internal format.
*
* @note This function must handle the case where any subset of the columns comes
* in the `json` argument. You cannot assume that all columns are present as it
* depends on the select statement. There can also be additional columns because
* of join clauses, aliases etc.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in database format.
*
* @return {Object}
* The JSON object in internal format.
*/
ModelBase.prototype.$parseDatabaseJson = function (json) {
return json;
};
// No need to call $beforeValidate (and clone the jsonSchema) if $beforeValidate has not been overwritten.
if (this.$beforeValidate !== ModelBase.prototype.$beforeValidate) {
jsonSchema = _lodash2.default.cloneDeep(jsonSchema);
jsonSchema = this.$beforeValidate(jsonSchema, json, options);
}
/**
* This is called when a ModelBase is converted to database format.
*
* Converts the JSON object from the internal format to the database format.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in internal format.
*
* @return {Object}
* The JSON object in database format.
*/
ModelBase.prototype.$formatDatabaseJson = function (json) {
return json;
};
var report = tryValidate(jsonSchema, json, options);
var validationError = parseValidationError(report);
/**
* This is called when a ModelBase is created from a JSON object.
*
* Converts the JSON object to the internal format.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in external format.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The JSON object in internal format.
*/
ModelBase.prototype.$parseJson = function (json, options) {
return json;
};
if (validationError) {
throw validationError;
}
/**
* This is called when a ModelBase is converted to JSON.
*
* @note Remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in internal format
*
* @return {Object}
* The JSON object in external format.
*/
ModelBase.prototype.$formatJson = function (json) {
return json;
};
this.$afterValidate(json, options);
return json;
}
/**
* Exports this model as a database JSON object.
*
* Calls `$formatDatabaseJson()`.
*
* @return {Object}
* This model as a JSON object in database format.
*/
ModelBase.prototype.$toDatabaseJson = function () {
return this.$$toJson(true, null, null);
};
/**
* This is called after successful validation.
*
* You can do further validation here and throw a ValidationError if something goes wrong.
*
* @param {Object=} json
* The JSON object to validate.
*
* @param {ModelOptions=} options
* Optional options.
*/
/**
* Exports this model as a JSON object.
*
* Calls `$formatJson()`.
*
* @return {Object}
* This model as a JSON object.
*/
ModelBase.prototype.$toJson = function () {
return this.$$toJson(false, null, null);
};
}, {
key: '$afterValidate',
value: function $afterValidate(json, options) {}
// Do nothing by default.
/**
* Alias for `this.$toJson()`.
*
* For JSON.stringify compatibility.
*/
ModelBase.prototype.toJSON = function () {
return this.$toJson();
};
/**
* This is called when a ModelBase is created from a database JSON object.
*
* Converts the JSON object from the database format to the internal format.
*
* @note This function must handle the case where any subset of the columns comes
* in the `json` argument. You cannot assume that all columns are present as it
* depends on the select statement. There can also be additional columns because
* of join clauses, aliases etc.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in database format.
*
* @return {Object}
* The JSON object in internal format.
*/
/**
* Sets the values from a JSON object.
*
* Validates the JSON before setting values. Calls `this.$parseJson()`.
*
* @param {Object} json
* The JSON object to set.
*
* @param {ModelOptions=} options
* Optional options.
*
* @throws ValidationError
* If validation fails.
*/
ModelBase.prototype.$setJson = function (json, options) {
json = json || {};
options = options || {};
}, {
key: '$parseDatabaseJson',
value: function $parseDatabaseJson(json) {
return json;
}
if (!_.isObject(json)
|| _.isString(json)
|| _.isNumber(json)
|| _.isDate(json)
|| _.isArray(json)
|| _.isFunction(json)
|| _.isTypedArray(json)
|| _.isRegExp(json)) {
/**
* This is called when a ModelBase is converted to database format.
*
* Converts the JSON object from the internal format to the database format.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in internal format.
*
* @return {Object}
* The JSON object in database format.
*/
throw new Error('You should only pass objects to $setJson method. '
+ '$setJson method was given an invalid value '
+ json);
}
}, {
key: '$formatDatabaseJson',
value: function $formatDatabaseJson(json) {
return json;
}
if (!options.patch) {
json = mergeWithDefaults(this.constructor.jsonSchema, json);
}
/**
* This is called when a ModelBase is created from a JSON object.
*
* Converts the JSON object to the internal format.
*
* @note If you override this remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in external format.
*
* @param {ModelOptions=} options
* Optional options.
*
* @return {Object}
* The JSON object in internal format.
*/
json = this.$parseJson(json, options);
json = this.$validate(json, options);
}, {
key: '$parseJson',
value: function $parseJson(json, options) {
return json;
}
this.$set(json);
};
/**
* This is called when a ModelBase is converted to JSON.
*
* @note Remember to call the super class's implementation.
*
* @param {Object} json
* The JSON object in internal format
*
* @return {Object}
* The JSON object in external format.
*/
/**
* Sets the values from a JSON object in database format.
*
* Calls `this.$parseDatabaseJson()`.
*
* @param {Object} json
* The JSON object in database format.
*/
ModelBase.prototype.$setDatabaseJson = function (json) {
json = this.$parseDatabaseJson(json || {});
}, {
key: '$formatJson',
value: function $formatJson(json) {
return json;
}
for (var key in json) {
this[key] = json[key];
}
};
/**
* Exports this model as a database JSON object.
*
* Calls `$formatDatabaseJson()`.
*
* @return {Object}
* This model as a JSON object in database format.
*/
/**
* Sets the values from another model or object.
*
* Unlike $setJson, this doesn't call any `$parseJson` methods or validate the input.
* This simply sets each value in the object to this object.
*
* @param {Object} obj
*/
ModelBase.prototype.$set = function (obj) {
var self = this;
}, {
key: '$toDatabaseJson',
value: function $toDatabaseJson() {
return this.$$toJson(true, null, null);
}
_.each(obj, function $setLooper(value, key) {
if (key.charAt(0) !== '$' && !_.isFunction(value)) {
self[key] = value;
/**
* Exports this model as a JSON object.
*
* Calls `$formatJson()`.
*
* @return {Object}
* This model as a JSON object.
*/
}, {
key: '$toJson',
value: function $toJson() {
return this.$$toJson(false, null, null);
}
});
return this;
};
/**
* Alias for `this.$toJson()`.
*
* For JSON.stringify compatibility.
*/
/**
* Omits a set of properties.
*
* The selected properties are set to `undefined`. Note that this is done in-place.
* Properties are set to undefined instead of deleting them for performance reasons
* (V8 doesn't like delete).
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit('lastName')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit('lastName')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit('lastName')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* @param {Array.<String>|Object.<String, Boolean>} keys
*/
ModelBase.prototype.$omit = function () {
if (arguments.length === 1 && _.isObject(arguments[0])) {
var keys = arguments[0];
}, {
key: 'toJSON',
value: function toJSON() {
return this.$toJson();
}
if (_.isArray(keys)) {
omitArray(this, keys);
} else {
omitObject(this, keys);
/**
* Sets the values from a JSON object.
*
* Validates the JSON before setting values. Calls `this.$parseJson()`.
*
* @param {Object} json
* The JSON object to set.
*
* @param {ModelOptions=} options
* Optional options.
*
* @returns {ModelBase} `this` for chaining.
*
* @throws ValidationError
* If validation fails.
*/
}, {
key: '$setJson',
value: function $setJson(json) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
json = json || {};
if (!_lodash2.default.isObject(json) || _lodash2.default.isString(json) || _lodash2.default.isNumber(json) || _lodash2.default.isDate(json) || _lodash2.default.isArray(json) || _lodash2.default.isFunction(json) || _lodash2.default.isTypedArray(json) || _lodash2.default.isRegExp(json)) {
throw new Error('You should only pass objects to $setJson method. ' + '$setJson method was given an invalid value ' + json);
}
if (!options.patch) {
json = mergeWithDefaults(this.constructor.jsonSchema, json);
}
json = this.$parseJson(json, options);
json = this.$validate(json, options);
return this.$set(json);
}
} else {
omitArray(this, _.toArray(arguments));
}
return this;
};
/**
* Sets the values from a JSON object in database format.
*
* Calls `this.$parseDatabaseJson()`.
*
* @param {Object} json
* The JSON object in database format.
*
* @returns {ModelBase} `this` for chaining.
*/
/**
* Picks a set of properties.
*
* All other properties but the selected ones are set to `undefined`. Note that
* this is done in-place. Properties are set to undefined instead of deleting
* them for performance reasons (V8 doesn't like delete).
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick('firstName', 'age')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick(['firstName', 'age'])
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick({firstName: true, age: true})
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* @param {Array.<String>|Object.<String, Boolean>} keys
*/
ModelBase.prototype.$pick = function () {
if (arguments.length === 1 && _.isObject(arguments[0])) {
var keys = arguments[0];
}, {
key: '$setDatabaseJson',
value: function $setDatabaseJson() {
var json = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
if (_.isArray(keys)) {
pickArray(this, keys);
} else {
pickObject(this, keys);
json = this.$parseDatabaseJson(json);
for (var key in json) {
this[key] = json[key];
}
return this;
}
} else {
pickArray(this, _.toArray(arguments));
}
return this;
};
/**
* Sets the values from another model or object.
*
* Unlike $setJson, this doesn't call any `$parseJson` methods or validate the input.
* This simply sets each value in the object to this object.
*
* @param {Object} obj
* @returns {ModelBase} `this` for chaining.
*/
/**
* Returns a deep copy of this model.
*
* If this object has instances of ModelBase as properties (or arrays of them)
* they are cloned using their `.$clone()` method.
*
* @return {ModelBase}
*/
ModelBase.prototype.$clone = function () {
var clone = new this.constructor();
}, {
key: '$set',
value: function $set(obj) {
var self = this;
_.each(this, function cloneLooper(value, key) {
if (_.isObject(value)) {
clone[key] = cloneObject(value);
} else {
clone[key] = value;
_lodash2.default.each(obj, function (value, key) {
if (key.charAt(0) !== '$' && !_lodash2.default.isFunction(value)) {
self[key] = value;
}
});
return this;
}
});
return clone;
};
/**
* Omits a set of properties.
*
* The selected properties are set to `undefined`. Note that this is done in-place.
* Properties are set to undefined instead of deleting them for performance reasons
* (V8 doesn't like delete).
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit('lastName')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit(['lastName'])
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$omit({lastName: true})
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* @param {string|Array.<string>|Object.<string, boolean>} keys
* @returns {ModelBase} `this` for chaining.
*/
/**
* The optional schema against which the JSON is validated.
*
* The jsonSchema can be dynamically modified in the `$beforeValidate` method.
*
* Must follow http://json-schema.org specification. If null no validation is done.
*
* @see $beforeValidate()
* @see $validate()
* @see $afterValidate()
*
* @type {Object}
*/
ModelBase.jsonSchema = null;
}, {
key: '$omit',
value: function $omit() {
if (arguments.length === 1 && _lodash2.default.isObject(arguments[0])) {
var keys = arguments[0];
/**
* @private
*/
ModelBase.$$colToProp = null;
if (_lodash2.default.isArray(keys)) {
omitArray(this, keys);
} else {
omitObject(this, keys);
}
} else {
omitArray(this, _lodash2.default.toArray(arguments));
}
/**
* @private
*/
ModelBase.$$propToCol = null;
return this;
}
/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {function}
*/
ModelBase.extend = function (subclassConstructor) {
if (_.isEmpty(subclassConstructor.name)) {
throw new Error('Each ModelBase subclass constructor must have a name');
}
/**
* Picks a set of properties.
*
* All other properties but the selected ones are set to `undefined`. Note that
* this is done in-place. Properties are set to undefined instead of deleting
* them for performance reasons (V8 doesn't like delete).
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick('firstName', 'age')
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick(['firstName', 'age'])
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* ```js
* var json = person
* .fromJson({firstName: 'Jennifer', lastName: 'Lawrence', age: 24})
* .$pick({firstName: true, age: true})
* .toJSON();
*
* console.log(_.has(json, 'lastName')); // --> false
* ```
*
* @param {string|Array.<string>|Object.<string, boolean>} keys
* @returns {ModelBase} `this` for chaining.
*/
utils.inherits(subclassConstructor, this);
return subclassConstructor;
};
}, {
key: '$pick',
value: function $pick() {
if (arguments.length === 1 && _lodash2.default.isObject(arguments[0])) {
var keys = arguments[0];
/**
* Creates a model instance from a JSON object.
*
* The object is checked against `jsonSchema` and an exception is thrown on failure.
*
* @param {Object=} json
* The JSON from which to create the model.
*
* @param {ModelOptions=} options
* Optional options.
*
* @throws ValidationError
* If validation fails.
*/
ModelBase.fromJson = function (json, options) {
var model = new this();
model.$setJson(json || {}, options);
return model;
};
if (_lodash2.default.isArray(keys)) {
pickArray(this, keys);
} else {
pickObject(this, keys);
}
} else {
pickArray(this, _lodash2.default.toArray(arguments));
}
/**
* Creates a model instance from a JSON object in database format.
*
* @param {Object=} json
* The JSON from which to create the model.
*/
ModelBase.fromDatabaseJson = function (json) {
var model = new this();
model.$setDatabaseJson(json || {});
return model;
};
return this;
}
/**
* Omit implementation to use.
*
* The default just sets the property to undefined for performance reasons.
* If the slight performance drop is not an issue for you, you can override
* this method to delete the property instead.
*
* @param {Object} obj
* @param {String} prop
*/
ModelBase.omitImpl = function (obj, prop) {
obj[prop] = undefined;
};
/**
* Returns a deep copy of this model.
*
* If this object has instances of ModelBase as properties (or arrays of them)
* they are cloned using their `.$clone()` method.
*
* @return {ModelBase}
*/
/**
* @ignore
* @param {string} columnName
* @returns {string}
*/
ModelBase.columnNameToPropertyName = function (columnName) {
this.$$ensurePropNameConversionCache();
}, {
key: '$clone',
value: function $clone() {
var clone = new this.constructor();
if (!this.$$colToProp[columnName]) {
this.$$cachePropNameConversion(this.$$columnNameToPropertyName(columnName), columnName);
}
_lodash2.default.each(this, function (value, key) {
if (_lodash2.default.isObject(value)) {
clone[key] = cloneObject(value);
} else {
clone[key] = value;
}
});
return this.$$colToProp[columnName];
};
return clone;
}
/**
* @ignore
* @param {string} propertyName
* @returns {string}
*/
ModelBase.propertyNameToColumnName = function (propertyName) {
this.$$ensurePropNameConversionCache();
/**
* @protected
*/
if (!this.$$propToCol[propertyName]) {
this.$$cachePropNameConversion(propertyName, this.$$propertyNameToColumnName(propertyName));
}
}, {
key: '$$toJson',
value: function $$toJson(createDbJson, omit, pick) {
var json = toJsonImpl(this, createDbJson, omit, pick);
return this.$$propToCol[propertyName];
};
if (createDbJson) {
return this.$formatDatabaseJson(json);
} else {
return this.$formatJson(json);
}
}
/**
* @protected
*/
ModelBase.prototype.$$toJson = function (createDbJson, omit, pick) {
var json = toJsonImpl(this, createDbJson, omit, pick);
/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {function}
*/
if (createDbJson) {
return this.$formatDatabaseJson(json);
} else {
return this.$formatJson(json);
}
};
}], [{
key: 'extend',
value: function extend(subclassConstructor) {
if (_lodash2.default.isEmpty(subclassConstructor.name)) {
throw new Error('Each ModelBase subclass constructor must have a name');
}
/**
* @private
*/
ModelBase.$$ensurePropNameConversionCache = function () {
if (!this.$$propToCol) {
this.$$propToCol = Object.create(null)
}
_utils2.default.inherits(subclassConstructor, this);
return subclassConstructor;
}
if (!this.$$colToProp) {
this.$$colToProp = Object.create(null);
}
};
/**
* Creates a model instance from a JSON object.
*
* The object is checked against `jsonSchema` and an exception is thrown on failure.
*
* @param {Object=} json
* The JSON from which to create the model.
*
* @param {ModelOptions=} options
* Optional options.
*
* @returns {Model}
*
* @throws ValidationError
* If validation fails.
*/
/**
* @private
*/
ModelBase.$$cachePropNameConversion = function (propertyName, columnName) {
this.$$propToCol[propertyName] = columnName;
this.$$colToProp[columnName] = propertyName;
};
}, {
key: 'fromJson',
value: function fromJson(json, options) {
var model = new this();
model.$setJson(json || {}, options);
return model;
}
/**
* @private
*/
ModelBase.$$columnNameToPropertyName = function (columnName) {
var ModelClass = this;
var model = new ModelClass();
var addedProps = _.keys(model.$parseDatabaseJson({}));
/**
* Creates a model instance from a JSON object in database format.
*
* @param {Object=} json
* The JSON from which to create the model.
*
* @returns {Model}
*/
var row = {};
row[columnName] = null;
}, {
key: 'fromDatabaseJson',
value: function fromDatabaseJson(json) {
var model = new this();
model.$setDatabaseJson(json || {});
return model;
}
var props = _.keys(_.omit(model.$parseDatabaseJson(row), addedProps));
var propertyName = _.first(props);
/**
* Omit implementation to use.
*
* The default just sets the property to undefined for performance reasons.
* If the slight performance drop is not an issue for you, you can override
* this method to delete the property instead.
*
* @param {Object} obj
* @param {string} prop
*/
return propertyName || null;
};
}, {
key: 'omitImpl',
value: function omitImpl(obj, prop) {
obj[prop] = undefined;
}
/**
* @private
*/
ModelBase.$$propertyNameToColumnName = function (propertyName) {
var ModelClass = this;
var model = new ModelClass();
var addedCols = _.keys(model.$formatDatabaseJson({}));
/**
* @ignore
* @param {string} columnName
* @returns {string}
*/
var obj = {};
obj[propertyName] = null;
}, {
key: 'columnNameToPropertyName',
value: function columnNameToPropertyName(columnName) {
this.$$ensurePropNameConversionCache();
var cols = _.keys(_.omit(model.$formatDatabaseJson(obj), addedCols));
var columnName = _.first(cols);
if (!this.$$colToProp[columnName]) {
this.$$cachePropNameConversion(_columnNameToPropertyName(this, columnName), columnName);
}
return columnName || null;
};
return this.$$colToProp[columnName];
}
/**
* @ignore
* @param {string} propertyName
* @returns {string}
*/
}, {
key: 'propertyNameToColumnName',
value: function propertyNameToColumnName(propertyName) {
this.$$ensurePropNameConversionCache();
if (!this.$$propToCol[propertyName]) {
this.$$cachePropNameConversion(propertyName, _propertyNameToColumnName(this, propertyName));
}
return this.$$propToCol[propertyName];
}
/**
* @private
*/
}, {
key: '$$ensurePropNameConversionCache',
value: function $$ensurePropNameConversionCache() {
if (!this.$$propToCol) {
this.$$propToCol = (0, _create2.default)(null);
}
if (!this.$$colToProp) {
this.$$colToProp = (0, _create2.default)(null);
}
}
/**
* @private
*/
}, {
key: '$$cachePropNameConversion',
value: function $$cachePropNameConversion(propertyName, columnName) {
this.$$propToCol[propertyName] = columnName;
this.$$colToProp[columnName] = propertyName;
}
}]);
return ModelBase;
})(), _class.jsonSchema = null, _class.$$colToProp = null, _class.$$propToCol = null, _temp);
/**
* @private
*/
exports.default = ModelBase;
function mergeWithDefaults(jsonSchema, json) {

@@ -703,10 +783,10 @@ var merged = null;

if (!_.has(json, key) && _.has(prop, 'default')) {
if (!_lodash2.default.has(json, key) && _lodash2.default.has(prop, 'default')) {
if (merged === null) {
// Only take expensive clone if needed.
merged = _.cloneDeep(json);
merged = _lodash2.default.cloneDeep(json);
}
if (_.isObject(prop.default)) {
merged[key] = _.cloneDeep(prop.default);
if (_lodash2.default.isObject(prop.default)) {
merged[key] = _lodash2.default.cloneDeep(prop.default);
} else {

@@ -729,3 +809,3 @@ merged[key] = prop.default;

function tryValidate(jsonSchema, json, options) {
var required;
var required = undefined;

@@ -738,3 +818,3 @@ try {

return tv4.validateMultiple(json, jsonSchema);
return _tv2.default.validateMultiple(json, jsonSchema);
} finally {

@@ -777,3 +857,3 @@ if (options.patch) {

return new ValidationError(errorHash);
return new _ValidationError2.default(errorHash);
}

@@ -787,10 +867,6 @@

_.each(self, function toJsonImplLooper(value, key) {
if (key.charAt(0) !== '$'
&& !_.isFunction(value)
&& !_.isUndefined(value)
&& (!omit || !omit[key])
&& (!pick || pick[key])) {
_lodash2.default.each(self, function (value, key) {
if (key.charAt(0) !== '$' && !_lodash2.default.isFunction(value) && !_lodash2.default.isUndefined(value) && (!omit || !omit[key]) && (!pick || pick[key])) {
if (_.isObject(value)) {
if (_lodash2.default.isObject(value)) {
json[key] = toJsonObject(value, createDbJson);

@@ -810,3 +886,3 @@ } else {

function toJsonObject(value, createDbJson) {
if (_.isArray(value)) {
if (_lodash2.default.isArray(value)) {
return toJsonArray(value, createDbJson);

@@ -820,3 +896,3 @@ } else if (value instanceof ModelBase) {

} else {
return _.cloneDeep(value);
return _lodash2.default.cloneDeep(value);
}

@@ -829,3 +905,3 @@ }

function toJsonArray(value, createDbJson) {
return _.map(value, function toJsonArrayLooper(value) {
return _lodash2.default.map(value, function (value) {
return toJsonObject(value, createDbJson);

@@ -839,3 +915,3 @@ });

function cloneObject(value) {
if (_.isArray(value)) {
if (_lodash2.default.isArray(value)) {
return cloneArray(value);

@@ -845,3 +921,3 @@ } else if (value instanceof ModelBase) {

} else {
return _.cloneDeep(value);
return _lodash2.default.cloneDeep(value);
}

@@ -854,5 +930,3 @@ }

function cloneArray(value) {
return _.map(value, function cloneArrayLooper(value) {
return cloneObject(value);
});
return _lodash2.default.map(value, cloneObject);
}

@@ -866,4 +940,4 @@

_.each(keyObj, function (value, key) {
if (value && key.charAt(0) !== '$' && _.has(model, key)) {
_lodash2.default.each(keyObj, function (value, key) {
if (value && key.charAt(0) !== '$' && _lodash2.default.has(model, key)) {
ModelClass.omitImpl(model, key);

@@ -880,4 +954,4 @@ }

_.each(keys, function (key) {
if (key.charAt(0) !== '$' && _.has(model, key)) {
_lodash2.default.each(keys, function (key) {
if (key.charAt(0) !== '$' && _lodash2.default.has(model, key)) {
ModelClass.omitImpl(model, key);

@@ -894,3 +968,3 @@ }

_.each(model, function (value, key) {
_lodash2.default.each(model, function (value, key) {
if (key.charAt(0) !== '$' && !keyObj[key]) {

@@ -908,3 +982,3 @@ ModelClass.omitImpl(model, key);

_.each(model, function (value, key) {
_lodash2.default.each(model, function (value, key) {
if (key.charAt(0) !== '$' && !contains(keys, key)) {

@@ -928,2 +1002,36 @@ ModelClass.omitImpl(model, key);

module.exports = ModelBase;
/**
* @private
*/
function _propertyNameToColumnName(ModelClass, propertyName) {
var model = new ModelClass();
var addedCols = _lodash2.default.keys(model.$formatDatabaseJson({}));
var obj = {};
obj[propertyName] = null;
var cols = _lodash2.default.keys(_lodash2.default.omit(model.$formatDatabaseJson(obj), addedCols));
var columnName = _lodash2.default.first(cols);
return columnName || null;
}
/**
* @private
*/
function _columnNameToPropertyName(ModelClass, columnName) {
var model = new ModelClass();
var addedProps = _lodash2.default.keys(model.$parseDatabaseJson({}));
var row = {};
row[columnName] = null;
var props = _lodash2.default.keys(_lodash2.default.omit(model.$parseDatabaseJson(row), addedProps));
var propertyName = _lodash2.default.first(props);
return propertyName || null;
}
// Add validation formats, so that for example the following schema validation works:
// createTime: {type: 'string', format: 'date-time'}
_tv2.default.addFormat(_tv4Formats2.default);
'use strict';
var _ = require('lodash')
, Promise = require('bluebird')
, QueryBuilder = require('./QueryBuilder')
, ValidationError = require('./../ValidationError')
, RelationExpression = require('./RelationExpression');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _ValidationError = require('../ValidationError');
var _ValidationError2 = _interopRequireDefault(_ValidationError);
var _RelationExpression = require('./RelationExpression');
var _RelationExpression2 = _interopRequireDefault(_RelationExpression);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @constructor
* @ignore
*/
function EagerFetcher(opt) {
this.modelClass = opt.modelClass;
this.models = opt.models;
this.eager = opt.eager;
this.filters = opt.filters || {};
this.parent = opt.parent || null;
this.children = Object.create(null);
this.promise = null;
}
EagerFetcher.prototype.fetch = function () {
if (this.promise) {
return this.promise;
}
var EagerFetcher = (function () {
function EagerFetcher(_ref) {
var modelClass = _ref.modelClass;
var models = _ref.models;
var eager = _ref.eager;
var filters = _ref.filters;
var parent = _ref.parent;
var rootQuery = _ref.rootQuery;
(0, _classCallCheck3.default)(this, EagerFetcher);
if (_.isEmpty(this.models)) {
this.promise = Promise.resolve([]);
return this.promise;
this.modelClass = modelClass;
this.models = models;
this.eager = eager;
this.filters = filters || {};
this.parent = parent || null;
this.rootQuery = rootQuery || null;
this.children = (0, _create2.default)(null);
this.promise = null;
}
var self = this;
var promises = [];
(0, _createClass3.default)(EagerFetcher, [{
key: 'fetch',
value: function fetch() {
var _this = this;
this.eager.forEachChild(function (child) {
var relation = self.modelClass.getRelations()[child.name];
if (this.promise) {
return this.promise;
}
if (!relation) {
throw new ValidationError({eager: 'unknown relation "' + child.name + '" in an eager expression'});
}
});
if (_lodash2.default.isEmpty(this.models)) {
this.promise = _bluebird2.default.resolve([]);
return this.promise;
}
_.each(this.modelClass.getRelations(), function (relation) {
var nextEager = self.eager.childExpression(relation.name);
var promises = [];
if (nextEager) {
promises.push(self._fetchRelation(relation, nextEager));
}
});
this.eager.forEachChild(function (child) {
var relation = _this.modelClass.getRelations()[child.name];
this.promise = Promise.all(promises).return(this.models);
return this.promise;
};
if (!relation) {
throw new _ValidationError2.default({ eager: 'unknown relation "' + child.name + '" in an eager expression' });
}
});
EagerFetcher.prototype._fetchRelation = function (relation, nextEager) {
var self = this;
var ModelClass = relation.relatedModelClass;
var queryBuilder = ModelClass.RelatedQueryBuilder.forClass(ModelClass);
_lodash2.default.each(this.modelClass.getRelations(), function (relation) {
var nextEager = _this.eager.childExpression(relation.name);
relation.find(queryBuilder, this.models);
if (nextEager) {
promises.push(_this._fetchRelation(relation, nextEager));
}
});
_.each(nextEager.args, function (filterName) {
var filter = self.filters[filterName];
if (!_.isFunction(filter)) {
throw new ValidationError({eager: 'could not find filter "' + filterName + '" for relation "' + relation.name + '"'});
this.promise = _bluebird2.default.all(promises).return(this.models);
return this.promise;
}
}, {
key: '_fetchRelation',
value: function _fetchRelation(relation, nextEager) {
var _this2 = this;
filter(queryBuilder);
});
var ModelClass = relation.relatedModelClass;
var queryBuilder = ModelClass.RelatedQueryBuilder.forClass(ModelClass).childQueryOf(this.rootQuery);
return queryBuilder.then(function (related) {
return self._fetchNextEager(relation, related, nextEager);
});
};
relation.find(queryBuilder, this.models);
EagerFetcher.prototype._fetchNextEager = function (relation, related, eager) {
this.children[relation.name] = new EagerFetcher({
modelClass: relation.relatedModelClass,
models: related,
eager: eager,
filters: this.filters,
parent: this
});
_lodash2.default.each(nextEager.args, function (filterName) {
var filter = _this2.filters[filterName];
return this.children[relation.name].fetch();
};
if (!_lodash2.default.isFunction(filter)) {
throw new _ValidationError2.default({ eager: 'could not find filter "' + filterName + '" for relation "' + relation.name + '"' });
}
module.exports = EagerFetcher;
filter(queryBuilder);
});
return queryBuilder.then(function (related) {
return _this2._fetchNextEager(relation, related, nextEager);
});
}
}, {
key: '_fetchNextEager',
value: function _fetchNextEager(relation, related, eager) {
this.children[relation.name] = new EagerFetcher({
modelClass: relation.relatedModelClass,
models: related,
eager: eager,
filters: this.filters,
parent: this,
rootQuery: this.rootQuery
});
return this.children[relation.name].fetch();
}
}]);
return EagerFetcher;
})();
exports.default = EagerFetcher;
'use strict';
var _ = require('lodash');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _QueryBuilderBase = require('./QueryBuilderBase');
var _QueryBuilderBase2 = _interopRequireDefault(_QueryBuilderBase);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**

@@ -29,111 +50,128 @@ * Internal representation of insert and update data.

* @ignore
* @constructor
*/
function InsertionOrUpdate(QueryBuilder, ModelClass) {
this.QueryBuilder = QueryBuilder;
this.ModelClass = ModelClass;
this._models = [];
this._rawOrQuery = [];
this._arrayInput = false;
}
var InsertionOrUpdate = (function () {
function InsertionOrUpdate(_ref) {
var ModelClass = _ref.ModelClass;
var modelsOrObjects = _ref.modelsOrObjects;
var modelOptions = _ref.modelOptions;
(0, _classCallCheck3.default)(this, InsertionOrUpdate);
InsertionOrUpdate.prototype.model = function () {
return this._models[0];
};
this.ModelClass = ModelClass;
InsertionOrUpdate.prototype.models = function () {
return this._models;
};
this._models = [];
this._rawOrQuery = [];
this._arrayInput = false;
/**
* Returns true if the input to `setData` method was an array.
*
* @ignore
* @returns {boolean}
*/
InsertionOrUpdate.prototype.isArray = function () {
return this._arrayInput;
};
this.setData(modelsOrObjects, modelOptions);
}
/**
* Sets the actual insert/update data.
*
* @ignore
* @param {Object|Array.<Object>} data
* @param {ModelOptions} modelOptions
*/
InsertionOrUpdate.prototype.setData = function (data, modelOptions) {
var self = this;
var knex = this.ModelClass.knex();
var KnexQueryBuilder = knex.client.QueryBuilder;
var Raw = knex.client.Raw;
(0, _createClass3.default)(InsertionOrUpdate, [{
key: 'model',
value: function model() {
return this._models[0];
}
}, {
key: 'models',
value: function models() {
return this._models;
}
// knex.QueryBuilder and knex.Raw are not documented properties.
// We make sure here that things break if knex changes things.
if (!_.isFunction(KnexQueryBuilder) || !_.isFunction(Raw)) {
throw new Error('knex API has changed: knex.QueryBuilder or knex.Raw constructor missing.');
}
/**
* Returns true if the input to `setData` method was an array.
*
* @ignore
* @returns {boolean}
*/
this._models = [];
this._rawOrQuery = [];
this._arrayInput = _.isArray(data);
}, {
key: 'isArray',
value: function isArray() {
return this._arrayInput;
}
if (!this._arrayInput) {
data = _.isObject(data) ? [data] : [];
}
/**
* Sets the actual insert/update data.
*
* @ignore
* @param {(Object|Array.<Object>)} data
* @param {ModelOptions} modelOptions
*/
// Separate raw queries and query builders from javascript primitives.
// The javascript primitives are converted into a Model instance and the
// "query" properties are stored separately.
_.each(data, function (obj) {
if (obj instanceof self.ModelClass) {
self._models.push(obj);
self._rawOrQuery.push({});
} else {
var modelJson = {};
var rawOrSubquery = {};
}, {
key: 'setData',
value: function setData(data, modelOptions) {
var _this = this;
_.each(obj, function (value, key) {
if (value instanceof KnexQueryBuilder|| value instanceof Raw) {
rawOrSubquery[key] = value;
} else if (value instanceof self.QueryBuilder) {
rawOrSubquery[key] = value.build();
var knex = this.ModelClass.knex();
var KnexQueryBuilder = knex.client.QueryBuilder;
var Raw = knex.client.Raw;
// knex.QueryBuilder and knex.Raw are not documented properties.
// We make sure here that things break if knex changes things.
if (!_lodash2.default.isFunction(KnexQueryBuilder) || !_lodash2.default.isFunction(Raw)) {
throw new Error('knex API has changed: knex.QueryBuilder or knex.Raw constructor missing.');
}
this._models = [];
this._rawOrQuery = [];
this._arrayInput = _lodash2.default.isArray(data);
if (!this._arrayInput) {
data = _lodash2.default.isObject(data) ? [data] : [];
}
// Separate raw queries and query builders from javascript primitives.
// The javascript primitives are converted into a Model instance and the
// "query" properties are stored separately.
_lodash2.default.forEach(data, function (obj) {
if (obj instanceof _this.ModelClass) {
_this._models.push(obj);
_this._rawOrQuery.push({});
} else {
modelJson[key] = value;
(function () {
var modelJson = {};
var rawOrSubquery = {};
_lodash2.default.forEach(obj, function (value, key) {
if (value instanceof KnexQueryBuilder || value instanceof Raw) {
rawOrSubquery[key] = value;
} else if (value instanceof _QueryBuilderBase2.default) {
rawOrSubquery[key] = value.build();
} else {
modelJson[key] = value;
}
});
_this._models.push(_this.ModelClass.fromJson(modelJson, modelOptions));
_this._rawOrQuery.push(rawOrSubquery);
})();
}
});
self._models.push(self.ModelClass.fromJson(modelJson, modelOptions));
self._rawOrQuery.push(rawOrSubquery);
}
});
};
/**
* Create an object that can be given for the knex update or insert method.
*
* @ignore
* @returns {Object|Array.<Object>}
*/
InsertionOrUpdate.prototype.toKnexInput = function () {
var self = this;
/**
* Create an object that can be given for the knex update or insert method.
*
* @ignore
* @returns {Object|Array.<Object>}
*/
var knexInput = _.map(this._models, function (model, i) {
var modelJson = model.$toDatabaseJson();
}, {
key: 'toKnexInput',
value: function toKnexInput() {
var _this2 = this;
var rawOrQuery = _.mapKeys(self._rawOrQuery[i], function (value, key) {
return model.constructor.propertyNameToColumnName(key);
});
var knexInput = _lodash2.default.map(this._models, function (model, i) {
return _lodash2.default.merge(model.$toDatabaseJson(), _lodash2.default.mapKeys(_this2._rawOrQuery[i], function (value, key) {
return model.constructor.propertyNameToColumnName(key);
}));
});
return _.merge(modelJson, rawOrQuery);
});
return knexInput.length === 1 ? knexInput[0] : knexInput;
}
}]);
return InsertionOrUpdate;
})();
if (knexInput.length === 1) {
return knexInput[0];
} else {
return knexInput;
}
};
module.exports = InsertionOrUpdate;
exports.default = InsertionOrUpdate;
'use strict';
var _ = require('lodash')
, Promise = require('bluebird')
, RelationExpression = require('./RelationExpression')
, ManyToManyRelation = require('./../relations/ManyToManyRelation')
, OneToManyRelation = require('./../relations/OneToManyRelation')
, OneToOneRelation = require('./../relations/OneToOneRelation')
, ValidationError = require('./../ValidationError')
, Model;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _RelationExpression = require('./RelationExpression');
var _RelationExpression2 = _interopRequireDefault(_RelationExpression);
var _ManyToManyRelation = require('../relations/ManyToManyRelation');
var _ManyToManyRelation2 = _interopRequireDefault(_ManyToManyRelation);
var _OneToManyRelation = require('../relations/OneToManyRelation');
var _OneToManyRelation2 = _interopRequireDefault(_OneToManyRelation);
var _OneToOneRelation = require('../relations/OneToOneRelation');
var _OneToOneRelation2 = _interopRequireDefault(_OneToOneRelation);
var _ValidationError = require('../ValidationError');
var _ValidationError2 = _interopRequireDefault(_ValidationError);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Model = undefined;
/**

@@ -16,201 +56,298 @@ * Given an model with nested relations, finds a fast way to insert the models into

*
* This class assumes that all foreign key references have a not-null constraint.
*
* By the way, the code in this module is ugly as hell because of stupid micro-optimizations :|
*
* @constructor
* @ignore
*/
function InsertWithRelated(opt) {
// Lazy-load Model.
Model = Model || require('./../model/Model');
this.modelClass = opt.modelClass;
this.models = opt.models;
this.allowedRelations = opt.allowedRelations || null;
this.graph = this._buildDependencyGraph();
this.done = false;
}
var InsertWithRelated = (function () {
function InsertWithRelated(_ref) {
var modelClass = _ref.modelClass;
var models = _ref.models;
var allowedRelations = _ref.allowedRelations;
(0, _classCallCheck3.default)(this, InsertWithRelated);
/**
* @returns {DependencyGraph}
* @private
*/
InsertWithRelated.prototype._buildDependencyGraph = function () {
var graph = new DependencyGraph(this.allowedRelations);
graph.build(this.modelClass, this.models);
return graph;
};
// Lazy-load Model.
Model = Model || require('./../model/Model').default;
InsertWithRelated.prototype.execute = function (inserter) {
return this.executeNextBatch(inserter);
};
this.modelClass = modelClass;
this.models = models;
this.allowedRelations = allowedRelations || null;
this.done = false;
this.graph = this._buildDependencyGraph();
}
InsertWithRelated.prototype.executeNextBatch = function (inserter) {
var self = this;
var batch = this.nextBatch();
/**
* @param {function(TableInsertion)} inserter
* @return {Promise}
*/
if (!batch) {
// TODO turn this into a function.
for (var n = 0, ln = this.graph.nodes.length; n < ln; ++n) {
var refNode = this.graph.nodes[n];
var ref = getUidRef(refNode.model);
(0, _createClass3.default)(InsertWithRelated, [{
key: 'execute',
value: function execute(inserter) {
return this._executeNextBatch(inserter);
}
if (ref) {
// Copy all the properties to the reference nodes.
var actualNode = getNode(this.graph.nodesById, ref);
var relations = actualNode.modelClass.getRelations();
/**
* @returns {DependencyGraph}
* @private
*/
_.each(actualNode.model, function (value, key) {
if (!getRelation(relations, key) && !_.isFunction(value)) {
refNode.model[key] = value;
}, {
key: '_buildDependencyGraph',
value: function _buildDependencyGraph() {
var graph = new DependencyGraph(this.allowedRelations);
graph.build(this.modelClass, this.models);
return graph;
}
/**
* @param {function(TableInsertion)} inserter
* @returns {Promise}
* @private
*/
}, {
key: '_executeNextBatch',
value: function _executeNextBatch(inserter) {
var _this = this;
var batch = this._nextBatch();
if (!batch) {
// If we get here, we are done. All we need to do now is to finalize the object graph
// and return it as the final output.
return this._finalize();
}
// Insert the batch using the `inserter` function.
return _bluebird2.default.all(_lodash2.default.map(batch, function (tableInsertion) {
var uids = undefined;
if (!tableInsertion.isJoinTableInsertion) {
// We need to omit the uid properties so that they don't get inserted
// into the database. Join table insertions never have uids.
uids = _this._omitUids(tableInsertion);
}
return inserter(tableInsertion).then(function () {
if (!tableInsertion.isJoinTableInsertion) {
// Resolve dependencies to the inserted objects.
_this._resolveDepsForInsertion(tableInsertion, uids);
}
});
})).then(function () {
return _this._executeNextBatch(inserter);
});
}
refNode.model.$omit(refNode.modelClass.uidProp, refNode.modelClass.uidRefProp);
/**
* @private
* @returns {Object.<string, TableInsertion>}
*/
}, {
key: '_nextBatch',
value: function _nextBatch() {
if (this.done) {
return null;
}
var batch = this._createBatch();
if (_lodash2.default.isEmpty(batch)) {
this.done = true;
return this._createManyToManyRelationJoinRowBatch();
} else {
this._markBatchHandled(batch);
return batch;
}
}
return Promise.resolve(this.models);
}
/**
* @private
* @returns {Object.<string, TableInsertion>}
*/
return Promise.all(_.map(batch, function (tableInsertion) {
var ids;
}, {
key: '_createBatch',
value: function _createBatch() {
var batch = (0, _create2.default)(null);
var nodes = this.graph.nodes;
// If the insertion is a model insertion instead of join row insertion,
// we need to delete the uid properties so that they don't get inserted
// into the database.
if (tableInsertion.modelClass) {
ids = _.pluck(tableInsertion.models, tableInsertion.modelClass.uidProp);
for (var n = 0, ln = nodes.length; n < ln; ++n) {
var node = nodes[n];
for (var m = 0, lm = tableInsertion.models.length; m < lm; ++m) {
tableInsertion.models[m].$omit(tableInsertion.modelClass.uidProp);
if (!node.handled && node.needs.length === node.numHandledNeeds) {
var tableInsertion = getTableInsertion(batch, node.modelClass.tableName);
if (!tableInsertion) {
tableInsertion = new TableInsertion(node.modelClass, false);
setTableInsertion(batch, node.modelClass.tableName, tableInsertion);
}
tableInsertion.models.push(node.model);
tableInsertion.isInputModel.push(isInputNode(this.graph.inputNodesById, node));
}
}
return batch;
}
return inserter(tableInsertion).then(function () {
if (!tableInsertion.modelClass) {
// The Many to many join row table insertions don't have a modelClass.
return;
}
/**
* @private
* @param {Object.<string, TableInsertion>} batch
*/
for (var m = 0, lm = tableInsertion.models.length; m < lm; ++m) {
var node = getNode(self.graph.nodesById, ids[m]);
var model = tableInsertion.models[m];
}, {
key: '_markBatchHandled',
value: function _markBatchHandled(batch) {
var models = _lodash2.default.flatten(_lodash2.default.pluck(batch, 'models'));
var nodes = this.graph.nodesById;
for (var d = 0, ld = node.isNeededBy.length; d < ld; ++d) {
var dep = node.isNeededBy[d];
dep.resolve(model);
for (var m = 0, lm = models.length; m < lm; ++m) {
var id = getUid(models[m]);
var node = getNode(nodes, id);
for (var nb = 0, lnb = node.isNeededBy.length; nb < lnb; ++nb) {
var dep = node.isNeededBy[nb];
dep.node.numHandledNeeds++;
}
node.handled = true;
}
});
})).then(function () {
return self.executeNextBatch(inserter);
})
};
}
InsertWithRelated.prototype.nextBatch = function () {
if (this.done) {
return null;
}
/**
* @private
* @returns {Object.<string, TableInsertion>}
*/
var batch = this.createBatch();
}, {
key: '_createManyToManyRelationJoinRowBatch',
value: function _createManyToManyRelationJoinRowBatch() {
var batch = (0, _create2.default)(null);
var notUnique = (0, _create2.default)(null);
if (_.isEmpty(batch)) {
this.done = true;
return this.createManyToManyRelationJoinRowBatch();
} else {
this.markBatchHandled(batch);
return batch;
}
};
for (var n = 0, ln = this.graph.nodes.length; n < ln; ++n) {
var node = this.graph.nodes[n];
InsertWithRelated.prototype.createBatch = function () {
var batch = Object.create(null);
var nodes = this.graph.nodes;
for (var m = 0, lm = node.manyToManyConnections.length; m < lm; ++m) {
var conn = node.manyToManyConnections[m];
var tableInsertion = getTableInsertion(batch, conn.relation.joinTable);
for (var n = 0, ln = nodes.length; n < ln; ++n) {
var node = nodes[n];
var sourceVal = node.model[conn.relation.ownerProp];
var targetVal = conn.node.model[conn.relation.relatedProp];
if (!node.handled && node.needs.length === node.numHandledNeeds) {
var tableInsertion = getTableInsertion(batch, node.modelClass.tableName);
var uniqueKey = undefined;
if (!tableInsertion) {
tableInsertion = new TableInsertion(node.modelClass, node.modelClass.tableName);
setTableInsertion(batch, node.modelClass.tableName, tableInsertion)
if (conn.relation.joinTableOwnerCol < conn.relation.joinTableRelatedCol) {
uniqueKey = conn.relation.joinTable + '_' + sourceVal + '_' + targetVal;
} else {
uniqueKey = conn.relation.joinTable + '_' + targetVal + '_' + sourceVal;
}
if (notUnique[uniqueKey]) {
continue;
}
notUnique[uniqueKey] = true;
var joinModel = {};
joinModel[conn.relation.joinTableOwnerProp] = sourceVal;
joinModel[conn.relation.joinTableRelatedProp] = targetVal;
joinModel = conn.relation.joinTableModelClass.fromJson(joinModel);
if (!tableInsertion) {
tableInsertion = new TableInsertion(conn.relation.joinTableModelClass, true);
setTableInsertion(batch, conn.relation.joinTable, tableInsertion);
}
tableInsertion.models.push(joinModel);
tableInsertion.isInputModel.push(false);
}
}
tableInsertion.models.push(node.model);
tableInsertion.isInputModel.push(isInputNode(this.graph.inputNodesById, node));
return batch;
}
}
return batch;
};
/**
* @private
*/
InsertWithRelated.prototype.markBatchHandled = function (batch) {
var models = _.flatten(_.pluck(batch, 'models'));
var nodes = this.graph.nodesById;
}, {
key: '_omitUids',
value: function _omitUids(tableInsertion) {
var ids = _lodash2.default.pluck(tableInsertion.models, tableInsertion.modelClass.uidProp);
for (var m = 0, lm = models.length; m < lm; ++m) {
var id = getUid(models[m]);
var node = getNode(nodes, id);
for (var m = 0, lm = tableInsertion.models.length; m < lm; ++m) {
tableInsertion.models[m].$omit(tableInsertion.modelClass.uidProp);
}
for (var nb = 0, lnb = node.isNeededBy.length; nb < lnb; ++nb) {
var dep = node.isNeededBy[nb];
dep.node.numHandledNeeds++;
return ids;
}
node.handled = true;
}
};
/**
* @private
* @param {TableInsertion} tableInsertion
* @param {Array.<string>} uids
*/
InsertWithRelated.prototype.createManyToManyRelationJoinRowBatch = function () {
var batch = Object.create(null);
var notUnique = Object.create(null);
}, {
key: '_resolveDepsForInsertion',
value: function _resolveDepsForInsertion(tableInsertion, uids) {
for (var m = 0, lm = tableInsertion.models.length; m < lm; ++m) {
var node = getNode(this.graph.nodesById, uids[m]);
var model = tableInsertion.models[m];
for (var n = 0, ln = this.graph.nodes.length; n < ln; ++n) {
var node = this.graph.nodes[n];
for (var d = 0, ld = node.isNeededBy.length; d < ld; ++d) {
var dep = node.isNeededBy[d];
dep.resolve(model);
}
}
}
for (var m = 0, lm = node.manyToManyConnections.length; m < lm; ++m) {
var conn = node.manyToManyConnections[m];
var tableInsertion = getTableInsertion(batch, conn.relation.joinTable);
/**
* @private
* @return {Promise}
*/
var sourceVal = node.model[conn.relation.ownerProp];
var targetVal = conn.node.model[conn.relation.relatedProp];
}, {
key: '_finalize',
value: function _finalize() {
var _this2 = this;
var uniqueKey;
if (conn.relation.joinTableOwnerCol < conn.relation.joinTableRelatedCol) {
uniqueKey = conn.relation.joinTable + '_' + sourceVal + '_' + targetVal;
} else {
uniqueKey = conn.relation.joinTable + '_' + targetVal + '_' + sourceVal;
}
var _loop = function _loop(n, ln) {
var refNode = _this2.graph.nodes[n];
var ref = getUidRef(refNode.model);
if (notUnique[uniqueKey]) {
continue;
}
if (ref) {
(function () {
// Copy all the properties to the reference nodes.
var actualNode = getNode(_this2.graph.nodesById, ref);
var relations = actualNode.modelClass.getRelations();
var joinRow = {};
_lodash2.default.each(actualNode.model, function (value, key) {
if (!getRelation(relations, key) && !_lodash2.default.isFunction(value)) {
refNode.model[key] = value;
}
});
notUnique[uniqueKey] = true;
joinRow[conn.relation.joinTableOwnerCol] = sourceVal;
joinRow[conn.relation.joinTableRelatedCol] = targetVal;
refNode.model.$omit(refNode.modelClass.uidProp, refNode.modelClass.uidRefProp);
})();
}
};
if (!tableInsertion) {
tableInsertion = new TableInsertion(null, conn.relation.joinTable);
setTableInsertion(batch, conn.relation.joinTable, tableInsertion)
for (var n = 0, ln = this.graph.nodes.length; n < ln; ++n) {
_loop(n, ln);
}
tableInsertion.models.push(joinRow);
tableInsertion.isInputModel.push(false);
return _bluebird2.default.resolve(this.models);
}
}
}]);
return InsertWithRelated;
})();
return batch;
};
exports.default = InsertWithRelated;
function TableInsertion(modelClass, tableName) {
function TableInsertion(modelClass, isJoinTableInsertion) {
this.modelClass = modelClass;
this.tableName = tableName;
this.isJoinTableInsertion = isJoinTableInsertion;
this.models = [];

@@ -247,4 +384,4 @@ this.isInputModel = [];

this.allowedRelations = allowedRelations;
this.nodesById = Object.create(null);
this.inputNodesById = Object.create(null);
this.nodesById = (0, _create2.default)(null);
this.inputNodesById = (0, _create2.default)(null);
this.nodes = [];

@@ -257,7 +394,7 @@ this.uid = 0;

this.nodesById = Object.create(null);
this.nodesById = (0, _create2.default)(null);
this.nodes = [];
if (_.isArray(models)) {
_.each(models, function (model) {
if (_lodash2.default.isArray(models)) {
_lodash2.default.each(models, function (model) {
self.buildForModel(modelClass, model, null, null, self.allowedRelations);

@@ -273,3 +410,3 @@ });

if (this.isCyclic(this.nodes)) {
throw new ValidationError({cyclic: 'the object graph contains cyclic references'});
throw new _ValidationError2.default({ cyclic: 'the object graph contains cyclic references' });
}

@@ -282,7 +419,7 @@

if (!(model instanceof Model)) {
throw new ValidationError({notModel: 'the object graph contains cyclic references'});
throw new _ValidationError2.default({ notModel: 'the object graph contains cyclic references' });
}
if (!getUid(model)) {
setUid(model, '__objection_uid(' + (++this.uid) + ')__');
setUid(model, '__objection_uid(' + ++this.uid + ')__');
}

@@ -299,3 +436,3 @@

if (rel instanceof OneToManyRelation) {
if (rel instanceof _OneToManyRelation2.default) {

@@ -309,5 +446,4 @@ node.needs.push(new Dependency(parentNode, function (model) {

}));
} else if (rel instanceof _OneToOneRelation2.default) {
} else if (rel instanceof OneToOneRelation) {
node.isNeededBy.push(new Dependency(parentNode, function (model) {

@@ -320,4 +456,3 @@ this.node.model[rel.ownerProp] = model[rel.relatedProp];

}));
} else if (rel instanceof ManyToManyRelation) {
} else if (rel instanceof _ManyToManyRelation2.default) {
// ManyToManyRelations create no dependencies since we can create the

@@ -341,11 +476,11 @@ // join table rows after everything else has been inserted.

if (relModels && allowedRelations instanceof RelationExpression) {
if (relModels && allowedRelations instanceof _RelationExpression2.default) {
nextAllowed = allowedRelations.childExpression(relName);
if (!nextAllowed) {
throw new ValidationError({allowedRelations: 'trying to insert an unallowed relation'});
throw new _ValidationError2.default({ allowedRelations: 'trying to insert an unallowed relation' });
}
}
if (_.isArray(relModels)) {
if (_lodash2.default.isArray(relModels)) {
for (var i = 0, l = relModels.length; i < l; ++i) {

@@ -361,3 +496,3 @@ this.buildForModel(rel.relatedModelClass, relModels[i], node, rel, nextAllowed);

DependencyGraph.prototype.solveReferences = function () {
var refMap = Object.create(null);
var refMap = (0, _create2.default)(null);

@@ -373,34 +508,35 @@ // First merge all reference nodes into the actual node.

for (var n = 0, ln = this.nodes.length; n < ln; ++n) {
var refNode = this.nodes[n];
var _refNode = this.nodes[n];
if (refNode.handled) {
if (_refNode.handled) {
continue;
}
var ref = getUidRef(refNode.model);
var _ref2 = getUidRef(_refNode.model);
if (ref) {
var actualNode = getNode(this.nodesById, ref);
if (_ref2) {
var actualNode = getNode(this.nodesById, _ref2);
if (!actualNode) {
throw new ValidationError({ref: 'could not resolve reference "' + ref + '"'});
throw new _ValidationError2.default({ ref: 'could not resolve reference "' + _ref2 + '"' });
}
var d, ld;
var d = undefined,
ld = undefined;
for (d = 0, ld = refNode.needs.length; d < ld; ++d) {
actualNode.needs.push(refNode.needs[d]);
for (d = 0, ld = _refNode.needs.length; d < ld; ++d) {
actualNode.needs.push(_refNode.needs[d]);
}
for (d = 0, ld = refNode.isNeededBy.length; d < ld; ++d) {
actualNode.isNeededBy.push(refNode.isNeededBy[d]);
for (d = 0, ld = _refNode.isNeededBy.length; d < ld; ++d) {
actualNode.isNeededBy.push(_refNode.isNeededBy[d]);
}
for (var m = 0, lm = refNode.manyToManyConnections.length; m < lm; ++m) {
actualNode.manyToManyConnections.push(refNode.manyToManyConnections[m]);
for (var m = 0, lm = _refNode.manyToManyConnections.length; m < lm; ++m) {
actualNode.manyToManyConnections.push(_refNode.manyToManyConnections[m]);
}
setRefMap(refMap, refNode.id, actualNode);
setRefMap(refMap, _refNode.id, actualNode);
refNode.handled = true;
_refNode.handled = true;
}

@@ -413,3 +549,6 @@ }

var node = this.nodes[n];
var d, ld, dep, actualNode;
var d = undefined,
ld = undefined,
dep = undefined,
actualNode = undefined;

@@ -458,6 +597,7 @@ for (d = 0, ld = node.needs.length; d < ld; ++d) {

var relations = node.modelClass.getRelations();
var isModel = obj instanceof Model;
var self = this;
_.each(obj, function (value, key) {
if (obj instanceof Model && getRelation(relations, key)) {
_lodash2.default.each(obj, function (value, key) {
if (isModel && getRelation(relations, key)) {
// Don't traverse the relations of model instances.

@@ -469,3 +609,3 @@ return;

if (_.isString(value)) {
if (_lodash2.default.isString(value)) {
allMatches(propRefRegex, value, function (matchResult) {

@@ -479,3 +619,3 @@ var match = matchResult[0];

if (!refNode) {
throw new ValidationError({ref: 'could not resolve reference "' + value + '"'});
throw new _ValidationError2.default({ ref: 'could not resolve reference "' + value + '"' });
}

@@ -488,6 +628,6 @@

node.needs.push(new Dependency(refNode, function (model) {
_.set(model, pathClone, this.node.model[refProp]);
_lodash2.default.set(model, pathClone, this.node.model[refProp]);
}));
refNode.isNeededBy.push(new Dependency(node, function (model) {
_.set(this.node.model, pathClone, model[refProp]);
_lodash2.default.set(this.node.model, pathClone, model[refProp]);
}));

@@ -499,11 +639,11 @@ } else {

value = value.replace(match, this.node.model[refProp]);
_.set(model, pathClone, value);
_lodash2.default.set(model, pathClone, value);
}));
refNode.isNeededBy.push(new Dependency(node, function (model) {
value = value.replace(match, model[refProp]);
_.set(this.node.model, pathClone, value);
_lodash2.default.set(this.node.model, pathClone, value);
}));
}
});
} else if (_.isObject(value)) {
} else if (_lodash2.default.isObject(value)) {
self.createNonRelationDepsForObject(value, node, path);

@@ -616,4 +756,2 @@ }

}
}
module.exports = InsertWithRelated;
}

@@ -1,2 +0,4 @@

module.exports = (function() {
"use strict";
module.exports = (function () {
/*

@@ -9,3 +11,5 @@ * Generated by PEG.js 0.8.0.

function peg$subclass(child, parent) {
function ctor() { this.constructor = child; }
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;

@@ -16,10 +20,10 @@ child.prototype = new ctor();

function SyntaxError(message, expected, found, offset, line, column) {
this.message = message;
this.message = message;
this.expected = expected;
this.found = found;
this.offset = offset;
this.line = line;
this.column = column;
this.found = found;
this.offset = offset;
this.line = line;
this.column = column;
this.name = "SyntaxError";
this.name = "SyntaxError";
}

@@ -31,8 +35,5 @@

var options = arguments.length > 1 ? arguments[1] : {},
peg$FAILED = {},
peg$startRuleFunctions = { start: peg$parsestart },
peg$startRuleFunction = peg$parsestart,
peg$startRuleFunction = peg$parsestart,
peg$c0 = peg$FAILED,

@@ -43,11 +44,11 @@ peg$c1 = null,

peg$c4 = [],
peg$c5 = function(column, refs) {
var access = [];
if (refs) {
var firstAccess = refs[1];
access = refs[2];
access.unshift(firstAccess);
}
return { columnName: column, access: access };
},
peg$c5 = function peg$c5(column, refs) {
var access = [];
if (refs) {
var firstAccess = refs[1];
access = refs[2];
access.unshift(firstAccess);
}
return { columnName: column, access: access };
},
peg$c6 = "[",

@@ -61,5 +62,11 @@ peg$c7 = { type: "literal", value: "[", description: "\"[\"" },

peg$c13 = { type: "literal", value: "]", description: "\"]\"" },
peg$c14 = function(key) { return { type: 'object', ref: Array.isArray(key) ? key[1] : key }; },
peg$c15 = function(index) { return { type: 'array', ref: parseInt(index, 10) }; },
peg$c16 = function(key) { return { type: 'object', ref: key }; },
peg$c14 = function peg$c14(key) {
return { type: 'object', ref: Array.isArray(key) ? key[1] : key };
},
peg$c15 = function peg$c15(index) {
return { type: 'array', ref: parseInt(index, 10) };
},
peg$c16 = function peg$c16(key) {
return { type: 'object', ref: key };
},
peg$c17 = ".",

@@ -69,3 +76,5 @@ peg$c18 = { type: "literal", value: ".", description: "\".\"" },

peg$c20 = { type: "class", value: "[^\\][]", description: "[^\\][]" },
peg$c21 = function(chars) { return chars.join(""); },
peg$c21 = function peg$c21(chars) {
return chars.join("");
},
peg$c22 = /^[^:]/,

@@ -81,12 +90,12 @@ peg$c23 = { type: "class", value: "[^:]", description: "[^:]" },

peg$c31 = { type: "class", value: "[0-9]", description: "[0-9]" },
peg$c32 = function(digits) { return digits.join(""); },
peg$currPos = 0,
peg$reportedPos = 0,
peg$cachedPos = 0,
peg$c32 = function peg$c32(digits) {
return digits.join("");
},
peg$currPos = 0,
peg$reportedPos = 0,
peg$cachedPos = 0,
peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
peg$result;

@@ -119,7 +128,3 @@

function expected(description) {
throw peg$buildException(
null,
[{ type: "other", description: description }],
peg$reportedPos
);
throw peg$buildException(null, [{ type: "other", description: description }], peg$reportedPos);
}

@@ -138,3 +143,5 @@

if (ch === "\n") {
if (!details.seenCR) { details.line++; }
if (!details.seenCR) {
details.line++;
}
details.column = 1;

@@ -166,3 +173,5 @@ details.seenCR = false;

function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) { return; }
if (peg$currPos < peg$maxFailPos) {
return;
}

@@ -181,3 +190,3 @@ if (peg$currPos > peg$maxFailPos) {

expected.sort(function(a, b) {
expected.sort(function (a, b) {
if (a.description < b.description) {

@@ -203,20 +212,21 @@ return -1;

function stringEscape(s) {
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\x08/g, '\\b')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\f/g, '\\f')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
.replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
.replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\x08/g, '\\b').replace(/\t/g, '\\t').replace(/\n/g, '\\n').replace(/\f/g, '\\f').replace(/\r/g, '\\r').replace(/[\x00-\x07\x0B\x0E\x0F]/g, function (ch) {
return '\\x0' + hex(ch);
}).replace(/[\x10-\x1F\x80-\xFF]/g, function (ch) {
return '\\x' + hex(ch);
}).replace(/[\u0180-\u0FFF]/g, function (ch) {
return "\\u0" + hex(ch);
}).replace(/[\u1080-\uFFFF]/g, function (ch) {
return "\\u" + hex(ch);
});
}
var expectedDescs = new Array(expected.length),
expectedDesc, foundDesc, i;
expectedDesc,
foundDesc,
i;

@@ -227,7 +237,3 @@ for (i = 0; i < expected.length; i++) {

expectedDesc = expected.length > 1
? expectedDescs.slice(0, -1).join(", ")
+ " or "
+ expectedDescs[expected.length - 1]
: expectedDescs[0];
expectedDesc = expected.length > 1 ? expectedDescs.slice(0, -1).join(", ") + " or " + expectedDescs[expected.length - 1] : expectedDescs[0];

@@ -240,3 +246,3 @@ foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";

var posDetails = peg$computePosDetails(pos),
found = pos < input.length ? input.charAt(pos) : null;
found = pos < input.length ? input.charAt(pos) : null;

@@ -247,10 +253,3 @@ if (expected !== null) {

return new SyntaxError(
message !== null ? message : buildMessage(expected, found),
expected,
found,
pos,
posDetails.line,
posDetails.column
);
return new SyntaxError(message !== null ? message : buildMessage(expected, found), expected, found, pos, posDetails.line, posDetails.column);
}

@@ -270,3 +269,5 @@

s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c3); }
if (peg$silentFails === 0) {
peg$fail(peg$c3);
}
}

@@ -343,3 +344,5 @@ if (s3 !== peg$FAILED) {

s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c7); }
if (peg$silentFails === 0) {
peg$fail(peg$c7);
}
}

@@ -353,3 +356,5 @@ if (s1 !== peg$FAILED) {

s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c9); }
if (peg$silentFails === 0) {
peg$fail(peg$c9);
}
}

@@ -364,3 +369,5 @@ if (s3 !== peg$FAILED) {

s5 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c9); }
if (peg$silentFails === 0) {
peg$fail(peg$c9);
}
}

@@ -389,3 +396,5 @@ if (s5 !== peg$FAILED) {

s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c11); }
if (peg$silentFails === 0) {
peg$fail(peg$c11);
}
}

@@ -400,3 +409,5 @@ if (s3 !== peg$FAILED) {

s5 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c11); }
if (peg$silentFails === 0) {
peg$fail(peg$c11);
}
}

@@ -428,3 +439,5 @@ if (s5 !== peg$FAILED) {

s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
if (peg$silentFails === 0) {
peg$fail(peg$c13);
}
}

@@ -460,3 +473,5 @@ if (s3 !== peg$FAILED) {

s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c7); }
if (peg$silentFails === 0) {
peg$fail(peg$c7);
}
}

@@ -471,3 +486,5 @@ if (s1 !== peg$FAILED) {

s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
if (peg$silentFails === 0) {
peg$fail(peg$c13);
}
}

@@ -517,3 +534,5 @@ if (s3 !== peg$FAILED) {

s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c18); }
if (peg$silentFails === 0) {
peg$fail(peg$c18);
}
}

@@ -548,3 +567,5 @@ if (s1 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c20); }
if (peg$silentFails === 0) {
peg$fail(peg$c20);
}
}

@@ -559,3 +580,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c20); }
if (peg$silentFails === 0) {
peg$fail(peg$c20);
}
}

@@ -585,3 +608,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c23); }
if (peg$silentFails === 0) {
peg$fail(peg$c23);
}
}

@@ -596,3 +621,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c23); }
if (peg$silentFails === 0) {
peg$fail(peg$c23);
}
}

@@ -622,3 +649,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c25); }
if (peg$silentFails === 0) {
peg$fail(peg$c25);
}
}

@@ -633,3 +662,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c25); }
if (peg$silentFails === 0) {
peg$fail(peg$c25);
}
}

@@ -659,3 +690,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c27); }
if (peg$silentFails === 0) {
peg$fail(peg$c27);
}
}

@@ -670,3 +703,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c27); }
if (peg$silentFails === 0) {
peg$fail(peg$c27);
}
}

@@ -696,3 +731,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c29); }
if (peg$silentFails === 0) {
peg$fail(peg$c29);
}
}

@@ -707,3 +744,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c29); }
if (peg$silentFails === 0) {
peg$fail(peg$c29);
}
}

@@ -733,3 +772,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c31); }
if (peg$silentFails === 0) {
peg$fail(peg$c31);
}
}

@@ -744,3 +785,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c31); }
if (peg$silentFails === 0) {
peg$fail(peg$c31);
}
}

@@ -775,4 +818,4 @@ }

SyntaxError: SyntaxError,
parse: parse
parse: parse
};
})();
})();

@@ -1,2 +0,4 @@

module.exports = (function() {
"use strict";
module.exports = (function () {
"use strict";

@@ -11,3 +13,5 @@

function peg$subclass(child, parent) {
function ctor() { this.constructor = child; }
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;

@@ -18,7 +22,7 @@ child.prototype = new ctor();

function peg$SyntaxError(message, expected, found, location) {
this.message = message;
this.message = message;
this.expected = expected;
this.found = found;
this.found = found;
this.location = location;
this.name = "SyntaxError";
this.name = "SyntaxError";

@@ -34,31 +38,28 @@ if (typeof Error.captureStackTrace === "function") {

var options = arguments.length > 1 ? arguments[1] : {},
parser = this,
parser = this,
peg$FAILED = {},
peg$startRuleFunctions = { start: peg$parsestart },
peg$startRuleFunction = peg$parsestart,
peg$c0 = function(expr) {
var node = {name: null, args: [], numChildren: 1, children: {}};
node.children[expr.name] = expr;
return node;
},
peg$c1 = function(list) {
return {name: null, args: [], numChildren: list.list.length, children: list.byName};
},
peg$c2 = function(rel, args, list) {
return {name: rel, args: args || [], numChildren: list.list.length, children: list.byName};
},
peg$c3 = function(rel, args, sub) {
var node = {name: rel, args: args || [], numChildren: 0, children: {}};
if (sub) {
node.numChildren = 1;
node.children[sub.name] = sub;
}
return node;
},
peg$c4 = function(rel) {
return rel.join('')
},
peg$startRuleFunction = peg$parsestart,
peg$c0 = function peg$c0(expr) {
var node = { name: null, args: [], numChildren: 1, children: {} };
node.children[expr.name] = expr;
return node;
},
peg$c1 = function peg$c1(list) {
return { name: null, args: [], numChildren: list.list.length, children: list.byName };
},
peg$c2 = function peg$c2(rel, args, list) {
return { name: rel, args: args || [], numChildren: list.list.length, children: list.byName };
},
peg$c3 = function peg$c3(rel, args, sub) {
var node = { name: rel, args: args || [], numChildren: 0, children: {} };
if (sub) {
node.numChildren = 1;
node.children[sub.name] = sub;
}
return node;
},
peg$c4 = function peg$c4(rel) {
return rel.join('');
},
peg$c5 = /^[^[\](),. \t\r\n]/,

@@ -70,10 +71,10 @@ peg$c6 = { type: "class", value: "[^\\[\\]\\(\\),\\. \\t\\r\\n]", description: "[^\\[\\]\\(\\),\\. \\t\\r\\n]" },

peg$c10 = { type: "literal", value: ")", description: "\")\"" },
peg$c11 = function(args) {
return args
},
peg$c11 = function peg$c11(args) {
return args;
},
peg$c12 = ",",
peg$c13 = { type: "literal", value: ",", description: "\",\"" },
peg$c14 = function(arg) {
return arg
},
peg$c14 = function peg$c14(arg) {
return arg;
},
peg$c15 = /^[ \t\r\n]/,

@@ -83,5 +84,5 @@ peg$c16 = { type: "class", value: "[ \\t\\r\\n]", description: "[ \\t\\r\\n]" },

peg$c18 = { type: "literal", value: ".", description: "\".\"" },
peg$c19 = function(list) {
return list
},
peg$c19 = function peg$c19(list) {
return list;
},
peg$c20 = "[",

@@ -91,28 +92,26 @@ peg$c21 = { type: "literal", value: "[", description: "\"[\"" },

peg$c23 = { type: "literal", value: "]", description: "\"]\"" },
peg$c24 = function(items) {
var itemsByName = {};
peg$c24 = function peg$c24(items) {
var itemsByName = {};
for (var i = 0; i < items.length; ++i) {
itemsByName[items[i].name] = items[i];
}
for (var i = 0; i < items.length; ++i) {
itemsByName[items[i].name] = items[i];
}
return {
list: items,
byName: itemsByName
};
},
peg$c25 = function(expr) {
return expr
},
peg$c26 = function(sub) {
return sub
},
peg$currPos = 0,
peg$savedPos = 0,
peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }],
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
return {
list: items,
byName: itemsByName
};
},
peg$c25 = function peg$c25(expr) {
return expr;
},
peg$c26 = function peg$c26(sub) {
return sub;
},
peg$currPos = 0,
peg$savedPos = 0,
peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }],
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
peg$result;

@@ -137,17 +136,7 @@

function expected(description) {
throw peg$buildException(
null,
[{ type: "other", description: description }],
input.substring(peg$savedPos, peg$currPos),
peg$computeLocation(peg$savedPos, peg$currPos)
);
throw peg$buildException(null, [{ type: "other", description: description }], input.substring(peg$savedPos, peg$currPos), peg$computeLocation(peg$savedPos, peg$currPos));
}
function error(message) {
throw peg$buildException(
message,
null,
input.substring(peg$savedPos, peg$currPos),
peg$computeLocation(peg$savedPos, peg$currPos)
);
throw peg$buildException(message, null, input.substring(peg$savedPos, peg$currPos), peg$computeLocation(peg$savedPos, peg$currPos));
}

@@ -157,3 +146,4 @@

var details = peg$posDetailsCache[pos],
p, ch;
p,
ch;

@@ -170,3 +160,3 @@ if (details) {

details = {
line: details.line,
line: details.line,
column: details.column,

@@ -179,3 +169,5 @@ seenCR: details.seenCR

if (ch === "\n") {
if (!details.seenCR) { details.line++; }
if (!details.seenCR) {
details.line++;
}
details.column = 1;

@@ -202,3 +194,3 @@ details.seenCR = false;

var startPosDetails = peg$computePosDetails(startPos),
endPosDetails = peg$computePosDetails(endPos);
endPosDetails = peg$computePosDetails(endPos);

@@ -208,3 +200,3 @@ return {

offset: startPos,
line: startPosDetails.line,
line: startPosDetails.line,
column: startPosDetails.column

@@ -214,3 +206,3 @@ },

offset: endPos,
line: endPosDetails.line,
line: endPosDetails.line,
column: endPosDetails.column

@@ -222,3 +214,5 @@ }

function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) { return; }
if (peg$currPos < peg$maxFailPos) {
return;
}

@@ -237,3 +231,3 @@ if (peg$currPos > peg$maxFailPos) {

expected.sort(function(a, b) {
expected.sort(function (a, b) {
if (a.description < b.description) {

@@ -259,20 +253,21 @@ return -1;

function stringEscape(s) {
function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\x08/g, '\\b')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\f/g, '\\f')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
.replace(/[\u0100-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
.replace(/[\u1000-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\x08/g, '\\b').replace(/\t/g, '\\t').replace(/\n/g, '\\n').replace(/\f/g, '\\f').replace(/\r/g, '\\r').replace(/[\x00-\x07\x0B\x0E\x0F]/g, function (ch) {
return '\\x0' + hex(ch);
}).replace(/[\x10-\x1F\x80-\xFF]/g, function (ch) {
return '\\x' + hex(ch);
}).replace(/[\u0100-\u0FFF]/g, function (ch) {
return "\\u0" + hex(ch);
}).replace(/[\u1000-\uFFFF]/g, function (ch) {
return "\\u" + hex(ch);
});
}
var expectedDescs = new Array(expected.length),
expectedDesc, foundDesc, i;
expectedDesc,
foundDesc,
i;

@@ -283,7 +278,3 @@ for (i = 0; i < expected.length; i++) {

expectedDesc = expected.length > 1
? expectedDescs.slice(0, -1).join(", ")
+ " or "
+ expectedDescs[expected.length - 1]
: expectedDescs[0];
expectedDesc = expected.length > 1 ? expectedDescs.slice(0, -1).join(", ") + " or " + expectedDescs[expected.length - 1] : expectedDescs[0];

@@ -299,8 +290,3 @@ foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";

return new peg$SyntaxError(
message !== null ? message : buildMessage(expected, found),
expected,
found,
location
);
return new peg$SyntaxError(message !== null ? message : buildMessage(expected, found), expected, found, location);
}

@@ -424,3 +410,5 @@

s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c6); }
if (peg$silentFails === 0) {
peg$fail(peg$c6);
}
}

@@ -442,3 +430,5 @@

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c8); }
if (peg$silentFails === 0) {
peg$fail(peg$c8);
}
}

@@ -458,3 +448,5 @@ if (s2 !== peg$FAILED) {

s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c10); }
if (peg$silentFails === 0) {
peg$fail(peg$c10);
}
}

@@ -506,3 +498,5 @@ if (s4 !== peg$FAILED) {

s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
if (peg$silentFails === 0) {
peg$fail(peg$c13);
}
}

@@ -551,3 +545,5 @@ if (s4 === peg$FAILED) {

s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c16); }
if (peg$silentFails === 0) {
peg$fail(peg$c16);
}
}

@@ -561,3 +557,5 @@ while (s1 !== peg$FAILED) {

s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c16); }
if (peg$silentFails === 0) {
peg$fail(peg$c16);
}
}

@@ -603,3 +601,5 @@ }

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c18); }
if (peg$silentFails === 0) {
peg$fail(peg$c18);
}
}

@@ -651,3 +651,5 @@ if (s2 !== peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c21); }
if (peg$silentFails === 0) {
peg$fail(peg$c21);
}
}

@@ -667,3 +669,5 @@ if (s2 !== peg$FAILED) {

s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c23); }
if (peg$silentFails === 0) {
peg$fail(peg$c23);
}
}

@@ -715,3 +719,5 @@ if (s4 !== peg$FAILED) {

s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
if (peg$silentFails === 0) {
peg$fail(peg$c13);
}
}

@@ -762,3 +768,5 @@ if (s4 === peg$FAILED) {

s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c18); }
if (peg$silentFails === 0) {
peg$fail(peg$c18);
}
}

@@ -808,10 +816,3 @@ if (s2 !== peg$FAILED) {

throw peg$buildException(
null,
peg$maxFailExpected,
peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
peg$maxFailPos < input.length
? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, peg$maxFailPos < input.length ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) : peg$computeLocation(peg$maxFailPos, peg$maxFailPos));
}

@@ -822,4 +823,4 @@ }

SyntaxError: peg$SyntaxError,
parse: peg$parse
parse: peg$parse
};
})();
})();
'use strict';
var _ = require('lodash')
, jsonFieldExpressionParser = require('./parsers/jsonFieldExpressionParser')
, InsertionOrUpdate = require('./InsertionOrUpdate')
, utils = require('./../utils');
var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _dec16, _dec17, _dec18, _dec19, _dec20, _dec21, _dec22, _dec23, _dec24, _dec25, _dec26, _dec27, _dec28, _dec29, _dec30, _dec31, _dec32, _dec33, _dec34, _dec35, _dec36, _dec37, _dec38, _dec39, _dec40, _dec41, _dec42, _dec43, _dec44, _dec45, _dec46, _dec47, _dec48, _dec49, _dec50, _dec51, _dec52, _dec53, _dec54, _dec55, _dec56, _dec57, _dec58, _dec59, _dec60, _dec61, _dec62, _dec63, _dec64, _dec65, _dec66, _dec67, _dec68, _dec69, _dec70, _dec71, _dec72, _desc, _value, _class;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _getOwnPropertyDescriptor = require('babel-runtime/core-js/object/get-own-property-descriptor');
var _getOwnPropertyDescriptor2 = _interopRequireDefault(_getOwnPropertyDescriptor);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _jsonFieldExpressionParser = require('./parsers/jsonFieldExpressionParser');
var _jsonFieldExpressionParser2 = _interopRequireDefault(_jsonFieldExpressionParser);
var _InsertionOrUpdate = require('./InsertionOrUpdate');
var _InsertionOrUpdate2 = _interopRequireDefault(_InsertionOrUpdate);
var _utils = require('../utils');
var _utils2 = _interopRequireDefault(_utils);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
var desc = {};
Object['ke' + 'ys'](descriptor).forEach(function (key) {
desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;
if ('value' in desc || desc.initializer) {
desc.writable = true;
}
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
return decorator(target, property, desc) || desc;
}, desc);
if (context && desc.initializer !== void 0) {
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
desc.initializer = undefined;
}
if (desc.initializer === void 0) {
Object['define' + 'Property'](target, property, desc);
desc = null;
}
return desc;
}
/**

@@ -17,1105 +82,1409 @@ * Knex query builder wrapper.

*/
function QueryBuilderBase(knex) {
this._knex = knex;
this._knexMethodCalls = [];
}
var QueryBuilderBase = (_dec = knexQueryMethod(), _dec2 = knexQueryMethod(), _dec3 = knexQueryMethod(), _dec4 = knexQueryMethod('delete'), _dec5 = knexQueryMethod(), _dec6 = knexQueryMethod(), _dec7 = knexQueryMethod(), _dec8 = knexQueryMethod(), _dec9 = knexQueryMethod(), _dec10 = knexQueryMethod(), _dec11 = knexQueryMethod(), _dec12 = knexQueryMethod(), _dec13 = knexQueryMethod(), _dec14 = knexQueryMethod(), _dec15 = knexQueryMethod(), _dec16 = knexQueryMethod(), _dec17 = knexQueryMethod(), _dec18 = knexQueryMethod(), _dec19 = knexQueryMethod(), _dec20 = knexQueryMethod(), _dec21 = knexQueryMethod(), _dec22 = knexQueryMethod(), _dec23 = knexQueryMethod(), _dec24 = knexQueryMethod(), _dec25 = knexQueryMethod(), _dec26 = knexQueryMethod(), _dec27 = knexQueryMethod(), _dec28 = knexQueryMethod(), _dec29 = knexQueryMethod(), _dec30 = knexQueryMethod(), _dec31 = knexQueryMethod(), _dec32 = knexQueryMethod(), _dec33 = knexQueryMethod(), _dec34 = knexQueryMethod(), _dec35 = knexQueryMethod(), _dec36 = knexQueryMethod(), _dec37 = knexQueryMethod(), _dec38 = knexQueryMethod(), _dec39 = knexQueryMethod(), _dec40 = knexQueryMethod(), _dec41 = knexQueryMethod(), _dec42 = knexQueryMethod(), _dec43 = knexQueryMethod(), _dec44 = knexQueryMethod(), _dec45 = knexQueryMethod(), _dec46 = knexQueryMethod(), _dec47 = knexQueryMethod(), _dec48 = knexQueryMethod(), _dec49 = knexQueryMethod(), _dec50 = knexQueryMethod(), _dec51 = knexQueryMethod(), _dec52 = knexQueryMethod(), _dec53 = knexQueryMethod(), _dec54 = knexQueryMethod(), _dec55 = knexQueryMethod(), _dec56 = knexQueryMethod(), _dec57 = knexQueryMethod(), _dec58 = knexQueryMethod(), _dec59 = knexQueryMethod(), _dec60 = knexQueryMethod(), _dec61 = knexQueryMethod(), _dec62 = knexQueryMethod(), _dec63 = knexQueryMethod(), _dec64 = knexQueryMethod(), _dec65 = knexQueryMethod(), _dec66 = knexQueryMethod(), _dec67 = knexQueryMethod(), _dec68 = knexQueryMethod(), _dec69 = knexQueryMethod(), _dec70 = knexQueryMethod(), _dec71 = knexQueryMethod(), _dec72 = knexQueryMethod(), (_class = (function () {
function QueryBuilderBase(knex) {
(0, _classCallCheck3.default)(this, QueryBuilderBase);
/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {function}
*/
QueryBuilderBase.extend = function (subclassConstructor) {
utils.inherits(subclassConstructor, this);
return subclassConstructor;
};
this._knex = knex;
this._knexMethodCalls = [];
this._context = {};
}
/**
* Calls the given function immediately and passes `this` as an argument.
*
* Handy for chaining conditional stuff:
*
* ```js
* new QueryBuilderBase().call(function (builder) {
* if (someCondition) {
* builder.where('something', someValue);
* }
* });
* ```
*
* @param {function} func
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.call = function (func) {
func.call(this, this);
return this;
};
/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {QueryBuilderBase}
*/
/**
* Returns the SQL string.
*
* @returns {String}
*/
QueryBuilderBase.prototype.toString = function () {
return this.build().toString();
};
(0, _createClass3.default)(QueryBuilderBase, [{
key: 'context',
/**
* Returns the SQL string.
*
* @returns {String}
*/
QueryBuilderBase.prototype.toSql = function () {
return this.toString();
};
/**
* Sets/gets the query context.
*/
value: function context() {
if (arguments.length === 0) {
return this._context.userContext;
} else {
this._context.userContext = arguments[0];
return this;
}
}
/**
* Create a clone of this builder.
*
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.clone = function () {
var clone = new this.constructor(this._knex);
this.cloneInto(clone);
return clone;
};
/**
* Sets/gets the query full internal context.
*
* For internal use only.
*
* @ignore
*/
/**
* @protected
*/
QueryBuilderBase.prototype.cloneInto = function (builder) {
builder._knex = this._knex;
builder._knexMethodCalls = this._knexMethodCalls.slice();
};
}, {
key: 'internalContext',
value: function internalContext() {
if (arguments.length === 0) {
return this._context;
} else {
this._context = arguments[0];
return this;
}
}
/**
* Removes query builder method calls.
*
* @param {RegExp=} methodNameRegex
* Optional patter to that must match the method names to remove.
* If not given, all calls are removed.
*
* @ignore
*/
QueryBuilderBase.prototype.clear = function (methodNameRegex) {
if (methodNameRegex) {
// Reject all query method calls that don't pass the filter.
this._knexMethodCalls = _.reject(this._knexMethodCalls, function (call) {
return methodNameRegex.test(call.method);
});
} else {
// If no arguments are given, clear all query method calls.
this._knexMethodCalls = [];
}
/**
* Calls the given function immediately and passes `this` as an argument.
*
* Handy for chaining conditional stuff:
*
* ```js
* new QueryBuilderBase().call(function (builder) {
* if (someCondition) {
* builder.where('something', someValue);
* }
* });
* ```
*
* @param {function} func
* @returns {QueryBuilderBase}
*/
return this;
};
}, {
key: 'call',
value: function call(func) {
func.call(this, this);
return this;
}
/**
* Copy query builder method calls from another query builder.
*
* @param {QueryBuilderBase} queryBuilder
* The builder to copy from.
*
* @param {RegExp} methodNameRegex
* Optional regular expression to filter which method calls are copied.
*
* @ignore
*/
QueryBuilderBase.prototype.copyFrom = function (queryBuilder, methodNameRegex) {
var self = this;
/**
* Returns the SQL string.
*
* @returns {string}
*/
_.each(queryBuilder._knexMethodCalls, function (call) {
if (!methodNameRegex || methodNameRegex.test(call.method)) {
self._knexMethodCalls.push(call);
}, {
key: 'toString',
value: function toString() {
return this.build().toString();
}
});
return this;
};
/**
* Returns the SQL string.
*
* @returns {string}
*/
/**
* Returns true if the builder has a call to a method whose name matches the `methodNameRegex`.
*
* @param {RegExp} methodNameRegex
*
* @ignore
*/
QueryBuilderBase.prototype.has = function (methodNameRegex) {
return _.any(this._knexMethodCalls, function (call) {
return methodNameRegex.test(call.method);
});
};
}, {
key: 'toSql',
value: function toSql() {
return this.toString();
}
/**
* Builds the query into a knex query builder.
*
* @returns {knex.QueryBuilder}
* The built knex query builder.
*
* @protected
*/
QueryBuilderBase.prototype.build = function () {
return this.buildInto(this._knex.queryBuilder());
};
/**
* Create a clone of this builder.
*
* @returns {QueryBuilderBase}
*/
/**
* @private
*/
QueryBuilderBase.prototype.buildInto = function (knexBuilder) {
_.each(this._knexMethodCalls, function (call) {
if (_.isFunction(knexBuilder[call.method])) {
knexBuilder[call.method].apply(knexBuilder, call.args);
}, {
key: 'clone',
value: function clone() {
var clone = new this.constructor(this._knex);
this.cloneInto(clone);
return clone;
}
});
return knexBuilder;
};
/**
* @protected
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.insert = knexQueryMethod('insert');
}, {
key: 'cloneInto',
value: function cloneInto(builder) {
builder._knex = this._knex;
builder._knexMethodCalls = this._knexMethodCalls.slice();
builder._context = this._context;
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.update = knexQueryMethod('update');
/**
* Removes query builder method calls.
*
* @param {RegExp=} methodNameRegex
* Optional patter to that must match the method names to remove.
* If not given, all calls are removed.
*
* @ignore
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.delete = knexQueryMethod('delete');
}, {
key: 'clear',
value: function clear(methodNameRegex) {
if (methodNameRegex) {
// Reject all query method calls that don't pass the filter.
this._knexMethodCalls = _lodash2.default.reject(this._knexMethodCalls, function (call) {
return methodNameRegex.test(call.method);
});
} else {
// If no arguments are given, clear all query method calls.
this._knexMethodCalls = [];
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.del = knexQueryMethod('delete');
return this;
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.select = knexQueryMethod('select');
/**
* Copy query builder method calls from another query builder.
*
* @param {QueryBuilderBase} queryBuilder
* The builder to copy from.
*
* @param {RegExp} methodNameRegex
* Optional regular expression to filter which method calls are copied.
*
* @ignore
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.as = knexQueryMethod('as');
}, {
key: 'copyFrom',
value: function copyFrom(queryBuilder, methodNameRegex) {
var self = this;
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.columns = knexQueryMethod('columns');
_lodash2.default.forEach(queryBuilder._knexMethodCalls, function (call) {
if (!methodNameRegex || methodNameRegex.test(call.method)) {
self._knexMethodCalls.push(call);
}
});
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.column = knexQueryMethod('column');
return this;
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.from = knexQueryMethod('from');
/**
* Returns true if the builder has a call to a method whose name matches the `methodNameRegex`.
*
* @param {RegExp} methodNameRegex
*
* @ignore
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.fromJS = knexQueryMethod('fromJS');
}, {
key: 'has',
value: function has(methodNameRegex) {
return _lodash2.default.any(this._knexMethodCalls, function (call) {
return methodNameRegex.test(call.method);
});
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.into = knexQueryMethod('into');
/**
* Builds the query into a knex query builder.
*
* @returns {knex.QueryBuilder}
* The built knex query builder.
*
* @protected
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.withSchema = knexQueryMethod('withSchema');
}, {
key: 'build',
value: function build() {
return this.buildInto(this._knex.queryBuilder());
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.table = knexQueryMethod('table');
/**
* @private
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.distinct = knexQueryMethod('distinct');
}, {
key: 'buildInto',
value: function buildInto(knexBuilder) {
_lodash2.default.forEach(this._knexMethodCalls, function (call) {
if (_lodash2.default.isFunction(knexBuilder[call.method])) {
knexBuilder[call.method].apply(knexBuilder, call.args);
}
});
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.join = knexQueryMethod('join');
return knexBuilder;
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.joinRaw = knexQueryMethod('joinRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.innerJoin = knexQueryMethod('innerJoin');
}, {
key: 'insert',
value: function insert() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.leftJoin = knexQueryMethod('leftJoin');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.leftOuterJoin = knexQueryMethod('leftOuterJoin');
}, {
key: 'update',
value: function update() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.rightJoin = knexQueryMethod('rightJoin');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.rightOuterJoin = knexQueryMethod('rightOuterJoin');
}, {
key: 'delete',
value: function _delete() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.outerJoin = knexQueryMethod('outerJoin');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.fullOuterJoin = knexQueryMethod('fullOuterJoin');
}, {
key: 'del',
value: function del() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.crossJoin = knexQueryMethod('crossJoin');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.where = knexQueryMethod('where');
}, {
key: 'select',
value: function select() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.andWhere = knexQueryMethod('andWhere');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhere = knexQueryMethod('orWhere');
}, {
key: 'as',
value: function as() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNot = knexQueryMethod('whereNot');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereNot = knexQueryMethod('orWhereNot');
}, {
key: 'columns',
value: function columns() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereRaw = knexQueryMethod('whereRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereWrapped = knexQueryMethod('whereWrapped');
}, {
key: 'column',
value: function column() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.havingWrapped = knexQueryMethod('havingWrapped');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'from',
value: function from() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereRaw = knexQueryMethod('orWhereRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereExists = knexQueryMethod('whereExists');
}, {
key: 'fromJS',
value: function fromJS() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereExists = knexQueryMethod('orWhereExists');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNotExists = knexQueryMethod('whereNotExists');
}, {
key: 'into',
value: function into() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereNotExists = knexQueryMethod('orWhereNotExists');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereIn = knexQueryMethod('whereIn');
}, {
key: 'withSchema',
value: function withSchema() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereIn = knexQueryMethod('orWhereIn');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNotIn = knexQueryMethod('whereNotIn');
}, {
key: 'table',
value: function table() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
*/
QueryBuilderBase.prototype.orWhereNotIn = knexQueryMethod('orWhereNotIn');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNull = knexQueryMethod('whereNull');
}, {
key: 'distinct',
value: function distinct() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereNull = knexQueryMethod('orWhereNull');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNotNull = knexQueryMethod('whereNotNull');
}, {
key: 'join',
value: function join() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereNotNull = knexQueryMethod('orWhereNotNull');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereBetween = knexQueryMethod('whereBetween');
}, {
key: 'joinRaw',
value: function joinRaw() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereNotBetween = knexQueryMethod('whereNotBetween');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereBetween = knexQueryMethod('orWhereBetween');
}, {
key: 'innerJoin',
value: function innerJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orWhereNotBetween = knexQueryMethod('orWhereNotBetween');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.groupBy = knexQueryMethod('groupBy');
}, {
key: 'leftJoin',
value: function leftJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.groupByRaw = knexQueryMethod('groupByRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orderBy = knexQueryMethod('orderBy');
}, {
key: 'leftOuterJoin',
value: function leftOuterJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orderByRaw = knexQueryMethod('orderByRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.union = knexQueryMethod('union');
}, {
key: 'rightJoin',
value: function rightJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.unionAll = knexQueryMethod('unionAll');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.having = knexQueryMethod('having');
}, {
key: 'rightOuterJoin',
value: function rightOuterJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.havingRaw = knexQueryMethod('havingRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orHaving = knexQueryMethod('orHaving');
}, {
key: 'outerJoin',
value: function outerJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.orHavingRaw = knexQueryMethod('orHavingRaw');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.offset = knexQueryMethod('offset');
}, {
key: 'fullOuterJoin',
value: function fullOuterJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.limit = knexQueryMethod('limit');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.count = knexQueryMethod('count');
}, {
key: 'crossJoin',
value: function crossJoin() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.countDistinct = knexQueryMethod('countDistinct');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.min = knexQueryMethod('min');
}, {
key: 'where',
value: function where() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.max = knexQueryMethod('max');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.sum = knexQueryMethod('sum');
}, {
key: 'andWhere',
value: function andWhere() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.avg = knexQueryMethod('avg');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.avgDistinct = knexQueryMethod('avgDistinct');
}, {
key: 'orWhere',
value: function orWhere() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.debug = knexQueryMethod('debug');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.returning = knexQueryMethod('returning');
}, {
key: 'whereNot',
value: function whereNot() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.truncate = knexQueryMethod('truncate');
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @method
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.connection = knexQueryMethod('connection');
}, {
key: 'orWhereNot',
value: function orWhereNot() {}
/**
* Compares a column reference to another
*
* ```js
* builder.whereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
QueryBuilderBase.prototype.whereRef = function (lhs, op, rhs) {
return this._whereRef('and', lhs, op, rhs);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* Compares a column reference to another
*
* ```js
* builder.orWhereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
QueryBuilderBase.prototype.orWhereRef = function (lhs, op, rhs) {
return this._whereRef('or', lhs, op, rhs);
};
}, {
key: 'whereRaw',
value: function whereRaw() {}
/**
* Json query APIs
*/
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @typedef {String} FieldExpression
*
* Json field expression to refer to jsonb columns or keys / objects inside columns.
*
* e.g. `Person.jsonColumnName:details.names[1]` would refer to column
* `Person.jsonColumnName` which has `{ details: { names: ['First', 'Second', 'Last'] } }`
* object stored in it.
*
*/
}, {
key: 'whereWrapped',
value: function whereWrapped() {}
/**
* Where jsonb field reference equals jsonb object or other field reference.
*
* Also supports having field expression in both sides of equality.
*
* ```js
* Person
* .query()
* .whereJsonEquals('additionalData:myDogs', 'additionalData:dogsAtHome')
* .then(function (people) {
* // oh joy! these people have all their dogs at home!
* });
*
* Person
* .query()
* .whereJsonEquals('additionalData:myDogs[0]', { name: "peter"})
* .then(function (people) {
* // these people's first dog name is "peter" and the dog has no other
* // attributes, but its name
* });
* ```
*
* @param {FieldExpression} fieldExpression
* Reference to column / json field.
*
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* Reference to column / json field or json object.
*
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonEquals = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "=", jsonObjectOrFieldExpression);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
QueryBuilderBase.prototype.orWhereJsonEquals = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "=", jsonObjectOrFieldExpression);
};
}, {
key: 'havingWrapped',
value: function havingWrapped() {}
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
QueryBuilderBase.prototype.whereJsonNotEquals = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "!=", jsonObjectOrFieldExpression);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
QueryBuilderBase.prototype.orWhereJsonNotEquals = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "!=", jsonObjectOrFieldExpression);
};
}, {
key: 'orWhereRaw',
value: function orWhereRaw() {}
/**
* Where left hand json field reference is a superset of the right hand json value or reference.
*
* ```js
* Person
* .query()
* .whereJsonSupersetOf('additionalData:myDogs', 'additionalData:dogsAtHome')
* .then(function (people) {
* // These people have all or some of their dogs at home. Person might have some
* // additional dogs in their custody since myDogs is supreset of dogsAtHome.
* });
*
* Person
* .query()
* .whereJsonSupersetOf('additionalData:myDogs[0]', { name: "peter"})
* .then(function (people) {
* // These people's first dog name is "peter", but the dog might have
* // additional attributes as well.
* });
* ```
*
* Object and array are always their own supersets.
*
* For arrays this means that left side matches if it has all the elements
* listed in the right hand side. e.g.
*
* ```
* [1,2,3] isSuperSetOf [2] => true
* [1,2,3] isSuperSetOf [2,1,3] => true
* [1,2,3] isSuperSetOf [2,null] => false
* [1,2,3] isSuperSetOf [] => true
* ```
*
* The `not` variants with jsonb operators behave in a way that they won't match rows, which don't have
* the referred json key referred in field expression. e.g. for table
*
* ```
* id | jsonObject
* ----+--------------------------
* 1 | {}
* 2 | NULL
* 3 | {"a": 1}
* 4 | {"a": 1, "b": 2}
* 5 | {"a": ['3'], "b": ['3']}
* ```
*
* query:
*
* ```js
* builder.whereJsonNotEquals("jsonObject:a", "jsonObject:b")
* ```
*
* Returns only the row `4` which has keys `a` and `b` and `a` != `b`, but it won't return any rows which
* does not have `jsonObject.a` or `jsonObject.b`.
*
* @param {FieldExpression} fieldExpression
* Reference to column / json field, which is tested for being a superset.
*
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* To which to compare.
*
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonSupersetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
QueryBuilderBase.prototype.orWhereJsonSupersetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression);
};
}, {
key: 'whereExists',
value: function whereExists() {}
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
QueryBuilderBase.prototype.whereJsonNotSupersetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression, 'not');
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
QueryBuilderBase.prototype.orWhereJsonNotSupersetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression, 'not');
};
}, {
key: 'orWhereExists',
value: function orWhereExists() {}
/**
* Where left hand json field reference is a subset of the right hand json value or reference.
*
* Object and array are always their own subsets.
*
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*
* @param {FieldExpression} fieldExpression
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonSubsetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
QueryBuilderBase.prototype.orWhereJsonSubsetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression);
};
}, {
key: 'whereNotExists',
value: function whereNotExists() {}
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
QueryBuilderBase.prototype.whereJsonNotSubsetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression, 'not');
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
QueryBuilderBase.prototype.orWhereJsonNotSubsetOf = function (fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression, 'not');
};
}, {
key: 'orWhereNotExists',
value: function orWhereNotExists() {}
/**
* Where json field reference is an array.
*
* @param {FieldExpression} fieldExpression
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonIsArray = function (fieldExpression) {
return this.whereJsonSupersetOf(fieldExpression, []);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
*/
QueryBuilderBase.prototype.orWhereJsonIsArray = function (fieldExpression) {
return this.orWhereJsonSupersetOf(fieldExpression, []);
};
}, {
key: 'whereIn',
value: function whereIn() {}
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
* @note Also returns rows where `fieldExpression` does not exist.
*/
QueryBuilderBase.prototype.whereJsonNotArray = function (fieldExpression) {
var knex = this._knex;
// uhh... ugly. own subquery builder could help... now this refers to plain knex subquery builder
return this.where(function () {
// not array
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", [], 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
* @note Also returns rows where `fieldExpression` does not exist.
*/
QueryBuilderBase.prototype.orWhereJsonNotArray = function (fieldExpression) {
var knex = this._knex;
return this.orWhere(function () {
// not array
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", [], 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
};
}, {
key: 'orWhereIn',
value: function orWhereIn() {}
/**
* Where json field reference is an object.
*
* @param {FieldExpression} fieldExpression
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonIsObject = function (fieldExpression) {
return this.whereJsonSupersetOf(fieldExpression, {});
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
*/
QueryBuilderBase.prototype.orWhereJsonIsObject = function (fieldExpression) {
return this.orWhereJsonSupersetOf(fieldExpression, {});
};
}, {
key: 'whereNotIn',
value: function whereNotIn() {}
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
* @note Also returns rows where `fieldExpression` does not exist.
*/
QueryBuilderBase.prototype.whereJsonNotObject = function (fieldExpression) {
var knex = this._knex;
return this.where(function () {
// not object
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", {}, 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
*/
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
* @note Also returns rows where `fieldExpression` does not exist.
*/
QueryBuilderBase.prototype.orWhereJsonNotObject = function (fieldExpression) {
var knex = this._knex;
return this.orWhere(function () {
// not object
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", {}, 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
};
}, {
key: 'orWhereNotIn',
value: function orWhereNotIn() {}
/**
* Where any of given strings is found from json object key(s) or array items.
*
* @param {FieldExpression} fieldExpression
* @param {String|Array.<String>} keys Strings that are looked from object or array.
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonHasAny = function (fieldExpression, keys) {
return whereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?|', keys);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonHasAny}
*/
QueryBuilderBase.prototype.orWhereJsonHasAny = function (fieldExpression, keys) {
return orWhereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?|', keys);
};
}, {
key: 'whereNull',
value: function whereNull() {}
/**
* Where all of given strings are found from json object key(s) or array items.
*
* @param {FieldExpression} fieldExpression
* @param {String|Array.<String>} keys Strings that are looked from object or array.
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonHasAll = function (fieldExpression, keys) {
return whereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?&', keys);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonHasAll}
*/
QueryBuilderBase.prototype.orWhereJsonHasAll = function (fieldExpression, keys) {
return orWhereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?&', keys);
};
}, {
key: 'orWhereNull',
value: function orWhereNull() {}
/**
* Where referred json field value casted to same type with value fulfill given operand.
*
* Value may be number, string, null, boolean and referred json field is converted
* to TEXT, NUMERIC or BOOLEAN sql type for comparison.
*
* If left hand field does not exist rows appear IS null so if one needs to get only
* rows, which has key and it's value is null one may use e.g.
* `.whereJsonSupersetOf("column", { field: null })` or check is key exist and
* then `.whereJsonField('column:field', 'IS', null)`
*
* For testing against objects or arrays one should see tested with whereJsonEqual,
* whereJsonSupersetOf and whereJsonSubsetOf methods.
*
* @param {FieldExpression} fieldExpression Expression pointing to certain value.
* @param {String} operator SQL comparator usually `<`, `>`, `<>`, `=` or `!=`
* @param {Boolean|Number|String|null} value Value to which field is compared to.
* @returns {QueryBuilderBase}
*/
QueryBuilderBase.prototype.whereJsonField = function (fieldExpression, operator, value) {
var query = whereJsonFieldQuery(this._knex, fieldExpression, operator, value);
return this.whereRaw(query);
};
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
/**
* @see {@link QueryBuilderBase#whereJsonField}
*/
QueryBuilderBase.prototype.orWhereJsonField = function (fieldExpression, operator, value) {
var query = whereJsonFieldQuery(this._knex, fieldExpression, operator, value);
return this.orWhereRaw(query);
};
}, {
key: 'whereNotNull',
value: function whereNotNull() {}
/**
* @private
*/
QueryBuilderBase.prototype._whereRef = function (bool, lhs, op, rhs) {
if (!rhs) {
rhs = op;
op = '=';
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
var formatter = this._knex.client.formatter();
op = formatter.operator(op);
}, {
key: 'orWhereNotNull',
value: function orWhereNotNull() {}
if (!_.isString(lhs) || !_.isString(rhs) || !_.isString(op)) {
throw new Error('whereRef: invalid operands or operator');
}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
var sql = formatter.wrap(lhs) + ' ' + op + ' ' + formatter.wrap(rhs);
if (bool === 'or') {
return this.orWhereRaw(sql);
} else {
return this.whereRaw(sql);
}
};
}, {
key: 'whereBetween',
value: function whereBetween() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereNotBetween',
value: function whereNotBetween() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orWhereBetween',
value: function orWhereBetween() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orWhereNotBetween',
value: function orWhereNotBetween() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'groupBy',
value: function groupBy() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'groupByRaw',
value: function groupByRaw() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orderBy',
value: function orderBy() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orderByRaw',
value: function orderByRaw() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'union',
value: function union() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'unionAll',
value: function unionAll() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'having',
value: function having() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'havingRaw',
value: function havingRaw() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orHaving',
value: function orHaving() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'orHavingRaw',
value: function orHavingRaw() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'offset',
value: function offset() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'limit',
value: function limit() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'count',
value: function count() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'countDistinct',
value: function countDistinct() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'min',
value: function min() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'max',
value: function max() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'sum',
value: function sum() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'avg',
value: function avg() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'avgDistinct',
value: function avgDistinct() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'debug',
value: function debug() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'returning',
value: function returning() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'truncate',
value: function truncate() {}
/**
* See <a href="http://knexjs.org">knex documentation</a>
* @returns {QueryBuilderBase}
*/
}, {
key: 'connection',
value: function connection() {}
/**
* Compares a column reference to another
*
* ```js
* builder.whereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
}, {
key: 'whereRef',
value: function whereRef(lhs, op, rhs) {
return this._whereRef('and', lhs, op, rhs);
}
/**
* Compares a column reference to another
*
* ```js
* builder.orWhereRef('Person.id', '=', 'Animal.ownerId');
* ```
*/
}, {
key: 'orWhereRef',
value: function orWhereRef(lhs, op, rhs) {
return this._whereRef('or', lhs, op, rhs);
}
/**
* Json query APIs
*/
/**
* @typedef {string} FieldExpression
*
* Json field expression to refer to jsonb columns or keys / objects inside columns.
*
* e.g. `Person.jsonColumnName:details.names[1]` would refer to column
* `Person.jsonColumnName` which has `{ details: { names: ['First', 'Second', 'Last'] } }`
* object stored in it.
*
*/
/**
* Where jsonb field reference equals jsonb object or other field reference.
*
* Also supports having field expression in both sides of equality.
*
* ```js
* Person
* .query()
* .whereJsonEquals('additionalData:myDogs', 'additionalData:dogsAtHome')
* .then(function (people) {
* // oh joy! these people have all their dogs at home!
* });
*
* Person
* .query()
* .whereJsonEquals('additionalData:myDogs[0]', { name: "peter"})
* .then(function (people) {
* // these people's first dog name is "peter" and the dog has no other
* // attributes, but its name
* });
* ```
*
* @param {FieldExpression} fieldExpression
* Reference to column / json field.
*
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* Reference to column / json field or json object.
*
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonEquals',
value: function whereJsonEquals(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "=", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
}, {
key: 'orWhereJsonEquals',
value: function orWhereJsonEquals(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "=", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
}, {
key: 'whereJsonNotEquals',
value: function whereJsonNotEquals(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "!=", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonEquals}
*/
}, {
key: 'orWhereJsonNotEquals',
value: function orWhereJsonNotEquals(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "!=", jsonObjectOrFieldExpression);
}
/**
* Where left hand json field reference is a superset of the right hand json value or reference.
*
* ```js
* Person
* .query()
* .whereJsonSupersetOf('additionalData:myDogs', 'additionalData:dogsAtHome')
* .then(function (people) {
* // These people have all or some of their dogs at home. Person might have some
* // additional dogs in their custody since myDogs is supreset of dogsAtHome.
* });
*
* Person
* .query()
* .whereJsonSupersetOf('additionalData:myDogs[0]', { name: "peter"})
* .then(function (people) {
* // These people's first dog name is "peter", but the dog might have
* // additional attributes as well.
* });
* ```
*
* Object and array are always their own supersets.
*
* For arrays this means that left side matches if it has all the elements
* listed in the right hand side. e.g.
*
* ```
* [1,2,3] isSuperSetOf [2] => true
* [1,2,3] isSuperSetOf [2,1,3] => true
* [1,2,3] isSuperSetOf [2,null] => false
* [1,2,3] isSuperSetOf [] => true
* ```
*
* The `not` variants with jsonb operators behave in a way that they won't match rows, which don't have
* the referred json key referred in field expression. e.g. for table
*
* ```
* id | jsonObject
* ----+--------------------------
* 1 | {}
* 2 | NULL
* 3 | {"a": 1}
* 4 | {"a": 1, "b": 2}
* 5 | {"a": ['3'], "b": ['3']}
* ```
*
* query:
*
* ```js
* builder.whereJsonNotEquals("jsonObject:a", "jsonObject:b")
* ```
*
* Returns only the row `4` which has keys `a` and `b` and `a` != `b`, but it won't return any rows which
* does not have `jsonObject.a` or `jsonObject.b`.
*
* @param {FieldExpression} fieldExpression
* Reference to column / json field, which is tested for being a superset.
*
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* To which to compare.
*
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonSupersetOf',
value: function whereJsonSupersetOf(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
}, {
key: 'orWhereJsonSupersetOf',
value: function orWhereJsonSupersetOf(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
}, {
key: 'whereJsonNotSupersetOf',
value: function whereJsonNotSupersetOf(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression, 'not');
}
/**
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*/
}, {
key: 'orWhereJsonNotSupersetOf',
value: function orWhereJsonNotSupersetOf(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", jsonObjectOrFieldExpression, 'not');
}
/**
* Where left hand json field reference is a subset of the right hand json value or reference.
*
* Object and array are always their own subsets.
*
* @see {@link QueryBuilderBase#whereJsonSupersetOf}
*
* @param {FieldExpression} fieldExpression
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonSubsetOf',
value: function whereJsonSubsetOf(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
}, {
key: 'orWhereJsonSubsetOf',
value: function orWhereJsonSubsetOf(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression);
}
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
}, {
key: 'whereJsonNotSubsetOf',
value: function whereJsonNotSubsetOf(fieldExpression, jsonObjectOrFieldExpression) {
return whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression, 'not');
}
/**
* @see {@link QueryBuilderBase#whereJsonSubsetOf}
*/
}, {
key: 'orWhereJsonNotSubsetOf',
value: function orWhereJsonNotSubsetOf(fieldExpression, jsonObjectOrFieldExpression) {
return orWhereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "<@", jsonObjectOrFieldExpression, 'not');
}
/**
* Where json field reference is an array.
*
* @param {FieldExpression} fieldExpression
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonIsArray',
value: function whereJsonIsArray(fieldExpression) {
return this.whereJsonSupersetOf(fieldExpression, []);
}
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
*/
}, {
key: 'orWhereJsonIsArray',
value: function orWhereJsonIsArray(fieldExpression) {
return this.orWhereJsonSupersetOf(fieldExpression, []);
}
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
* @note Also returns rows where `fieldExpression` does not exist.
*/
}, {
key: 'whereJsonNotArray',
value: function whereJsonNotArray(fieldExpression) {
var knex = this._knex;
// uhh... ugly. own subquery builder could help... now this refers to plain knex subquery builder
return this.where(function () {
// not array
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", [], 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
}
/**
* @see {@link QueryBuilderBase#whereJsonIsArray}
* @note Also returns rows where `fieldExpression` does not exist.
*/
}, {
key: 'orWhereJsonNotArray',
value: function orWhereJsonNotArray(fieldExpression) {
var knex = this._knex;
return this.orWhere(function () {
// not array
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", [], 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
}
/**
* Where json field reference is an object.
*
* @param {FieldExpression} fieldExpression
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonIsObject',
value: function whereJsonIsObject(fieldExpression) {
return this.whereJsonSupersetOf(fieldExpression, {});
}
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
*/
}, {
key: 'orWhereJsonIsObject',
value: function orWhereJsonIsObject(fieldExpression) {
return this.orWhereJsonSupersetOf(fieldExpression, {});
}
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
* @note Also returns rows where `fieldExpression` does not exist.
*/
}, {
key: 'whereJsonNotObject',
value: function whereJsonNotObject(fieldExpression) {
var knex = this._knex;
return this.where(function () {
// not object
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", {}, 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
}
/**
* @see {@link QueryBuilderBase#whereJsonIsObject}
* @note Also returns rows where `fieldExpression` does not exist.
*/
}, {
key: 'orWhereJsonNotObject',
value: function orWhereJsonNotObject(fieldExpression) {
var knex = this._knex;
return this.orWhere(function () {
// not object
var builder = whereJsonbRefOnLeftJsonbValOrRefOnRight(this, fieldExpression, "@>", {}, 'not');
var ifRefNotExistQuery = whereJsonFieldQuery(knex, fieldExpression, "IS", null);
// or not exist
builder.orWhereRaw(ifRefNotExistQuery);
});
}
/**
* Where any of given strings is found from json object key(s) or array items.
*
* @param {FieldExpression} fieldExpression
* @param {string|Array.<string>} keys Strings that are looked from object or array.
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonHasAny',
value: function whereJsonHasAny(fieldExpression, keys) {
return whereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?|', keys);
}
/**
* @see {@link QueryBuilderBase#whereJsonHasAny}
*/
}, {
key: 'orWhereJsonHasAny',
value: function orWhereJsonHasAny(fieldExpression, keys) {
return orWhereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?|', keys);
}
/**
* Where all of given strings are found from json object key(s) or array items.
*
* @param {FieldExpression} fieldExpression
* @param {string|Array.<string>} keys Strings that are looked from object or array.
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonHasAll',
value: function whereJsonHasAll(fieldExpression, keys) {
return whereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?&', keys);
}
/**
* @see {@link QueryBuilderBase#whereJsonHasAll}
*/
}, {
key: 'orWhereJsonHasAll',
value: function orWhereJsonHasAll(fieldExpression, keys) {
return orWhereJsonFieldRightStringArrayOnLeft(this, fieldExpression, '?&', keys);
}
/**
* Where referred json field value casted to same type with value fulfill given operand.
*
* Value may be number, string, null, boolean and referred json field is converted
* to TEXT, NUMERIC or BOOLEAN sql type for comparison.
*
* If left hand field does not exist rows appear IS null so if one needs to get only
* rows, which has key and it's value is null one may use e.g.
* `.whereJsonSupersetOf("column", { field: null })` or check is key exist and
* then `.whereJsonField('column:field', 'IS', null)`
*
* For testing against objects or arrays one should see tested with whereJsonEqual,
* whereJsonSupersetOf and whereJsonSubsetOf methods.
*
* @param {FieldExpression} fieldExpression Expression pointing to certain value.
* @param {string} operator SQL comparator usually `<`, `>`, `<>`, `=` or `!=`
* @param {boolean|Number|string|null} value Value to which field is compared to.
* @returns {QueryBuilderBase}
*/
}, {
key: 'whereJsonField',
value: function whereJsonField(fieldExpression, operator, value) {
var query = whereJsonFieldQuery(this._knex, fieldExpression, operator, value);
return this.whereRaw(query);
}
/**
* @see {@link QueryBuilderBase#whereJsonField}
*/
}, {
key: 'orWhereJsonField',
value: function orWhereJsonField(fieldExpression, operator, value) {
var query = whereJsonFieldQuery(this._knex, fieldExpression, operator, value);
return this.orWhereRaw(query);
}
/**
* @private
*/
}, {
key: '_whereRef',
value: function _whereRef(bool, lhs, op, rhs) {
if (!rhs) {
rhs = op;
op = '=';
}
var formatter = this._knex.client.formatter();
op = formatter.operator(op);
if (!_lodash2.default.isString(lhs) || !_lodash2.default.isString(rhs) || !_lodash2.default.isString(op)) {
throw new Error('whereRef: invalid operands or operator');
}
var sql = formatter.wrap(lhs) + ' ' + op + ' ' + formatter.wrap(rhs);
if (bool === 'or') {
return this.orWhereRaw(sql);
} else {
return this.whereRaw(sql);
}
}
}], [{
key: 'extend',
value: function extend(subclassConstructor) {
_utils2.default.inherits(subclassConstructor, this);
return subclassConstructor;
}
}]);
return QueryBuilderBase;
})(), (_applyDecoratedDescriptor(_class.prototype, 'insert', [_dec], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'insert'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'update', [_dec2], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'update'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'delete', [_dec3], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'delete'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'del', [_dec4], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'del'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'select', [_dec5], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'select'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'as', [_dec6], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'as'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'columns', [_dec7], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'columns'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'column', [_dec8], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'column'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'from', [_dec9], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'from'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'fromJS', [_dec10], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'fromJS'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'into', [_dec11], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'into'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'withSchema', [_dec12], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'withSchema'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'table', [_dec13], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'table'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'distinct', [_dec14], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'distinct'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'join', [_dec15], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'join'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'joinRaw', [_dec16], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'joinRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'innerJoin', [_dec17], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'innerJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'leftJoin', [_dec18], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'leftJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'leftOuterJoin', [_dec19], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'leftOuterJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'rightJoin', [_dec20], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'rightJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'rightOuterJoin', [_dec21], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'rightOuterJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'outerJoin', [_dec22], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'outerJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'fullOuterJoin', [_dec23], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'fullOuterJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'crossJoin', [_dec24], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'crossJoin'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'where', [_dec25], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'where'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'andWhere', [_dec26], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'andWhere'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhere', [_dec27], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhere'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNot', [_dec28], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNot'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNot', [_dec29], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNot'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereRaw', [_dec30], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereWrapped', [_dec31], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereWrapped'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'havingWrapped', [_dec32], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'havingWrapped'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereRaw', [_dec33], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereExists', [_dec34], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereExists'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereExists', [_dec35], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereExists'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNotExists', [_dec36], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNotExists'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNotExists', [_dec37], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNotExists'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereIn', [_dec38], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereIn'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereIn', [_dec39], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereIn'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNotIn', [_dec40], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNotIn'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNotIn', [_dec41], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNotIn'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNull', [_dec42], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNull'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNull', [_dec43], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNull'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNotNull', [_dec44], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNotNull'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNotNull', [_dec45], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNotNull'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereBetween', [_dec46], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereBetween'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'whereNotBetween', [_dec47], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'whereNotBetween'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereBetween', [_dec48], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereBetween'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orWhereNotBetween', [_dec49], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orWhereNotBetween'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'groupBy', [_dec50], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'groupBy'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'groupByRaw', [_dec51], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'groupByRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orderBy', [_dec52], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orderBy'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orderByRaw', [_dec53], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orderByRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'union', [_dec54], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'union'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'unionAll', [_dec55], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'unionAll'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'having', [_dec56], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'having'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'havingRaw', [_dec57], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'havingRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orHaving', [_dec58], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orHaving'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'orHavingRaw', [_dec59], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'orHavingRaw'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'offset', [_dec60], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'offset'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'limit', [_dec61], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'limit'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'count', [_dec62], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'count'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'countDistinct', [_dec63], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'countDistinct'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'min', [_dec64], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'min'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'max', [_dec65], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'max'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'sum', [_dec66], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'sum'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'avg', [_dec67], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'avg'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'avgDistinct', [_dec68], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'avgDistinct'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'debug', [_dec69], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'debug'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'returning', [_dec70], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'returning'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'truncate', [_dec71], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'truncate'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'connection', [_dec72], (0, _getOwnPropertyDescriptor2.default)(_class.prototype, 'connection'), _class.prototype)), _class));
/**
* @returns {Function}
* @private
*/
function knexQueryMethod(methodName) {
/**
* @returns {QueryBuilderBase}
*/
return function () {
var args = new Array(arguments.length);
var knex = this._knex;
for (var i = 0, l = arguments.length; i < l; ++i) {
if (arguments[i] === undefined) {
// None of the query builder methods should accept undefined. Do nothing if
// one of the arguments is undefined. This enables us to do things like
// `.where('name', req.query.name)` without checking if req.query has the
// property `name`.
return this;
} else if (arguments[i] instanceof QueryBuilderBase) {
// Convert QueryBuilderBase instances into knex query builders.
args[i] = arguments[i].build();
} else if (_.isFunction(arguments[i])) {
// If an argument is a function, knex calls it with a query builder as
// `this` context. We call the function with a QueryBuilderBase as
// `this` context instead.
args[i] = wrapFunctionArg(knex, arguments[i]);
} else {
args[i] = arguments[i];
exports.default = QueryBuilderBase;
function knexQueryMethod(overrideMethodName) {
return function (target, methodName, descriptor) {
descriptor.value = function () {
var args = new Array(arguments.length);
var context = this.internalContext();
for (var i = 0, l = arguments.length; i < l; ++i) {
if (arguments[i] === undefined) {
// None of the query builder methods should accept undefined. Do nothing if
// one of the arguments is undefined. This enables us to do things like
// `.where('name', req.query.name)` without checking if req.query has the
// property `name`.
return this;
} else if (arguments[i] instanceof QueryBuilderBase) {
// Convert QueryBuilderBase instances into knex query builders.
args[i] = arguments[i].internalContext(context).build();
} else if (_lodash2.default.isFunction(arguments[i])) {
// If an argument is a function, knex calls it with a query builder as
// `this` context. We call the function with a QueryBuilderBase as
// `this` context instead.
args[i] = wrapFunctionArg(arguments[i], this);
} else {
args[i] = arguments[i];
}
}
}
this._knexMethodCalls.push({
method: methodName,
args: args
});
this._knexMethodCalls.push({
method: overrideMethodName || methodName,
args: args
});
return this;
return this;
};
};

@@ -1127,9 +1496,8 @@ }

*/
function wrapFunctionArg(knex, func) {
function wrapFunctionArg(func, query) {
return function () {
var builder = new QueryBuilderBase(knex);
var knexBuilder = this;
var context = query.internalContext();
var builder = new QueryBuilderBase(query._knex).internalContext(context);
func.call(builder, builder);
builder.buildInto(knexBuilder);
builder.buildInto(this);
};

@@ -1139,8 +1507,17 @@ }

/**
* Field expression how to refer certain nested field inside jsonb column.
* Parses a objection.js json field expression into a postgres jsonb field reference.
*
* Table.jsonColumn:obj[key.with.dots][0].key.0.me[0] refers to
* obj = { "key.with.dots" : [{"key" : { "0": { me : [ "I was referred" ] }}}]
* For example, assume we have this object stored in the column `jsonColumn` of a table `Table`:
*
* Since PostgreSql #>{field,0,field2,...} operator does not make difference if
* ```
* {obj: { "key.with.dots": [{"key": { "0": { me : [ "I was referred" ] } } } ] } }
* ```
*
* We can refer to the value "I was referred" using the following field expression:
*
* ```
* Table.jsonColumn:obj[key.with.dots][0].key.0.me[0]
* ```
*
* Since Postgresql #>{field,0,field2,...} operator does not make difference if
* reference is string or a number, one can actually use also jsonArray.0 notation

@@ -1150,14 +1527,13 @@ * to refer index of an array. Like wise one can use object[123] notation to refer

*
* @param {String} expression
* @param {Boolean} extractAsText Return text instead of jsonb object (useful for type casting).
* @private
* @returns {Array} Array of referred path, where first item is table/column.
* @param {string} expression
* @param {boolean} extractAsText Return text instead of jsonb object (useful for type casting).
* @returns {string} postgres json reference.
*/
function parseFieldExpression(expression, extractAsText) {
var parsed = jsonFieldExpressionParser.parse(expression);
var jsonRefs = _(parsed.access).pluck('ref').value().join(",");
var parsed = _jsonFieldExpressionParser2.default.parse(expression);
var jsonRefs = (0, _lodash2.default)(parsed.access).pluck('ref').value().join(",");
var extractor = extractAsText ? '#>>' : '#>';
// TODO: knex.raw('??', parsed.columnName) could work with latest knex
var middleQuotedColumnName = parsed.columnName.split('.').join('"."');
return ['"', middleQuotedColumnName, '"', extractor, "'{", jsonRefs, "}'"].join("");
return '"' + middleQuotedColumnName + '"' + extractor + '\'{' + jsonRefs + '}\'';
}

@@ -1179,8 +1555,8 @@

*
* @private
* @param {QueryBuilderBase} builder
* @param {FieldExpression} fieldExpression Reference to column / jsonField.
* @param {String} operator operator to apply.
* @param {string} operator operator to apply.
* @param {Object|Array|FieldExpression} jsonObjectOrFieldExpression Reference to column / jsonField or json object.
* @param {String=} queryPrefix String prepended to query e.g. 'not'. Space after string added implicitly.
* @private
* @param {string=} queryPrefix string prepended to query e.g. 'not'. Space after string added implicitly.
* @returns {QueryBuilderBase}

@@ -1210,3 +1586,3 @@ */

if (_.isString(jsonObjectOrFieldExpression)) {
if (_lodash2.default.isString(jsonObjectOrFieldExpression)) {
var rightHandReference = parseFieldExpression(jsonObjectOrFieldExpression);

@@ -1218,3 +1594,3 @@ var refRefQuery = ["(", fieldReference, ")::jsonb", operator, "(", rightHandReference, ")::jsonb"];

return [refRefQuery.join(" ")];
} else if (_.isObject(jsonObjectOrFieldExpression)) {
} else if (_lodash2.default.isObject(jsonObjectOrFieldExpression)) {
var refValQuery = ["(", fieldReference, ")::jsonb", operator, "?::jsonb"];

@@ -1224,3 +1600,3 @@ if (queryPrefix) {

}
return [refValQuery.join(" "), JSON.stringify(jsonObjectOrFieldExpression)];
return [refValQuery.join(" "), (0, _stringify2.default)(jsonObjectOrFieldExpression)];
}

@@ -1242,6 +1618,7 @@

*
* @private
* @param {QueryBuilderBase} builder
* @param {FieldExpression} fieldExpression
* @param {String} operator
* @param {Array.<String>} keys
* @param {string} operator
* @param {Array.<string>} keys
* @returns {QueryBuilderBase}

@@ -1268,6 +1645,6 @@ */

var fieldReference = parseFieldExpression(fieldExpression);
keys = _.isArray(keys) ? keys : [keys];
keys = _lodash2.default.isArray(keys) ? keys : [keys];
var questionMarksArray = _.map(keys, function (key) {
if (!_.isString(key)) {
var questionMarksArray = _lodash2.default.map(keys, function (key) {
if (!_lodash2.default.isString(key)) {
throw new Error("All keys to find must be strings.");

@@ -1281,3 +1658,3 @@ }

return [fieldReference, " ", operator.replace('?', '\\?'), " ", rightHandExpression].join("");
return fieldReference + ' ' + operator.replace('?', '\\?') + ' ' + rightHandExpression;
}

@@ -1294,11 +1671,11 @@

// json type comparison takes json type in string format
var cast;
var cast = undefined;
var escapedValue = knex.raw(" ?", [value]);
if (_.isNumber(value)) {
if (_lodash2.default.isNumber(value)) {
cast = "::NUMERIC";
} else if (_.isBoolean(value)) {
} else if (_lodash2.default.isBoolean(value)) {
cast = "::BOOLEAN";
} else if (_.isString(value)) {
} else if (_lodash2.default.isString(value)) {
cast = "::TEXT";
} else if (_.isNull(value)) {
} else if (_lodash2.default.isNull(value)) {
cast = "::TEXT";

@@ -1309,3 +1686,4 @@ escapedValue = 'NULL';

}
return ["(", fieldReference, ")", cast, " ", normalizedOperator," ", escapedValue].join("")
return '(' + fieldReference + ')' + cast + ' ' + normalizedOperator + ' ' + escapedValue;
}

@@ -1316,4 +1694,4 @@

* @param knex
* @param {String} operator
* @returns {String}
* @param {string} operator
* @returns {string}
*/

@@ -1330,4 +1708,2 @@ function normalizeOperator(knex, operator) {

}
}
module.exports = QueryBuilderBase;
}
'use strict';
var _ = require('lodash')
, parser = require('./parsers/relationExpressionParser')
, ValidationError = require('./../ValidationError');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _relationExpressionParser = require('./parsers/relationExpressionParser');
var _relationExpressionParser2 = _interopRequireDefault(_relationExpressionParser);
var _ValidationError = require('./../ValidationError');
var _ValidationError2 = _interopRequireDefault(_ValidationError);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**

@@ -64,128 +91,150 @@ * Relation expression is a simple DSL for expressing relation trees.

* the argument `arg3`.
*
* @constructor
*/
function RelationExpression(node) {
node = node || {};
this.name = node.name || null;
this.args = node.args || [];
this.numChildren = node.numChildren || 0;
this.children = node.children || {};
}
/**
* Parses an expression string into a {@link RelationExpression} object.
*
* @param {String|RelationExpression} expr
* @returns {RelationExpression}
*/
RelationExpression.parse = function (expr) {
if (expr instanceof RelationExpression) {
return expr;
} else if (!_.isString(expr) || _.isEmpty(expr.trim())) {
return new RelationExpression();
} else {
try {
return new RelationExpression(parser.parse(expr));
} catch (err) {
throw new ValidationError({
message: 'Invalid relation expression "' + expr + '"',
cause: err.message
});
}
var RelationExpression = (function () {
function RelationExpression(node) {
(0, _classCallCheck3.default)(this, RelationExpression);
node = node || {};
this.name = node.name || null;
this.args = node.args || [];
this.numChildren = node.numChildren || 0;
this.children = node.children || {};
}
};
/**
* Tests if another expression is a sub expression of this one.
*
* Expression B is a sub expression of expression A if:
*
* - A and B have the same root
* - And each path from root to a leaf in B can be found in A
*
* For example sub expressions of `children.[movies.actors, pets]` are:
*
* - `children`
* - `children.movies`
* - `children.pets`
* - `children.movies.actors`
* - `children.[movies, pets]`
* - `children.[movies.actors, pets]`
*
* @param {String|RelationExpression} expr
* @returns {boolean}
*/
RelationExpression.prototype.isSubExpression = function (expr) {
var self = this;
/**
* Parses an expression string into a {@link RelationExpression} object.
*
* @param {string|RelationExpression} expr
* @returns {RelationExpression}
*/
expr = RelationExpression.parse(expr);
(0, _createClass3.default)(RelationExpression, [{
key: 'isSubExpression',
if (this.isAllRecursive()) {
return true;
}
/**
* Tests if another expression is a sub expression of this one.
*
* Expression B is a sub expression of expression A if:
*
* - A and B have the same root
* - And each path from root to a leaf in B can be found in A
*
* For example sub expressions of `children.[movies.actors, pets]` are:
*
* - `children`
* - `children.movies`
* - `children.pets`
* - `children.movies.actors`
* - `children.[movies, pets]`
* - `children.[movies.actors, pets]`
*
* @param {string|RelationExpression} expr
* @returns {boolean}
*/
value: function isSubExpression(expr) {
var _this = this;
if (expr.isAllRecursive()) {
return this.isAllRecursive();
}
expr = RelationExpression.parse(expr);
if (this.name !== expr.name) {
return false;
}
if (this.isAllRecursive()) {
return true;
}
if (expr.isRecursive()) {
return this.isAllRecursive() || this.isRecursive();
}
if (expr.isAllRecursive()) {
return this.isAllRecursive();
}
return _.all(expr.children, function (child, childName) {
var ownSubExpression = self.childExpression(childName);
var subExpression = expr.childExpression(childName);
if (this.name !== expr.name) {
return false;
}
return ownSubExpression && ownSubExpression.isSubExpression(subExpression);
});
};
if (expr.isRecursive()) {
return this.isAllRecursive() || this.isRecursive();
}
/**
* @ignore
* @returns {boolean}
*/
RelationExpression.prototype.isRecursive = function () {
return !!_.find(this.children, {name: '^'});
};
return _lodash2.default.all(expr.children, function (child, childName) {
var ownSubExpression = _this.childExpression(childName);
var subExpression = expr.childExpression(childName);
/**
* @ignore
* @returns {boolean}
*/
RelationExpression.prototype.isAllRecursive = function () {
return this.numChildren === 1 && this.children[Object.keys(this.children)[0]].name === '*';
};
return ownSubExpression && ownSubExpression.isSubExpression(subExpression);
});
}
/**
* @ignore
* @returns {RelationExpression}
*/
RelationExpression.prototype.childExpression = function (childName) {
if (this.isAllRecursive() || (this.isRecursive() && childName === this.name)) {
return this;
}
/**
* @ignore
* @returns {boolean}
*/
if (this.children[childName]) {
return new RelationExpression(this.children[childName]);
} else {
return null;
}
};
}, {
key: 'isRecursive',
value: function isRecursive() {
return !!_lodash2.default.find(this.children, { name: '^' });
}
/**
* @ignore
*/
RelationExpression.prototype.forEachChild = function (cb) {
_.each(this.children, function (child, childName) {
if (childName !== '*' && childName !== '^') {
cb(child, childName);
/**
* @ignore
* @returns {boolean}
*/
}, {
key: 'isAllRecursive',
value: function isAllRecursive() {
return this.numChildren === 1 && this.children[(0, _keys2.default)(this.children)[0]].name === '*';
}
})
};
module.exports = RelationExpression;
/**
* @ignore
* @returns {RelationExpression}
*/
}, {
key: 'childExpression',
value: function childExpression(childName) {
if (this.isAllRecursive() || this.isRecursive() && childName === this.name) {
return this;
}
if (this.children[childName]) {
return new RelationExpression(this.children[childName]);
} else {
return null;
}
}
/**
* @ignore
*/
}, {
key: 'forEachChild',
value: function forEachChild(cb) {
_lodash2.default.each(this.children, function (child, childName) {
if (childName !== '*' && childName !== '^') {
cb(child, childName);
}
});
}
}], [{
key: 'parse',
value: function parse(expr) {
if (expr instanceof RelationExpression) {
return expr;
} else if (!_lodash2.default.isString(expr) || _lodash2.default.isEmpty(expr.trim())) {
return new RelationExpression();
} else {
try {
return new RelationExpression(_relationExpressionParser2.default.parse(expr));
} catch (err) {
throw new _ValidationError2.default({
message: 'Invalid relation expression "' + expr + '"',
cause: err.message
});
}
}
}
}]);
return RelationExpression;
})();
exports.default = RelationExpression;
'use strict';
var _ = require('lodash')
, Relation = require('./Relation')
, ownerJoinColumnAlias = 'objectiontmpjoin';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _get2 = require('babel-runtime/helpers/get');
var _get3 = _interopRequireDefault(_get2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _utils = require('../utils');
var _utils2 = _interopRequireDefault(_utils);
var _Relation2 = require('./Relation');
var _Relation3 = _interopRequireDefault(_Relation2);
var _inheritModel = require('../model/inheritModel');
var _inheritModel2 = _interopRequireDefault(_inheritModel);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var ownerJoinColumnAlias = 'objectiontmpjoin';;
/**
* @constructor
* @ignore
* @extends Relation
*/
function ManyToManyRelation() {
Relation.apply(this, arguments);
}
Relation.extend(ManyToManyRelation);
var ManyToManyRelation = (function (_Relation) {
(0, _inherits3.default)(ManyToManyRelation, _Relation);
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.setMapping = function (mapping) {
var retVal = Relation.prototype.setMapping.call(this, mapping);
function ManyToManyRelation() {
var _Object$getPrototypeO;
if (!this.joinTable || !this.joinTableOwnerCol || !this.joinTableRelatedCol) {
throw new Error(this.ownerModelClass.name + '.relationMappings.' + this.name + '.join must have the `through` that describes the join table.');
(0, _classCallCheck3.default)(this, ManyToManyRelation);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
/**
* The join table.
*
* @type {string}
*/
var _this = (0, _possibleConstructorReturn3.default)(this, (_Object$getPrototypeO = (0, _getPrototypeOf2.default)(ManyToManyRelation)).call.apply(_Object$getPrototypeO, [this].concat(args)));
_this.joinTable = null;
/**
* The relation column in the join table that points to the owner table.
*
* @type {string}
*/
_this.joinTableOwnerCol = null;
/**
* The relation property in the join model that points to the owner table.
*
* @type {string}
*/
_this.joinTableOwnerProp = null;
/**
* The relation column in the join table that points to the related table.
*
* @type {string}
*/
_this.joinTableRelatedCol = null;
/**
* The relation property in the join model that points to the related table.
*
* @type {string}
*/
_this.joinTableRelatedProp = null;
/**
* The join table model class.
*
* This can be optionally given using the `join.through.modelClass` property,
* otherwise an anonymous model class is created in `setMapping` method.
*
* @type {Class<Model>}
*/
_this.joinTableModelClass = null;
return _this;
}
return retVal;
};
/**
* @override
* @inheritDoc
*/
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.findQuery = function (builder, ownerCol, isColumnRef) {
builder.join(this.joinTable, this.fullJoinTableRelatedCol(), this.fullRelatedCol());
(0, _createClass3.default)(ManyToManyRelation, [{
key: 'setMapping',
value: function setMapping(mapping) {
// Avoid require loop and import here.
var Model = require(__dirname + '/../model/Model').default;
if (isColumnRef) {
builder.whereRef(this.fullJoinTableOwnerCol(), ownerCol);
} else {
if (_.isArray(ownerCol)) {
builder.whereIn(this.fullJoinTableOwnerCol(), ownerCol);
} else {
builder.where(this.fullJoinTableOwnerCol(), ownerCol);
var retVal = _Relation3.default.prototype.setMapping.call(this, mapping);
var errorPrefix = this.ownerModelClass.name + '.relationMappings.' + this.name;
if (!_lodash2.default.isObject(mapping.join.through)) {
throw new Error(errorPrefix + '.join must have the `through` that describes the join table.');
}
if (!_lodash2.default.isString(mapping.join.through.from) || !_lodash2.default.isString(mapping.join.through.to)) {
throw new Error(errorPrefix + '.join.through must be an object that describes the join table. For example: {from: \'JoinTable.someId\', to: \'JoinTable.someOtherId\'}');
}
var joinFrom = _Relation3.default.parseColumn(mapping.join.from);
var joinTableFrom = _Relation3.default.parseColumn(mapping.join.through.from);
var joinTableTo = _Relation3.default.parseColumn(mapping.join.through.to);
if (!joinTableFrom.table || !joinTableFrom.name) {
throw new Error(errorPrefix + '.join.through.from must have format JoinTable.columnName. For example `JoinTable.someId`.');
}
if (!joinTableTo.table || !joinTableTo.name) {
throw new Error(errorPrefix + '.join.through.to must have format JoinTable.columnName. For example `JoinTable.someId`.');
}
if (joinTableFrom.table !== joinTableTo.table) {
throw new Error(errorPrefix + '.join.through `from` and `to` must point to the same join table.');
}
this.joinTable = joinTableFrom.table;
if (joinFrom.table === this.ownerModelClass.tableName) {
this.joinTableOwnerCol = joinTableFrom.name;
this.joinTableRelatedCol = joinTableTo.name;
} else {
this.joinTableRelatedCol = joinTableFrom.name;
this.joinTableOwnerCol = joinTableTo.name;
}
if (mapping.join.through.modelClass) {
if (!_utils2.default.isSubclassOf(mapping.join.through.modelClass, Model)) {
throw new Error('Join table model class is not a subclass of Model');
}
this.joinTableModelClass = mapping.join.through.modelClass;
} else {
this.joinTableModelClass = (0, _inheritModel2.default)(Model);
this.joinTableModelClass.tableName = this.joinTable;
// We cannot know if the join table has a primary key. Therefore we set some
// known column as the idColumn so that inserts will work.
this.joinTableModelClass.idColumn = this.joinTableRelatedCol;
}
this.joinTableOwnerProp = this.joinTableModelClass.columnNameToPropertyName(this.joinTableOwnerCol);
this.joinTableRelatedProp = this.joinTableModelClass.columnNameToPropertyName(this.joinTableRelatedCol);
return retVal;
}
}
return builder.call(this.filter);
};
/**
* Reference to the column in the join table that refers to `fullOwnerCol()`.
*
* For example: `Person_Movie.actorId`.
*
* @returns {string}
*/
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
ManyToManyRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
}, {
key: 'fullJoinTableOwnerCol',
value: function fullJoinTableOwnerCol() {
return this.joinTable + '.' + this.joinTableOwnerCol;
}
var joinTable = this.joinTable;
var relatedTable = this.relatedModelClass.tableName;
/**
* Reference to the column in the join table that refers to `fullRelatedCol()`.
*
* For example: `Person_Movie.movieId`.
*
* @returns {string}
*/
var joinTableAlias = this.joinTableAlias();
var relatedTableAlias = this.relatedTableAlias();
}, {
key: 'fullJoinTableRelatedCol',
value: function fullJoinTableRelatedCol() {
return this.joinTable + '.' + this.joinTableRelatedCol;
}
return builder
[joinMethod](joinTable + ' as ' + joinTableAlias, joinTableAlias + '.' + this.joinTableOwnerCol, this.fullOwnerCol())
[joinMethod](relatedTable + ' as ' + relatedTableAlias, joinTableAlias + '.' + this.joinTableRelatedCol, relatedTableAlias + '.' + this.relatedCol)
.call(this.filter);
};
/**
* Alias to use for the join table when joining with the owner table.
*
* For example: `Person_Movie_rel_movies`.
*
* @returns {string}
*/
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.find = function (builder, owners) {
var self = this;
}, {
key: 'joinTableAlias',
value: function joinTableAlias() {
return this.joinTable + '_rel_' + this.name;
}
builder.onBuild(function (builder) {
var ownerIds = _.pluck(owners, self.ownerProp);
var ownerJoinColumn = self.fullJoinTableOwnerCol();
/**
* @inheritDoc
* @override
*/
if (!builder.has(/select/)) {
// If the user hasn't specified a select clause, select the related model's columns.
// If we don't do this we also get the join table's columns.
builder.select(self.relatedModelClass.tableName + '.*');
}, {
key: 'clone',
value: function clone() {
var relation = (0, _get3.default)((0, _getPrototypeOf2.default)(ManyToManyRelation.prototype), 'clone', this).call(this);
relation.joinTable = this.joinTable;
relation.joinTableOwnerCol = this.joinTableOwnerCol;
relation.joinTableOwnerProp = this.joinTableOwnerProp;
relation.joinTableRelatedCol = this.joinTableRelatedCol;
relation.joinTableRelatedProp = this.joinTableRelatedProp;
relation.joinTableModelClass = this.joinTableModelClass;
return relation;
}
self.findQuery(builder, ownerIds).select(ownerJoinColumn + ' as ' + ownerJoinColumnAlias);
});
/**
* @inheritDoc
* @override
*/
builder.runAfterModelCreate(function (related) {
// The ownerJoinColumnAlias column name may have been changed by the `$parseDatabaseJson`
// method of the related model class. We need to do the same conversion here.
var ownerJoinPropAlias = self.relatedModelClass.columnNameToPropertyName(ownerJoinColumnAlias);
var relatedByOwnerId = _.groupBy(related, ownerJoinPropAlias);
}, {
key: 'bindKnex',
value: function bindKnex(knex) {
var bound = (0, _get3.default)((0, _getPrototypeOf2.default)(ManyToManyRelation.prototype), 'bindKnex', this).call(this, knex);
_.each(owners, function (owner) {
owner[self.name] = relatedByOwnerId[owner[self.ownerProp]] || [];
});
bound.joinTableModelClass = this.joinTableModelClass.bindKnex(knex);
_.each(related, function (rel) {
delete rel[ownerJoinPropAlias];
});
return bound;
}
return related;
});
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.insert = function (builder, owner, insertion) {
var self = this;
}, {
key: 'findQuery',
value: function findQuery(builder, ownerCol, isColumnRef) {
builder.join(this.joinTable, this.fullJoinTableRelatedCol(), this.fullRelatedCol());
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
if (isColumnRef) {
builder.whereRef(this.fullJoinTableOwnerCol(), ownerCol);
} else {
if (_lodash2.default.isArray(ownerCol)) {
builder.whereIn(this.fullJoinTableOwnerCol(), ownerCol);
} else {
builder.where(this.fullJoinTableOwnerCol(), ownerCol);
}
}
builder.runAfterModelCreate(function (related) {
var ownerId = owner[self.ownerProp];
var relatedIds = _.pluck(related, self.relatedProp);
var joinRows = self._createJoinRows(ownerId, relatedIds);
return builder.call(this.filter);
}
owner[self.name] = self.mergeModels(owner[self.name], related);
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
// Insert the join rows to the join table.
return self.relatedModelClass
.knexQuery()
.insert(joinRows)
.into(self.joinTable)
.then(function () {
}, {
key: 'join',
value: function join(builder, joinMethod) {
joinMethod = joinMethod || 'join';
var joinTable = this.joinTable;
var relatedTable = this.relatedModelClass.tableName;
var joinTableAlias = this.joinTableAlias();
var relatedTableAlias = this.relatedTableAlias();
var joinTableAsAlias = joinTable + ' as ' + joinTableAlias;
var relatedTableAsAlias = relatedTable + ' as ' + relatedTableAlias;
var joinTableOwnerCol = joinTableAlias + '.' + this.joinTableOwnerCol;
var joinTableRelatedCol = joinTableAlias + '.' + this.joinTableRelatedCol;
var ownerCol = this.fullOwnerCol();
var relatedCol = relatedTableAlias + '.' + this.relatedCol;
return builder[joinMethod](joinTableAsAlias, joinTableOwnerCol, ownerCol)[joinMethod](relatedTableAsAlias, joinTableRelatedCol, relatedCol).call(this.filter);
}
/**
* @override
* @inheritDoc
*/
}, {
key: 'find',
value: function find(builder, owners) {
var _this2 = this;
builder.onBuild(function (builder) {
var ownerIds = _lodash2.default.pluck(owners, _this2.ownerProp);
var ownerJoinColumn = _this2.fullJoinTableOwnerCol();
if (!builder.has(/select/)) {
// If the user hasn't specified a select clause, select the related model's columns.
// If we don't do this we also get the join table's columns.
builder.select(_this2.relatedModelClass.tableName + '.*');
}
_this2.findQuery(builder, ownerIds).select(ownerJoinColumn + ' as ' + ownerJoinColumnAlias);
});
builder.runAfterModelCreate(function (related) {
// The ownerJoinColumnAlias column name may have been changed by the `$parseDatabaseJson`
// method of the related model class. We need to do the same conversion here.
var ownerJoinPropAlias = _this2.relatedModelClass.columnNameToPropertyName(ownerJoinColumnAlias);
var relatedByOwnerId = _lodash2.default.groupBy(related, ownerJoinPropAlias);
_lodash2.default.each(owners, function (owner) {
owner[_this2.name] = relatedByOwnerId[owner[_this2.ownerProp]] || [];
});
_lodash2.default.each(related, function (rel) {
delete rel[ownerJoinPropAlias];
});
return related;
});
});
};
}
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.update = function (builder, owner, update) {
var self = this;
/**
* @override
* @inheritDoc
*/
builder.onBuild(function (builder) {
var idSelectQuery = self._makeFindIdQuery(owner[self.ownerProp]);
}, {
key: 'insert',
value: function insert(builder, owner, insertion) {
var _this3 = this;
builder
.$$update(update)
.whereIn(self.relatedModelClass.getFullIdColumn(), idSelectQuery)
.call(self.filter);
});
};
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.patch = function (builder, owner, patch) {
return this.update(builder, owner, patch);
};
builder.runAfterModelCreate(function (related) {
var ownerId = owner[_this3.ownerProp];
var relatedIds = _lodash2.default.pluck(related, _this3.relatedProp);
var joinModels = _this3._createJoinModels(ownerId, relatedIds);
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.delete = function (builder, owner) {
var self = this;
owner[_this3.name] = _this3.mergeModels(owner[_this3.name], related);
builder.onBuild(function (builder) {
var idSelectQuery = self._makeFindIdQuery(owner[self.ownerProp]);
// Insert the join rows to the join table.
return _this3.joinTableModelClass.bindKnex(builder.modelClass().knex()).query().childQueryOf(builder).insert(joinModels).return(related);
});
}
builder
.$$delete()
.whereIn(self.relatedModelClass.getFullIdColumn(), idSelectQuery)
.call(self.filter);
});
};
/**
* @override
* @inheritDoc
*/
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.relate = function (builder, owner, ids) {
var self = this;
}, {
key: 'update',
value: function update(builder, owner, _update) {
var _this4 = this;
builder.onBuild(function (builder) {
var joinRows = self._createJoinRows(owner[self.ownerProp], ids);
// This is a bit weird: we make this query to the joinTable even though
// this query builder is bound to the related model class.
builder.$$insert(joinRows).into(self.joinTable);
});
};
builder.onBuild(function (builder) {
var idSelectQuery = _this4._makeFindIdQuery(builder, owner[_this4.ownerProp]);
/**
* @override
* @inheritDoc
*/
ManyToManyRelation.prototype.unrelate = function (builder, owner) {
var self = this;
builder.$$update(_update).whereIn(_this4.relatedModelClass.getFullIdColumn(), idSelectQuery).call(_this4.filter);
});
}
builder.onBuild(function (builder) {
var idSelectQuery = self.relatedModelClass
.query()
.copyFrom(builder, /where/i)
.select(self.fullRelatedCol())
.call(self.filter);
/**
* @override
* @inheritDoc
*/
// This is a bit weird: we make this query to the joinTable even though
// this query builder is bound to the related model class.
builder
.clear()
.$$delete()
.from(self.joinTable)
.where(self.fullJoinTableOwnerCol(), owner[self.ownerProp])
.whereIn(self.fullJoinTableRelatedCol(), idSelectQuery);
});
};
}, {
key: 'patch',
value: function patch(builder, owner, _patch) {
return this.update(builder, owner, _patch);
}
/**
* @private
*/
ManyToManyRelation.prototype._makeFindIdQuery = function (ownerId) {
return this.ownerModelClass
.knex()
.select(this.fullJoinTableRelatedCol())
.from(this.joinTable)
.where(this.fullJoinTableOwnerCol(), ownerId);
};
/**
* @override
* @inheritDoc
*/
/**
* @private
*/
ManyToManyRelation.prototype._createJoinRows = function (ownerId, relatedIds) {
var self = this;
}, {
key: 'delete',
value: function _delete(builder, owner) {
var _this5 = this;
return _.map(relatedIds, function (relatedId) {
var joinRow = {};
builder.onBuild(function (builder) {
var idSelectQuery = _this5._makeFindIdQuery(builder, owner[_this5.ownerProp]);
joinRow[self.joinTableOwnerCol] = ownerId;
joinRow[self.joinTableRelatedCol] = relatedId;
builder.$$delete().whereIn(_this5.relatedModelClass.getFullIdColumn(), idSelectQuery).call(_this5.filter);
});
}
return joinRow;
});
};
/**
* @override
* @inheritDoc
*/
module.exports = ManyToManyRelation;
}, {
key: 'relate',
value: function relate(builder, owner, ids) {
var _this6 = this;
builder.setQueryExecutor(function () {
var joinModels = _this6._createJoinModels(owner[_this6.ownerProp], ids);
return _this6.joinTableModelClass.bindKnex(_this6.ownerModelClass.knex()).query().childQueryOf(builder).insert(joinModels).runAfter(_lodash2.default.constant({}));
});
}
/**
* @override
* @inheritDoc
*/
}, {
key: 'unrelate',
value: function unrelate(builder, owner) {
var _this7 = this;
builder.setQueryExecutor(function (builder) {
var idSelectQuery = _this7.relatedModelClass.query().childQueryOf(builder).copyFrom(builder, /where/i).select(_this7.fullRelatedCol()).call(_this7.filter);
return _this7.joinTableModelClass.bindKnex(_this7.ownerModelClass.knex()).query().childQueryOf(builder).delete().where(_this7.fullJoinTableOwnerCol(), owner[_this7.ownerProp]).whereIn(_this7.fullJoinTableRelatedCol(), idSelectQuery).runAfter(_lodash2.default.constant({}));
});
}
/**
* @private
*/
}, {
key: '_makeFindIdQuery',
value: function _makeFindIdQuery(builder, ownerId) {
return this.joinTableModelClass.bindKnex(this.ownerModelClass.knex()).query().childQueryOf(builder).select(this.fullJoinTableRelatedCol()).where(this.fullJoinTableOwnerCol(), ownerId);
}
/**
* @private
*/
}, {
key: '_createJoinModels',
value: function _createJoinModels(ownerId, relatedIds) {
var _this8 = this;
return _lodash2.default.map(relatedIds, function (relatedId) {
var joinModel = {};
joinModel[_this8.joinTableOwnerProp] = ownerId;
joinModel[_this8.joinTableRelatedProp] = relatedId;
return joinModel;
});
}
}]);
return ManyToManyRelation;
})(_Relation3.default);
exports.default = ManyToManyRelation;
'use strict';
var _ = require('lodash')
, Relation = require('./Relation');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _Relation2 = require('./Relation');
var _Relation3 = _interopRequireDefault(_Relation2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @constructor
* @ignore
* @extends Relation
*/
function OneToManyRelation() {
Relation.apply(this, arguments);
}
Relation.extend(OneToManyRelation);
var OneToManyRelation = (function (_Relation) {
(0, _inherits3.default)(OneToManyRelation, _Relation);
/**
* @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);
}
function OneToManyRelation() {
(0, _classCallCheck3.default)(this, OneToManyRelation);
return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(OneToManyRelation).apply(this, arguments));
}
return builder.call(this.filter);
};
(0, _createClass3.default)(OneToManyRelation, [{
key: 'findQuery',
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToManyRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
value: function findQuery(builder, ownerCol, isColumnRef) {
if (isColumnRef) {
builder.whereRef(this.fullRelatedCol(), ownerCol);
} else {
if (_lodash2.default.isArray(ownerCol)) {
builder.whereIn(this.fullRelatedCol(), ownerCol);
} else {
builder.where(this.fullRelatedCol(), ownerCol);
}
}
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
return builder.call(this.filter);
}
return builder
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol())
.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.find = function (builder, owners) {
var self = this;
var ownerIds = _.unique(_.pluck(owners, this.ownerProp));
}, {
key: 'join',
value: function join(builder, joinMethod) {
joinMethod = joinMethod || 'join';
builder.onBuild(function (builder) {
self.findQuery(builder, ownerIds);
});
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
builder.runAfterModelCreate(function (related) {
var relatedByOwnerId = _.groupBy(related, self.relatedProp);
var relatedTableAsAlias = relatedTable + ' as ' + relatedTableAlias;
var relatedCol = relatedTableAlias + '.' + this.relatedCol;
_.each(owners, function (owner) {
owner[self.name] = relatedByOwnerId[owner[self.ownerProp]] || [];
});
return builder[joinMethod](relatedTableAsAlias, relatedCol, this.fullOwnerCol()).call(this.filter);
}
}, {
key: 'find',
return related;
});
};
/**
* @override
* @inheritDoc
*/
value: function find(builder, owners) {
var _this2 = this;
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.insert = function (builder, owner, insertion) {
var self = this;
var ownerIds = _lodash2.default.unique(_lodash2.default.pluck(owners, this.ownerProp));
_.each(insertion.models(), function (insert) {
insert[self.relatedProp] = owner[self.ownerProp];
});
builder.onBuild(function (builder) {
_this2.findQuery(builder, ownerIds);
});
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
builder.runAfterModelCreate(function (related) {
var relatedByOwnerId = _lodash2.default.groupBy(related, _this2.relatedProp);
builder.runAfterModelCreate(function (related) {
owner[self.name] = self.mergeModels(owner[self.name], related);
return related;
});
};
_lodash2.default.each(owners, function (owner) {
owner[_this2.name] = relatedByOwnerId[owner[_this2.ownerProp]] || [];
});
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.update = function (builder, owner, update) {
var self = this;
return related;
});
}
builder.onBuild(function (builder) {
self.findQuery(builder, owner[self.ownerProp]);
builder.$$update(update);
});
};
/**
* @override
* @inheritDoc
*/
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.patch = function (builder, owner, patch) {
return this.update(builder, owner, patch);
};
}, {
key: 'insert',
value: function insert(builder, owner, insertion) {
var _this3 = this;
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.delete = function (builder, owner) {
var self = this;
_lodash2.default.each(insertion.models(), function (insert) {
insert[_this3.relatedProp] = owner[_this3.ownerProp];
});
builder.onBuild(function (builder) {
self.findQuery(builder, owner[self.ownerProp]);
builder.$$delete();
});
};
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.relate = function (builder, owner, ids) {
var self = this;
builder.runAfterModelCreate(function (related) {
owner[_this3.name] = _this3.mergeModels(owner[_this3.name], related);
return related;
});
}
builder.onBuild(function (builder) {
var patch = relatePatch(self, owner[self.ownerProp]);
/**
* @override
* @inheritDoc
*/
// We build the input query, but we never actually execute it. This query is not executed,
// because it would not invoke the $beforeUpdate and $afterUpdate hooks. We build it so that
// toSql(), toString() etc. return the correct string.
builder
.$$update(patch)
.whereIn(self.relatedModelClass.getFullIdColumn(), ids);
});
}, {
key: 'update',
value: function update(builder, owner, _update) {
var _this4 = this;
// Set a custom executor that executes a patch query.
builder.setQueryExecutor(function () {
var patch = relatePatch(self, owner[self.ownerProp]);
builder.onBuild(function (builder) {
_this4.findQuery(builder, owner[_this4.ownerProp]);
builder.$$update(_update);
});
}
return self.relatedModelClass
.query()
.patch(patch)
.whereIn(self.relatedModelClass.getFullIdColumn(), ids)
.return({});
});
};
/**
* @override
* @inheritDoc
*/
/**
* @override
* @inheritDoc
*/
OneToManyRelation.prototype.unrelate = function (builder, owner) {
var self = this;
}, {
key: 'patch',
value: function patch(builder, owner, _patch) {
return this.update(builder, owner, _patch);
}
builder.onBuild(function (builder) {
var patch = relatePatch(self, null);
/**
* @override
* @inheritDoc
*/
// We build the input query, but we never actually execute it. This query is not executed,
// because it would not invoke the $beforeUpdate and $afterUpdate hooks. We build it so that
// toSql(), toString() etc. return the correct string.
builder
.$$update(patch)
.where(self.fullRelatedCol(), owner[self.ownerProp])
.call(self.filter);
});
}, {
key: 'delete',
value: function _delete(builder, owner) {
var _this5 = this;
// Set a custom executor that executes a patch query.
builder.setQueryExecutor(function (builder) {
var patch = relatePatch(self, null);
builder.onBuild(function (builder) {
_this5.findQuery(builder, owner[_this5.ownerProp]);
builder.$$delete();
});
}
return self.relatedModelClass
.query()
.patch(patch)
.copyFrom(builder, /where/i)
.return({});
});
};
/**
* @override
* @inheritDoc
*/
}, {
key: 'relate',
value: function relate(builder, owner, ids) {
var _this6 = this;
builder.setQueryExecutor(function () {
var patch = relatePatch(_this6, owner[_this6.ownerProp]);
return _this6.relatedModelClass.query().childQueryOf(builder).patch(patch).copyFrom(builder, /where/i).whereIn(_this6.relatedModelClass.getFullIdColumn(), ids).call(_this6.filter).runAfter(_lodash2.default.constant({}));
});
}
/**
* @override
* @inheritDoc
*/
}, {
key: 'unrelate',
value: function unrelate(builder, owner) {
var _this7 = this;
builder.setQueryExecutor(function (builder) {
var patch = relatePatch(_this7, null);
return _this7.relatedModelClass.query().childQueryOf(builder).patch(patch).copyFrom(builder, /where/i).where(_this7.fullRelatedCol(), owner[_this7.ownerProp]).call(_this7.filter).runAfter(_lodash2.default.constant({}));
});
}
}]);
return OneToManyRelation;
})(_Relation3.default);
/**
* @private
*/
exports.default = OneToManyRelation;
function relatePatch(relation, value) {

@@ -198,4 +232,2 @@ var patch = {};

return patch;
}
module.exports = OneToManyRelation;
}
'use strict';
var _ = require('lodash')
, Promise = require('bluebird')
, Relation = require('./Relation');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _Relation2 = require('./Relation');
var _Relation3 = _interopRequireDefault(_Relation2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @constructor
* @ignore
* @extends Relation
*/
function OneToOneRelation() {
Relation.apply(this, arguments);
}
Relation.extend(OneToOneRelation);
var OneToOneRelation = (function (_Relation) {
(0, _inherits3.default)(OneToOneRelation, _Relation);
/**
* @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(), _.compact(ownerCol));
} else {
builder.where(this.fullRelatedCol(), ownerCol)
}
function OneToOneRelation() {
(0, _classCallCheck3.default)(this, OneToOneRelation);
return (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(OneToOneRelation).apply(this, arguments));
}
return builder.call(this.filter);
};
(0, _createClass3.default)(OneToOneRelation, [{
key: 'findQuery',
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
OneToOneRelation.prototype.join = function (builder, joinMethod) {
joinMethod = joinMethod || 'join';
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
value: function findQuery(builder, ownerCol, isColumnRef) {
if (isColumnRef) {
builder.whereRef(this.fullRelatedCol(), ownerCol);
} else {
if (_lodash2.default.isArray(ownerCol)) {
builder.whereIn(this.fullRelatedCol(), _lodash2.default.compact(ownerCol));
} else {
builder.where(this.fullRelatedCol(), ownerCol);
}
}
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
return builder.call(this.filter);
}
return builder
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol())
.call(this.filter);
};
/**
* @override
* @inheritDoc
* @returns {QueryBuilder}
*/
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.find = function (builder, owners) {
var self = this;
}, {
key: 'join',
value: function join(builder, joinMethod) {
joinMethod = joinMethod || 'join';
builder.onBuild(function (builder) {
var relatedIds = _.unique(_.compact(_.pluck(owners, self.ownerProp)));
self._makeFindQuery(builder, relatedIds);
});
var relatedTable = this.relatedModelClass.tableName;
var relatedTableAlias = this.relatedTableAlias();
builder.runAfterModelCreate(function (related) {
var relatedById = _.indexBy(related, self.relatedProp);
var relatedTableAsAlias = relatedTable + ' as ' + relatedTableAlias;
var relatedCol = relatedTableAlias + '.' + this.relatedCol;
_.each(owners, function (owner) {
owner[self.name] = relatedById[owner[self.ownerProp]] || null;
});
return builder[joinMethod](relatedTableAsAlias, relatedCol, this.fullOwnerCol()).call(this.filter);
}
return related;
});
};
/**
* @override
* @inheritDoc
*/
/**
* Person
* .query()
* .update({
* age: Person.query().avg('age'),
*
* })
*/
}, {
key: 'find',
value: function find(builder, owners) {
var _this2 = this;
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.insert = function (builder, owner, insertion) {
var self = this;
builder.onBuild(function (builder) {
var relatedIds = _lodash2.default.unique(_lodash2.default.compact(_lodash2.default.pluck(owners, _this2.ownerProp)));
_this2._makeFindQuery(builder, relatedIds);
});
if (insertion.models().length > 1) {
throw new Error('can only insert one model to a OneToOneRelation');
}
builder.runAfterModelCreate(function (related) {
var relatedById = _lodash2.default.indexBy(related, _this2.relatedProp);
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
_lodash2.default.each(owners, function (owner) {
owner[_this2.name] = relatedById[owner[_this2.ownerProp]] || null;
});
builder.runAfterModelCreate(function (inserted) {
owner[self.ownerProp] = inserted[0][self.relatedProp];
owner[self.name] = inserted[0];
return related;
});
}
var patch = {};
patch[self.ownerProp] = inserted[0][self.relatedProp];
/**
* @override
* @inheritDoc
*/
return self.ownerModelClass
.query()
.patch(patch)
.where(self.ownerModelClass.getFullIdColumn(), owner.$id())
.return(inserted);
});
};
}, {
key: 'insert',
value: function insert(builder, owner, insertion) {
var _this3 = this;
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.update = function (builder, owner, update) {
var self = this;
if (insertion.models().length > 1) {
throw new Error('can only insert one model to a OneToOneRelation');
}
builder.onBuild(function (builder) {
self._makeFindQuery(builder, owner[self.ownerProp]);
builder.$$update(update);
});
};
builder.onBuild(function (builder) {
builder.$$insert(insertion);
});
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.patch = function (builder, owner, patch) {
return this.update(builder, owner, patch);
};
builder.runAfterModelCreate(function (inserted) {
owner[_this3.ownerProp] = inserted[0][_this3.relatedProp];
owner[_this3.name] = inserted[0];
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.delete = function (builder, owner) {
var self = this;
var patch = {};
patch[_this3.ownerProp] = inserted[0][_this3.relatedProp];
builder.onBuild(function (builder) {
self._makeFindQuery(builder, owner[self.ownerProp]);
builder.$$delete();
});
};
return _this3.ownerModelClass.query().childQueryOf(builder).patch(patch).where(_this3.ownerModelClass.getFullIdColumn(), owner.$id()).return(inserted);
});
}
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.relate = function (builder, owner, ids) {
var self = this;
/**
* @override
* @inheritDoc
*/
if (ids.length > 1) {
throw new Error('can only relate one model to a OneToOneRelation');
}
}, {
key: 'update',
value: function update(builder, owner, _update) {
var _this4 = this;
builder.onBuild(function (builder) {
var patch = {};
patch[self.ownerProp] = ids[0];
owner[self.ownerProp] = ids[0];
builder.onBuild(function (builder) {
_this4._makeFindQuery(builder, owner[_this4.ownerProp]);
builder.$$update(_update);
});
}
// We build the input query, but we never actually execute it. This query is not executed,
// because it would not invoke the $beforeUpdate and $afterUpdate hooks. We build it so that
// toSql(), toString() etc. return the correct string.
builder
.$$update(patch)
.from(self.ownerModelClass.tableName)
.where(self.ownerModelClass.getFullIdColumn(), owner.$id())
.call(self.filter);
});
/**
* @override
* @inheritDoc
*/
// Set a custom executor that executes a patch query.
builder.setQueryExecutor(function (builder) {
var patch = {};
patch[self.ownerProp] = ids[0];
}, {
key: 'patch',
value: function patch(builder, owner, _patch) {
return this.update(builder, owner, _patch);
}
return self.ownerModelClass
.query()
.patch(patch)
.copyFrom(builder, /where/i)
.return({});
});
};
/**
* @override
* @inheritDoc
*/
/**
* @override
* @inheritDoc
*/
OneToOneRelation.prototype.unrelate = function (builder, owner) {
var self = this;
}, {
key: 'delete',
value: function _delete(builder, owner) {
var _this5 = this;
builder.onBuild(function (builder) {
var patch = {};
patch[self.ownerProp] = null;
owner[self.ownerProp] = null;
builder.onBuild(function (builder) {
_this5._makeFindQuery(builder, owner[_this5.ownerProp]);
builder.$$delete();
});
}
// We build the input query, but we never actually execute it. This query is not executed,
// because it would not invoke the $beforeUpdate and $afterUpdate hooks. We build it so that
// toSql(), toString() etc. return the correct string.
builder
.$$update(patch)
.from(self.ownerModelClass.tableName)
.where(self.ownerModelClass.getFullIdColumn(), owner.$id())
.call(self.filter);
});
/**
* @override
* @inheritDoc
*/
// Set a custom executor that executes a patch query.
builder.setQueryExecutor(function (builder) {
var patch = {};
patch[self.ownerProp] = null;
}, {
key: 'relate',
value: function relate(builder, owner, ids) {
var _this6 = this;
return self.ownerModelClass
.query()
.patch(patch)
.copyFrom(builder, /where/i)
.return({});
});
};
if (ids.length > 1) {
throw new Error('can only relate one model to a OneToOneRelation');
}
/**
* @private
*/
OneToOneRelation.prototype._makeFindQuery = function (builder, relatedIds) {
if ((_.isArray(relatedIds) && _.isEmpty(relatedIds)) || !relatedIds) {
return builder.setQueryExecutor(_.constant(Promise.resolve([])));
} else {
return this.findQuery(builder, relatedIds);
}
};
builder.setQueryExecutor(function (builder) {
var patch = {};
module.exports = OneToOneRelation;
patch[_this6.ownerProp] = ids[0];
owner[_this6.ownerProp] = ids[0];
return _this6.ownerModelClass.query().childQueryOf(builder).patch(patch).copyFrom(builder, /where/i).where(_this6.ownerModelClass.getFullIdColumn(), owner.$id()).runAfterModelCreate(_lodash2.default.constant({}));
});
}
/**
* @override
* @inheritDoc
*/
}, {
key: 'unrelate',
value: function unrelate(builder, owner) {
var _this7 = this;
builder.setQueryExecutor(function (builder) {
var patch = {};
patch[_this7.ownerProp] = null;
owner[_this7.ownerProp] = null;
return _this7.ownerModelClass.query().childQueryOf(builder).patch(patch).copyFrom(builder, /where/i).where(_this7.ownerModelClass.getFullIdColumn(), owner.$id()).runAfterModelCreate(_lodash2.default.constant({}));
});
}
/**
* @private
*/
}, {
key: '_makeFindQuery',
value: function _makeFindQuery(builder, relatedIds) {
if (_lodash2.default.isArray(relatedIds) && _lodash2.default.isEmpty(relatedIds) || !relatedIds) {
return builder.resolve([]);
} else {
return this.findQuery(builder, relatedIds);
}
}
}]);
return OneToOneRelation;
})(_Relation3.default);
exports.default = OneToOneRelation;
'use strict';
var _ = require('lodash')
, utils = require('../utils')
, QueryBuilder = require('../queryBuilder/QueryBuilder');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _utils = require('../utils');
var _utils2 = _interopRequireDefault(_utils);
var _QueryBuilder = require('../queryBuilder/QueryBuilder');
var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**

@@ -32,3 +59,3 @@ * @typedef {Object} RelationJoin

*
* @property {String} from
* @property {string} from
* The relation column in the owner table. Must be given with the table name.

@@ -38,3 +65,3 @@ * For example `Person.id`. Note that neither this nor `to` need to be foreign

*
* @property {String} to
* @property {string} to
* The relation column in the related table. Must be given with the table name.

@@ -47,7 +74,11 @@ * For example `Movie.id`. Note that neither this nor `from` need to be foreign

*
* @property {String} through.from
* @property {Model} through.modelClass
* If the there is model class available for the join table, it can be provided
* using this property.
*
* @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
* @property {string} through.to
* The column that is joined to `to` property of the `RelationJoin`. For example

@@ -60,3 +91,3 @@ * `Person_Movie.movieId` where `Person_Movie` is the join table.

*
* @property {Model|String} modelClass
* @property {Model|string} modelClass
* A {@link Model} subclass constructor or an absolute path to a module that exports one.

@@ -81,3 +112,3 @@ *

*
* @param {String} relationName
* @param {string} relationName
* Name of the relation.

@@ -90,483 +121,456 @@ *

* @abstract
* @constructor
*/
function Relation(relationName, OwnerClass) {
/**
* Name of the relation.
*
* @type {String}
*/
this.name = relationName;
/**
* The owner class of this relation.
*
* This must be a subclass of Model.
*
* @type {Model}
*/
this.ownerModelClass = OwnerClass;
var Relation = (function () {
function Relation(relationName, OwnerClass) {
(0, _classCallCheck3.default)(this, Relation);
/**
* The related class.
*
* This must be a subclass of Model.
*
* @type {Model}
*/
this.relatedModelClass = null;
/**
* Name of the relation.
*
* @type {string}
*/
this.name = relationName;
/**
* The relation column in the owner table.
*
* @type {String}
*/
this.ownerCol = null;
/**
* The owner class of this relation.
*
* This must be a subclass of Model.
*
* @type {Class<Model>}
*/
this.ownerModelClass = OwnerClass;
/**
* The relation property in the owner model.
*
* @type {String}
*/
this.ownerProp = null;
/**
* The related class.
*
* This must be a subclass of Model.
*
* @type {Class<Model>}
*/
this.relatedModelClass = null;
/**
* The relation column in the related table.
*
* @type {String}
*/
this.relatedCol = null;
/**
* The relation column in the owner table.
*
* @type {string}
*/
this.ownerCol = null;
/**
* The relation property in the related model.
*
* @type {String}
*/
this.relatedProp = null;
/**
* The relation property in the owner model.
*
* @type {string}
*/
this.ownerProp = null;
/**
* The join table.
*
* @type {String}
*/
this.joinTable = null;
/**
* The relation column in the related table.
*
* @type {string}
*/
this.relatedCol = null;
/**
* The relation column in the join table that points to the owner table.
*
* @type {String}
*/
this.joinTableOwnerCol = null;
/**
* The relation property in the related model.
*
* @type {string}
*/
this.relatedProp = null;
/**
* The relation column in the join table that points to the related table.
*
* @type {String}
*/
this.joinTableRelatedCol = null;
/**
* Optional additional filter query.
*
* @type {function (QueryBuilder)}
*/
this.filter = null;
}
/**
* Optional additional filter query.
* Makes the given constructor a subclass of this class.
*
* @type {function (QueryBuilder)}
* @param {function=} subclassConstructor
* @return {function}
*/
this.filter = null;
}
/**
* Makes the given constructor a subclass of this class.
*
* @param {function=} subclassConstructor
* @return {function}
*/
Relation.extend = function (subclassConstructor) {
utils.inherits(subclassConstructor, this);
return subclassConstructor;
};
(0, _createClass3.default)(Relation, [{
key: 'setMapping',
/**
* Constructs the instance based on a mapping data.
*
* @param {RelationMapping} mapping
*/
Relation.prototype.setMapping = function (mapping) {
// Avoid require loop and import here.
var Model = require(__dirname + '/../model/Model');
/**
* Constructs the instance based on a mapping data.
*
* @param {RelationMapping} mapping
*/
value: function setMapping(mapping) {
// Avoid require loop and import here.
var Model = require(__dirname + '/../model/Model').default;
if (!utils.isSubclassOf(this.ownerModelClass, Model)) {
throw new Error('Relation\'s owner is not a subclass of Model');
}
if (!_utils2.default.isSubclassOf(this.ownerModelClass, Model)) {
throw new Error('Relation\'s owner is not a subclass of Model');
}
var errorPrefix = this.ownerModelClass.name + '.relationMappings.' + this.name;
var errorPrefix = this.ownerModelClass.name + '.relationMappings.' + this.name;
if (!mapping.modelClass) {
throw new Error(errorPrefix + '.modelClass is not defined');
}
if (!mapping.modelClass) {
throw new Error(errorPrefix + '.modelClass is not defined');
}
if (_.isString(mapping.modelClass)) {
try {
// babel 6 style of exposing es6 exports to commonjs https://github.com/babel/babel/issues/2683
var relatedModelClassModule = require(mapping.modelClass);
this.relatedModelClass = utils.isSubclassOf(relatedModelClassModule.default, Model) ?
relatedModelClassModule.default : relatedModelClassModule;
} catch (err) {
throw new Error(errorPrefix + '.modelClass is an invalid file path to a model class.');
}
if (_lodash2.default.isString(mapping.modelClass)) {
try {
// babel 6 style of exposing es6 exports to commonjs https://github.com/babel/babel/issues/2683
var relatedModelClassModule = require(mapping.modelClass);
this.relatedModelClass = _utils2.default.isSubclassOf(relatedModelClassModule.default, Model) ? relatedModelClassModule.default : relatedModelClassModule;
} catch (err) {
throw new Error(errorPrefix + '.modelClass is an invalid file path to a model class.');
}
if (!utils.isSubclassOf(this.relatedModelClass, Model)) {
throw new Error(errorPrefix + '.modelClass is a valid path to a module, but the module doesn\'t export a Model subclass.');
}
} else {
this.relatedModelClass = mapping.modelClass;
if (!_utils2.default.isSubclassOf(this.relatedModelClass, Model)) {
throw new Error(errorPrefix + '.modelClass is a valid path to a module, but the module doesn\'t export a Model subclass.');
}
} else {
this.relatedModelClass = mapping.modelClass;
if (!utils.isSubclassOf(this.relatedModelClass, Model)) {
throw new Error(errorPrefix + '.modelClass is not a subclass of Model or a file path to a module that exports one.');
}
}
if (!_utils2.default.isSubclassOf(this.relatedModelClass, Model)) {
throw new Error(errorPrefix + '.modelClass is not a subclass of Model or a file path to a module that exports one.');
}
}
if (!mapping.relation) {
throw new Error(errorPrefix + '.relation is not defined');
}
if (!mapping.relation) {
throw new Error(errorPrefix + '.relation is not defined');
}
if (!utils.isSubclassOf(mapping.relation, Relation)) {
throw new Error(errorPrefix + '.relation is not a subclass of Relation');
}
if (!_utils2.default.isSubclassOf(mapping.relation, Relation)) {
throw new Error(errorPrefix + '.relation is not a subclass of Relation');
}
if (!mapping.join || !_.isString(mapping.join.from) || !_.isString(mapping.join.to)) {
throw new Error(errorPrefix + '.join must be an object that maps the columns of the related models together. For example: {from: \'SomeTable.id\', to: \'SomeOtherTable.someModelId\'}');
}
if (!mapping.join || !_lodash2.default.isString(mapping.join.from) || !_lodash2.default.isString(mapping.join.to)) {
throw new Error(errorPrefix + '.join must be an object that maps the columns of the related models together. For example: {from: \'SomeTable.id\', to: \'SomeOtherTable.someModelId\'}');
}
var joinOwner = null;
var joinRelated = null;
var joinOwner = null;
var joinRelated = null;
var joinFrom = parseColumn(mapping.join.from);
var joinTo = parseColumn(mapping.join.to);
var joinFrom = Relation.parseColumn(mapping.join.from);
var joinTo = Relation.parseColumn(mapping.join.to);
if (!joinFrom.table || !joinFrom.name) {
throw new Error(errorPrefix + '.join.from must have format TableName.columnName. For example `SomeTable.id`.');
}
if (!joinFrom.table || !joinFrom.name) {
throw new Error(errorPrefix + '.join.from must have format TableName.columnName. For example `SomeTable.id`.');
}
if (!joinTo.table || !joinTo.name) {
throw new Error(errorPrefix + '.join.to must have format TableName.columnName. For example `SomeTable.id`.');
}
if (!joinTo.table || !joinTo.name) {
throw new Error(errorPrefix + '.join.to must have format TableName.columnName. For example `SomeTable.id`.');
}
if (joinFrom.table === this.ownerModelClass.tableName) {
joinOwner = joinFrom;
joinRelated = joinTo;
} else if (joinTo.table === this.ownerModelClass.tableName) {
joinOwner = joinTo;
joinRelated = joinFrom;
} else {
throw new Error(errorPrefix + '.join: either `from` or `to` must point to the owner model table.');
}
if (joinFrom.table === this.ownerModelClass.tableName) {
joinOwner = joinFrom;
joinRelated = joinTo;
} else if (joinTo.table === this.ownerModelClass.tableName) {
joinOwner = joinTo;
joinRelated = joinFrom;
} else {
throw new Error(errorPrefix + '.join: either `from` or `to` must point to the owner model table.');
}
if (joinRelated.table !== this.relatedModelClass.tableName) {
throw new Error(errorPrefix + '.join: either `from` or `to` must point to the related model table.');
}
if (joinRelated.table !== this.relatedModelClass.tableName) {
throw new Error(errorPrefix + '.join: either `from` or `to` must point to the related model table.');
}
if (mapping.join.through) {
if (!_.isString(mapping.join.through.from) || !_.isString(mapping.join.through.to)) {
throw new Error(errorPrefix + '.join.through must be an object that describes the join table. For example: {from: \'JoinTable.someId\', to: \'JoinTable.someOtherId\'}');
this.ownerProp = this._propertyName(joinOwner, this.ownerModelClass);
this.ownerCol = joinOwner.name;
this.relatedProp = this._propertyName(joinRelated, this.relatedModelClass);
this.relatedCol = joinRelated.name;
this.filter = Relation.parseFilter(mapping);
}
var joinTableFrom = parseColumn(mapping.join.through.from);
var joinTableTo = parseColumn(mapping.join.through.to);
/**
* Reference to the relation column in the owner model's table.
*
* For example: `Person.id`.
*
* @returns {string}
*/
if (!joinTableFrom.table || !joinTableFrom.name) {
throw new Error(errorPrefix + '.join.through.from must have format JoinTable.columnName. For example `JoinTable.someId`.');
}, {
key: 'fullOwnerCol',
value: function fullOwnerCol() {
return this.ownerModelClass.tableName + '.' + this.ownerCol;
}
if (!joinTableTo.table || !joinTableTo.name) {
throw new Error(errorPrefix + '.join.through.to must have format JoinTable.columnName. For example `JoinTable.someId`.');
/**
* Reference to the relation column in the related model's table.
*
* For example: `Movie.id`.
*
* @returns {string}
*/
}, {
key: 'fullRelatedCol',
value: function fullRelatedCol() {
return this.relatedModelClass.tableName + '.' + this.relatedCol;
}
if (joinTableFrom.table !== joinTableTo.table) {
throw new Error(errorPrefix + '.join.through `from` and `to` must point to the same join table.');
/**
* Alias to use for the related table when joining with the owner table.
*
* For example: `Movie_rel_movies`.
*
* @returns {string}
*/
}, {
key: 'relatedTableAlias',
value: function relatedTableAlias() {
return this.relatedModelClass.tableName + '_rel_' + this.name;
}
this.joinTable = joinTableFrom.table;
/**
* Clones this relation.
*
* @returns {Relation}
*/
if (joinFrom.table === this.ownerModelClass.tableName) {
this.joinTableOwnerCol = joinTableFrom.name;
this.joinTableRelatedCol = joinTableTo.name;
} else {
this.joinTableRelatedCol = joinTableFrom.name;
this.joinTableOwnerCol = joinTableTo.name;
}, {
key: 'clone',
value: function clone() {
var relation = new this.constructor(this.name, this.ownerModelClass);
relation.relatedModelClass = this.relatedModelClass;
relation.ownerCol = this.ownerCol;
relation.ownerProp = this.ownerProp;
relation.relatedCol = this.relatedCol;
relation.relatedProp = this.relatedProp;
relation.filter = this.filter;
return relation;
}
}
this.ownerProp = this._propertyName(joinOwner, this.ownerModelClass);
this.ownerCol = joinOwner.name;
this.relatedProp = this._propertyName(joinRelated, this.relatedModelClass);
this.relatedCol = joinRelated.name;
this.filter = parseFilter(mapping);
};
/**
* Returns a clone of this relation with `relatedModelClass` and `ownerModelClass` bound to the given knex.
*
* See `Model.bindKnex`.
*
* @param knex
* @returns {Relation}
*/
/**
* Reference to the relation column in the owner model's table.
*
* For example: `Person.id`.
*
* @returns {string}
*/
Relation.prototype.fullOwnerCol = function () {
return this.ownerModelClass.tableName + '.' + this.ownerCol;
};
}, {
key: 'bindKnex',
value: function bindKnex(knex) {
var bound = this.clone();
/**
* Reference to the relation column in the related model's table.
*
* For example: `Movie.id`.
*
* @returns {string}
*/
Relation.prototype.fullRelatedCol = function () {
return this.relatedModelClass.tableName + '.' + this.relatedCol;
};
bound.relatedModelClass = this.relatedModelClass.bindKnex(knex);
bound.ownerModelClass = this.ownerModelClass.bindKnex(knex);
/**
* Reference to the column in the join table that refers to `fullOwnerCol()`.
*
* For example: `Person_Movie.actorId`.
*
* @returns {string}
*/
Relation.prototype.fullJoinTableOwnerCol = function () {
return this.joinTable + '.' + this.joinTableOwnerCol;
};
return bound;
}
/**
* Reference to the column in the join table that refers to `fullRelatedCol()`.
*
* For example: `Person_Movie.movieId`.
*
* @returns {string}
*/
Relation.prototype.fullJoinTableRelatedCol = function () {
return this.joinTable + '.' + this.joinTableRelatedCol;
};
/**
* @protected
* @param {Array.<Model>} models1
* @param {Array.<Model>} models2
* @returns {Array.<Model>}
*/
/**
* 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;
};
}, {
key: 'mergeModels',
value: function mergeModels(models1, models2) {
models1 = _lodash2.default.compact(models1);
models2 = _lodash2.default.compact(models2);
var modelsById = (0, _create2.default)(null);
/**
* 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;
};
_lodash2.default.forEach(models1, function (model) {
modelsById[model.$id()] = model;
});
/**
* Clones this relation.
*
* @returns {Relation}
*/
Relation.prototype.clone = function () {
var clone = new this.constructor(this.name, this.ownerModelClass);
_lodash2.default.forEach(models2, function (model) {
modelsById[model.$id()] = model;
});
clone.relatedModelClass = this.relatedModelClass;
clone.ownerCol = this.ownerCol;
clone.ownerProp = this.ownerProp;
clone.relatedCol = this.relatedCol;
clone.relatedProp = this.relatedProp;
clone.joinTable = this.joinTable;
clone.joinTableOwnerCol = this.joinTableOwnerCol;
clone.joinTableRelatedCol = this.joinTableRelatedCol;
clone.filter = this.filter;
return _lodash2.default.sortBy(_lodash2.default.values(modelsById), function (model) {
return model.$id();
});
}
return clone;
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {number|string} ownerCol
* @param {boolean} isColumnRef
* @returns {QueryBuilder}
*/
/**
* 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) {
var bound = this.clone();
}, {
key: 'findQuery',
value: function findQuery(builder, ownerCol, isColumnRef) {
throw new Error('not implemented');
}
bound.relatedModelClass = bound.relatedModelClass.bindKnex(knex);
bound.ownerModelClass = bound.ownerModelClass.bindKnex(knex);
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {string} joinMethod
* @returns {QueryBuilder}
*/
return bound;
};
}, {
key: 'join',
value: function join(builder, joinMethod) {
throw new Error('not implemented');
}
/**
* @protected
* @param {Array.<Model>} models1
* @param {Array.<Model>} models2
* @returns {Array.<Model>}
*/
Relation.prototype.mergeModels = function (models1, models2) {
models1 = _.compact(models1);
models2 = _.compact(models2);
var modelsById = Object.create(null);
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object|Array.<Model>|Array.<Object>} owners
*/
_.each(models1, function (model) {
modelsById[model.$id()] = model;
});
}, {
key: 'find',
value: function find(builder, owners) {
throw new Error('not implemented');
}
_.each(models2, function (model) {
modelsById[model.$id()] = model;
});
/* istanbul ignore next */
/**
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {InsertionOrUpdate} insertion
*/
return _.sortBy(_.values(modelsById), function (model) {
return model.$id();
});
};
}, {
key: 'insert',
value: function insert(builder, owner, insertion) {
throw new Error('not implemented');
}
/* 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} owner
* @param {InsertionOrUpdate} update
*/
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {string} joinMethod
* @returns {QueryBuilder}
*/
Relation.prototype.join = function (builder, joinMethod) {
throw new Error('not implemented');
};
}, {
key: 'update',
value: function update(builder, owner, _update) {
return builder;
}
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object|Array.<Model>|Array.<Object>} owners
*/
Relation.prototype.find = function (builder, owners) {
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {InsertionOrUpdate} patch
*/
/* istanbul ignore next */
/**
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {InsertionOrUpdate} insertion
*/
Relation.prototype.insert = function (builder, owner, insertion) {
throw new Error('not implemented');
};
}, {
key: 'patch',
value: function patch(builder, owner, _patch) {
throw new Error('not implemented');
}
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {InsertionOrUpdate} update
*/
Relation.prototype.update = function (builder, owner, update) {
return builder;
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
*/
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {InsertionOrUpdate} patch
*/
Relation.prototype.patch = function (builder, owner, patch) {
throw new Error('not implemented');
};
}, {
key: 'delete',
value: function _delete(builder, owner) {
throw new Error('not implemented');
}
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
*/
Relation.prototype.delete = function (builder, owner) {
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {number|string|Array.<number>|Array.<string>} ids
*/
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
* @param {number|string|Array.<number>|Array.<string>} ids
*/
Relation.prototype.relate = function (builder, owner, ids) {
throw new Error('not implemented');
};
}, {
key: 'relate',
value: function relate(builder, owner, ids) {
throw new Error('not implemented');
}
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
*/
Relation.prototype.unrelate = function (builder, owner) {
throw new Error('not implemented');
};
/* istanbul ignore next */
/**
* @abstract
* @param {QueryBuilder} builder
* @param {Model|Object} owner
*/
/**
* @private
*/
Relation.prototype._propertyName = function (column, modelClass) {
var propertyName = modelClass.columnNameToPropertyName(column.name);
}, {
key: 'unrelate',
value: function unrelate(builder, owner) {
throw new Error('not implemented');
}
if (!propertyName) {
throw new Error(modelClass.name +
'.$parseDatabaseJson probably transforms the value of the column ' + column.name + '.' +
' This is a no-no because ' + column.name +
' is needed in the relation ' + this.ownerModelClass.tableName + '.' + this.name);
}
/**
* @private
*/
return propertyName;
};
}, {
key: '_propertyName',
value: function _propertyName(column, modelClass) {
var propertyName = modelClass.columnNameToPropertyName(column.name);
/**
* @private
*/
function parseFilter(mapping) {
if (_.isFunction(mapping.filter)) {
return mapping.filter;
} else if (_.isObject(mapping.filter)) {
return function (queryBuilder) {
queryBuilder.where(mapping.filter);
};
} else {
return _.noop;
}
}
if (!propertyName) {
throw new Error(modelClass.name + '.$parseDatabaseJson probably transforms the value of the column ' + column.name + '.' + ' This is a no-no because ' + column.name + ' is needed in the relation ' + this.ownerModelClass.tableName + '.' + this.name);
}
/**
* @private
*/
function parseColumn(column) {
var parts = column.split('.');
return propertyName;
}
return {
table: parts[0] && parts[0].trim(),
name: parts[1] && parts[1].trim()
};
}
/**
* @protected
*/
module.exports = Relation;
}], [{
key: 'extend',
value: function extend(subclassConstructor) {
_utils2.default.inherits(subclassConstructor, this);
return subclassConstructor;
}
}, {
key: 'parseFilter',
value: function parseFilter(mapping) {
if (_lodash2.default.isFunction(mapping.filter)) {
return mapping.filter;
} else if (_lodash2.default.isObject(mapping.filter)) {
return function (queryBuilder) {
queryBuilder.where(mapping.filter);
};
} else {
return _lodash2.default.noop;
}
}
/**
* @protected
*/
}, {
key: 'parseColumn',
value: function parseColumn(column) {
var parts = column.split('.');
return {
table: parts[0] && parts[0].trim(),
name: parts[1] && parts[1].trim()
};
}
}]);
return Relation;
})();
exports.default = Relation;
'use strict';
var _ = require('lodash');
var Promise = require('bluebird');
var Model = require('./model/Model');
var utils = require('./utils');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = transaction;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _Model = require('./model/Model');
var _Model2 = _interopRequireDefault(_Model);
var _utils = require('./utils');
var _utils2 = _interopRequireDefault(_utils);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**

@@ -66,3 +84,3 @@ * Starts a transaction.

* objection.transaction(Person, function (Person) {
* var knex = this;
* let knex = this;
*

@@ -86,23 +104,23 @@ * return Person

*/
module.exports = function transaction() {
function transaction() {
// There must be at least one model class and the callback.
if (arguments.length < 2) {
return Promise.reject(new Error('objection.transaction: provide at least one Model class to bind to the transaction'));
return _bluebird2.default.reject(new Error('objection.transaction: provide at least one Model class to bind to the transaction'));
}
// The last argument should be the callback and all other Model subclasses.
var callback = _.last(arguments);
var modelClasses = _.take(arguments, arguments.length - 1);
var i;
var callback = _lodash2.default.last(arguments);
var modelClasses = _lodash2.default.take(arguments, arguments.length - 1);
var i = undefined;
for (i = 0; i < modelClasses.length; ++i) {
if (!utils.isSubclassOf(modelClasses[i], Model)) {
return Promise.reject(new Error('objection.transaction: all but the last argument should be Model subclasses'));
if (!_utils2.default.isSubclassOf(modelClasses[i], _Model2.default)) {
return _bluebird2.default.reject(new Error('objection.transaction: all but the last argument should be Model subclasses'));
}
}
var knex = _.first(modelClasses).knex();
var knex = _lodash2.default.first(modelClasses).knex();
for (i = 0; i < modelClasses.length; ++i) {
if (modelClasses[i].knex() !== knex) {
return Promise.reject(new Error('objection.transaction: all Model subclasses must be bound to the same database'));
return _bluebird2.default.reject(new Error('objection.transaction: all Model subclasses must be bound to the same database'));
}

@@ -113,15 +131,15 @@ }

if (isGenerator(callback)) {
callback = Promise.coroutine(callback);
callback = _bluebird2.default.coroutine(callback);
}
return knex.transaction(function (trx) {
for (var i = 0; i < modelClasses.length; ++i) {
modelClasses[i] = modelClasses[i].bindTransaction(trx);
for (var _i = 0; _i < modelClasses.length; ++_i) {
modelClasses[_i] = modelClasses[_i].bindTransaction(trx);
}
return Promise.try(function () {
return _bluebird2.default.try(function () {
return callback.apply(trx, modelClasses);
});
});
};
}

@@ -137,4 +155,4 @@ /**

* ```js
* var Person = require('./models/Person');
* var transaction;
* let Person = require('./models/Person');
* let transaction;
*

@@ -167,14 +185,14 @@ * objection.transaction.start(Person).then(function (trx) {

*/
module.exports.start = function (modelClassOrKnex) {
transaction.start = function (modelClassOrKnex) {
var knex = modelClassOrKnex;
if (utils.isSubclassOf(modelClassOrKnex, Model)) {
if (_utils2.default.isSubclassOf(modelClassOrKnex, _Model2.default)) {
knex = modelClassOrKnex.knex();
}
if (!_.isFunction(knex.transaction)) {
return Promise.reject(new Error('objection.transaction.start: first argument must be a model class or a knex instance'));
if (!_lodash2.default.isFunction(knex.transaction)) {
return _bluebird2.default.reject(new Error('objection.transaction.start: first argument must be a model class or a knex instance'));
}
return new Promise(function (resolve) {
return new _bluebird2.default(function (resolve) {
knex.transaction(function (trx) {

@@ -190,2 +208,2 @@ resolve(trx);

return fn && fn.constructor && fn.constructor.name === 'GeneratorFunction';
}
}
'use strict';
var _ = require('lodash')
, util = require('util');
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _ = require('lodash'),
util = require('util');
/**

@@ -18,8 +28,8 @@ * Makes the `Constructor` inherit `SuperConstructor`.

*/
module.exports.inherits = function(subClass, superClass) {
module.exports.inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === 'undefined' ? 'undefined' : (0, _typeof3.default)(superClass)));
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
subClass.prototype = (0, _create2.default)(superClass && superClass.prototype, {
constructor: {

@@ -48,3 +58,3 @@ value: subClass,

*/
module.exports.isSubclassOf = function(Constructor, SuperConstructor) {
module.exports.isSubclassOf = function (Constructor, SuperConstructor) {
if (!_.isFunction(SuperConstructor)) {

@@ -100,2 +110,2 @@ return false;

return knex.client.dialect === 'postgresql';
};
};
'use strict';
var util = require('util');
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = ValidationError;
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _util = require('util');
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Note: babel cannot inherit from built-in types like Error.
// that's why we use ES5 inheritance here.
/**

@@ -30,9 +46,5 @@ * Error of this class is thrown when a Model validation fails.

*/
this.message = JSON.stringify(errors, null, 2);
this.message = (0, _stringify2.default)(errors, null, 2);
}
util.inherits(ValidationError, Error);
module.exports = ValidationError;
_util2.default.inherits(ValidationError, Error);
{
"name": "objection",
"version": "0.3.3",
"version": "0.4.0-rc.1",
"description": "An SQL-friendly ORM for Node.js",
"main": "objection.js",
"main": "lib/objection.js",
"license": "MIT",
"scripts": {
"test": "istanbul --config=.istanbul.yml cover _mocha -- --slow 10 --timeout 5000 --reporter spec --recursive tests",
"build": "node build",
"test": "npm run build && istanbul --config=.istanbul.yml cover _mocha -- --slow 10 --timeout 5000 --reporter spec --recursive tests",
"perf": "mocha --slow 60000 --timeout 60000 --reporter spec --recursive perf",
"test-only": "mocha --slow 10 --timeout 5000 --reporter spec --recursive tests",
"test-bail": "mocha --slow 10 --timeout 5000 --reporter spec --recursive tests --bail",
"test-trace-opt": "mocha --slow 10 --timeout 5000 --reporter spec --recursive tests --trace_opt --trace_deopt",
"coveralls": "cat ./test-coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
"coveralls": "cat ./test-coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"prepublish": "npm run build"
},

@@ -40,5 +40,6 @@ "author": {

"lib/*",
"objection.js"
"src/*"
],
"dependencies": {
"babel-runtime": "^6.3.19",
"bluebird": "^3.0.5",

@@ -50,5 +51,10 @@ "lodash": "^3.9.0",

"devDependencies": {
"browserify-global-shim": "^1.0.0",
"babel-core": "^6.3.17",
"babel-plugin-transform-decorators-legacy": "^1.3.3",
"babel-plugin-transform-runtime": "^6.3.13",
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"coveralls": "^2.11.2",
"expect.js": "^0.3.1",
"glob": "^6.0.1",
"grunt": "^0.4.5",

@@ -58,2 +64,3 @@ "grunt-jsdoc": "^1.0.0",

"knex": "^0.9.0",
"mkdirp": "^0.5.1",
"mocha": "^2.3.4",

@@ -63,12 +70,3 @@ "mysql": "^2.7.0",

"sqlite3": "^3.0.8"
},
"browserify": {
"transform": [
"browserify-global-shim"
]
},
"browserify-global-shim": {
"bluebird": "Promise",
"lodash": "_"
}
}
[![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?branch=master&service=github)](https://coveralls.io/github/Vincit/objection.js?branch=master)
# Introduction
Objection.js is an [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Node.js](https://nodejs.org/)
that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying
database engine.
Objection.js is built on the wonderful SQL query builder [knex](http://knexjs.org). All databases supported by knex
are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://travis-ci.org/Vincit/objection.js).
What objection.js gives you:
* An easy declarative way of [defining models](#models) and relations between them
* Simple and fun way to [fetch, insert, update and delete](#query-examples) models using the full power of SQL
* Powerful mechanisms for [eager loading](#eager-queries) and [inserting](#eager-inserts) arbitrarily large trees of relations
* A way to [store complex documents](#documents) as single rows
* Completely [Promise](https://github.com/petkaantonov/bluebird) based API
* Easy to use [transactions](#transactions)
* Optional [JSON schema](#validation) validation
What objection.js **doesn't** give you:
* A custom query DSL. SQL is used as a query language.
* Automatic database schema creation and migration.
It is useful for the simple things, but usually just gets in your way when doing anything non-trivial.
Objection.js leaves the schema related things to you. knex has a great [migration tool](http://knexjs.org/#Migrations)
that we recommend for this job. Check out the [example project](https://github.com/Vincit/objection.js/tree/master/examples/express).
Objection.js uses Promises and coding practices that make it ready for the future. We use Well known
[OOP](https://en.wikipedia.org/wiki/Object-oriented_programming) techniques and ES6 compatible classes and inheritance
in the codebase. You can even use things like ES7 [async/await](http://jakearchibald.com/2014/es7-async-functions/)
using a transpiler such as [Babel](https://babeljs.io/). Check out our [ES6](https://github.com/Vincit/objection.js/tree/master/examples/express-es6)
and [ES7](https://github.com/Vincit/objection.js/tree/master/examples/express-es7) example projects.
# Topics
- [Installation](#installation)
- [Getting started](#getting-started)
- [Query examples](#query-examples)
- [Eager queries](#eager-queries)
- [Eager inserts](#eager-inserts)
- [Transactions](#transactions)
- [Documents](#documents)
- [Validation](#validation)
- [Models](#models)
- [Testing](#testing)
- [API Documentation](http://vincit.github.io/objection.js/Model.html)
- [Recipe book](RECIPES.md)
- [Changelog](#changelog)
# Installation
```sh
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
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
git clone git@github.com:Vincit/objection.js.git objection
cd objection/examples/express
npm install
# We use knex for migrations in this example.
npm install knex -g
knex migrate:latest
npm start
```
The `express` example project is a simple express server. The `example-requests.sh` file contains a bunch of curl
commands for you to start playing with the REST API.
```sh
cat example-requests.sh
```
If you are using a newer version of Node and you want to use ES6 features then our
[ES6 version of the example project](https://github.com/Vincit/objection.js/tree/master/examples/express-es6)
is the best place to start.
We also have an [ES7 version of the example project](https://github.com/Vincit/objection.js/tree/master/examples/express-es7)
that uses [Babel](https://babeljs.io/) for ES7 --> ES5 transpiling.
Also check out our [API documentation](http://vincit.github.io/objection.js/Model.html) and [recipe book](RECIPES.md).
# Query examples
The `Person` model used in the examples is defined [here](#models).
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),
[$query()](http://vincit.github.io/objection.js/Model.html#Squery) or [$relatedQuery()](http://vincit.github.io/objection.js/Model.html#SrelatedQuery).
All these methods return a [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html) instance that can be used just like
a [knex QueryBuilder](http://knexjs.org/#Builder).
Insert a person to the database:
```js
Person
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'})
.then(function (jennifer) {
console.log(jennifer instanceof Person); // --> true
console.log(jennifer.firstName); // --> 'Jennifer'
console.log(jennifer.fullName()); // --> 'Jennifer Lawrence'
})
.catch(function (err) {
console.log('oh noes');
});
```
```sql
insert into "Person" ("firstName", "lastName") values ('Jennifer', 'Lawrence')
```
Fetch all persons from the database:
```js
Person
.query()
.then(function (persons) {
console.log(persons[0] instanceof Person); // --> true
console.log('there are', persons.length, 'Persons in total');
})
.catch(function (err) {
console.log('oh noes');
});
```
```sql
select * from "Person"
```
The return value of the `.query()` method is an instance of [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html)
that has all the methods a [knex QueryBuilder](http://knexjs.org/#Builder) has. Here is a simple example that uses some of them:
```js
Person
.query()
.where('age', '>', 40)
.andWhere('age', '<', 60)
.andWhere('firstName', 'Jennifer')
.orderBy('lastName')
.then(function (middleAgedJennifers) {
console.log('The last name of the first middle aged Jennifer is');
console.log(middleAgedJennifers[0].lastName);
});
```
```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:
```js
Person
.query()
.patch({lastName: 'Dinosaur'})
.where('age', '>', 60)
.then(function (numUpdated) {
console.log('all persons over 60 years old are now dinosaurs');
console.log(numUpdated, 'people were updated');.
})
.catch(function (err) {
console.log(err.stack);
});
```
```sql
update "Person" set "lastName" = 'Dinosaur' where "age" > 60
```
The `.patch()` and `.update()` method return the number of updated rows. If you want the freshly updated
model as a result you can use the helper method `.patchAndFetchById()` and `.updateAndFetchById()`.
```js
Person
.query()
.patchAndFetchById(246, {lastName: 'Updated'})
.then(function (updated) {
console.log(updated.lastName); // --> Updated.
})
.catch(function (err) {
console.log(err.stack);
});
```
```sql
update "Person" set "lastName" = 'Updated' where "id" = 246
select * from "Person" where "id" = 246
```
While the static `.query()` method can be used to create a query to a whole table `.$relatedQuery()` method
can be used to query a single relation. `.$relatedQuery()` returns an instance of [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html)
just like the `.query()` method.
```js
var jennifer;
Person
.query()
.where('firstName', 'Jennifer')
.first()
.then(function (person) {
jennifer = person;
return jennifer
.$relatedQuery('pets')
.where('species', 'dog')
.orderBy('name');
})
.then(function (jennifersDogs) {
console.log(jennifersDogs[0] instanceof Animal); // --> true
console.log(jennifer.pets === jennifersDogs); // --> true
console.log('Jennifer has', jennifersDogs.length, 'dogs');
})
.catch(function (err) {
console.log(err.stack);
});
```
```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:
```js
Person
.query()
.where('id', 1)
.first()
.then(function (person) {
return person.$relatedQuery('pets').insert({name: 'Fluffy'});
})
.then(function (fluffy) {
console.log(fluffy.id);
})
.catch(function (err) {
console.log('something went wrong with finding the person OR inserting the pet');
console.log(err.stack);
});
```
```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, as it isn't
something that can be done easily using SQL. In addition to making your life easier, eager queries avoid the select N+1
problem and provide a great performance. The following examples demonstrate how to use it:
Fetch the `pets` relation for all results of a query:
```js
Person
.query()
.eager('pets')
.then(function (persons) {
// Each person has the `.pets` property populated with Animal objects related
// through `pets` relation.
console.log(persons[0].pets[0].name);
console.log(persons[0].pets[0] instanceof Animal); // --> true
});
```
Fetch multiple relations on multiple levels:
```js
Person
.query()
.eager('[pets, children.[pets, children]]')
.then(function (persons) {
// Each person has the `.pets` property populated with Animal objects related
// through `pets` relation. The `.children` property contains the Person's
// children. Each child also has the `pets` and `children` relations eagerly
// fetched.
console.log(persons[0].pets[0].name);
console.log(persons[1].children[2].pets[1].name);
console.log(persons[1].children[2].children[0].name);
});
```
Fetch one relation recursively:
```js
Person
.query()
.eager('[pets, children.^]')
.then(function (persons) {
// The children relation is from Person to Person. If we want to fetch the whole
// descendant tree of a person we can just say "fetch this relation recursively"
// using the `.^` notation.
console.log(persons[0].children[0].children[0].children[0].children[0].firstName);
});
```
Relations can be filtered using named filters like this:
```js
Person
.query()
.eager('[pets(orderByName, onlyDogs), children(orderByAge).[pets, children]]', {
orderByName: function (builder) {
builder.orderBy('name');
},
orderByAge: function (builder) {
builder.orderBy('age')
},
onlyDogs: function (builder) {
builder.where('species', 'dog')
}
})
.then(function (persons) {
console.log(persons[0].children[0].pets[0].name);
console.log(persons[0].children[0].movies[0].id);
});
```
The expressions can be arbitrarily deep. See the full description [here](http://vincit.github.io/objection.js/RelationExpression.html).
Because the eager expressions are strings they can be easily passed for example as a query parameter of an HTTP
request. However, allowing the client to pass expressions like this without any limitations is not very secure.
Therefore the [QueryBuilder](http://vincit.github.io/objection.js/QueryBuilder.html) has the `.allowEager` method.
allowEager can be used to limit the allowed eager expression to a certain subset. Like this:
```js
expressApp.get('/persons', function (req, res, next) {
Person
.query()
.allowEager('[pets, children.pets]')
.eager(req.query.eager)
.then(function (persons) { res.send(persons); })
.catch(next);
});
```
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
[Model](http://vincit.github.io/objection.js/Model.html).
# Eager inserts
Arbitrary relation trees can be inserted using the [insertWithRelated()](http://vincit.github.io/objection.js/QueryBuilder.html#insertWithRelated)
method:
```js
Person
.query()
.insertWithRelated({
firstName: 'Sylvester',
lastName: 'Stallone',
children: [{
firstName: 'Sage',
lastName: 'Stallone',
pets: [{
name: 'Fluffy',
species: 'dog'
}]
}]
});
```
The query above will insert 'Sylvester', 'Sage' and 'Fluffy' into db and create relationships between them as defined
in the `relationMappings` of the models. Technically `insertWithRelated` builds a dependency graph from the object
tree and inserts the models that don't depend on any other models until the whole tree is inserted.
If you need to refer to the same model in multiple places you can use the special properties `#id` and `#ref` like this:
```js
Person
.query()
.insertWithRelated([{
firstName: 'Jennifer',
lastName: 'Lawrence',
movies: [{
"#id": 'silverLiningsPlaybook'
name: 'Silver Linings Playbook',
duration: 122
}]
}, {
firstName: 'Bradley',
lastName: 'Cooper',
movies: [{
"#ref": 'silverLiningsPlaybook'
}]
}]);
```
The query above will insert only one movie (the 'Silver Linings Playbook') but both 'Jennifer' and 'Bradley' will have
the movie related to them through the many-to-many relation `movies`. The `#id` can be any string. There are no format
or length requirements for them. It is quite easy to create circular dependencies using `#id` and `#ref`. Luckily
`insertWithRelated` detects them and rejects the query with a clear error message.
You can refer to the properties of other models anywhere in the graph using expressions of format `#ref{<id>.<property>}`
as long as the reference doesn't create a circular dependency. For example:
```js
Person
.query()
.insertWithRelated([{
"#id": 'jenniLaw',
firstName: 'Jennifer',
lastName: 'Lawrence',
pets: [{
name: "I am the dog of #ref{jenniLaw.firstName} whose id is #ref{jenniLaw.id}",
species: 'dog'
}]
}]);
```
The query above will insert a pet named `I am the dog of Jennifer whose id is 523` for Jennifer. If `#ref{}` is used
within a string, the references are replaced with the referred values inside the string. If the reference string
contains nothing but the reference, the referred value is copied to it's place preserving its type.
See the [allowInsert](http://vincit.github.io/objection.js/QueryBuilder.html#allowInsert) method if you need to limit
which relations can be inserted using `insertWithRelated` method to avoid security issues.
By the way, if you are using Postgres the inserts are done in batches for maximum performance.
# Transactions
There are two ways to use transactions in objection.js
1. [Transaction callback](#transaction-callback)
2. [Transaction object](#transaction-object)
## Transaction callback
The first way to work with transactions is to perform all operations inside one callback using the
[objection.transaction](http://vincit.github.io/objection.js/global.html#transaction) function. The beauty of this
method is that you don't need to pass a transaction object to each query explicitly as long as you start all queries
using the bound model classes that are passed to the transaction callback as arguments.
Transactions are started by calling the [objection.transaction](http://vincit.github.io/objection.js/global.html#transaction)
function. Give all the models you want to use in the transaction as parameters to the `transaction` function. The model
classes are bound to a newly created transaction and passed to the callback function. Inside this callback, all queries
started through them take part in the same transaction.
The transaction is committed if the returned Promise is resolved successfully. If the returned Promise is rejected
the transaction is rolled back.
```js
objection.transaction(Person, Animal, function (Person, Animal) {
return Person
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'})
.then(function () {
return Animal
.query()
.insert({name: 'Scrappy'});
});
}).then(function (scrappy) {
console.log('Jennifer and Scrappy were successfully inserted');
}).catch(function (err) {
console.log('Something went wrong. Neither Jennifer nor Scrappy were inserted');
});
```
You only need to give the `transaction` function the model classes you use explicitly. All the related model classes
are implicitly bound to the same transaction.
```js
objection.transaction(Person, function (Person) {
return Person
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'})
.then(function (jennifer) {
// This creates a query using the `Animal` model class but we
// don't need to give `Animal` as one of the arguments to the
// transaction function.
return jennifer
.$relatedQuery('pets')
.insert({name: 'Scrappy'});
});
}).then(function (scrappy) {
console.log('Jennifer and Scrappy were successfully inserted');
}).catch(function (err) {
console.log('Something went wrong. Neither Jennifer nor Scrappy were inserted');
});
```
The only way you can mess up with the transactions is if you _explicitly_ start a query using a model class that is not
bound to the transaction:
```js
var Person = require('./models/Person');
var Animal = require('./models/Animal');
objection.transaction(Person, function (Person) {
return Person
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'})
.then(function (jennifer) {
// OH NO! This query is executed outside the transaction
// since the `Animal` class is not bound to the transaction.
return Animal
.query()
.insert({name: 'Scrappy'});
});
});
```
## Transaction object
The second way to use transactions is to express the transaction as an object and bind model classes to the transaction
when you use them. This way is more convenient when you need to pass the transaction to functions and services.
The transaction object can be created using the [objection.transaction.start](http://vincit.github.io/objection.js/global.html#start)
method. You need to remember to call either the `commit` or `rollback` method of the transaction object.
```js
// You need to pass some model (any model with a knex connection)
// or the knex connection itself to the start method.
var trx;
objection.transaction.start(Person).then(function (transaction) {
trx = transaction;
return Person
.bindTransaction(trx)
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'});
}).then(function (jennifer) {
// jennifer was created using a bound model class. Therefore
// all queries started through it automatically take part in
// the same transaction. We don't need to bind anything here.
return jennifer
.$relatedQuery('pets')
.insert({name: 'Fluffy'});
}).then(function () {
return Movie
.bindTransaction(trx)
.query()
.where('name', 'ilike', '%forrest%');
}).then(function (movies) {
console.log(movies);
return trx.commit();
}).catch(function () {
return trx.rollback();
});
```
To understand what is happening in the above example you need to know how `bindTransaction` method works.
`bindTransaction` actually returns an anonymous subclass of the model class you call it for and sets the
transaction's database connection as that class's database connection. This way all queries started through
that anonymous class use the same connection and take part in the same transaction. If we didn't create a
subclass and just set the original model's database connection, all query chains running in parallel would
suddenly jump into the same transaction and things would go terribly wrong.
Now that you have an idea how the `bindTransaction` works you should see that the previous example could
also be implemented like this:
```js
var BoundPerson;
var BoundMovie;
objection.transaction.start(Person).then(function (transaction) {
BoundPerson = Person.bindTransaction(transaction);
BoundMovie = Movie.bindTransaction(transaction);
return BoundPerson
.query()
.insert({firstName: 'Jennifer', lastName: 'Lawrence'});
}).then(function (jennifer) {
return jennifer
.$relatedQuery('pets')
.insert({name: 'Fluffy'});
}).then(function () {
return BoundMovie
.query()
.where('name', 'ilike', '%forrest%');
}).then(function (movies) {
console.log(movies);
return BoundPerson.knex().commit();
}).catch(function () {
return BoundPerson.knex().rollback();
});
```
which is pretty much what the [objection.transaction](http://vincit.github.io/objection.js/global.html#transaction)
function does.
# Documents
Objection.js makes it easy to store non-flat documents as table rows. All properties of a model that are marked as
objects or arrays in the model's `jsonSchema` are automatically converted to JSON strings in the database and
back to objects when read from the database. The database columns for the object properties can be normal
text columns. Postgresql has the `json` and `jsonb` data types that can be used instead for better performance
and possibility to [make queries](http://www.postgresql.org/docs/9.4/static/functions-json.html) to the documents.
The `address` property of the Person model is defined as an object in the [Person.jsonSchema](#models):
```js
Person
.query()
.insert({
firstName: 'Jennifer',
lastName: 'Lawrence',
age: 24,
address: {
street: 'Somestreet 10',
zipCode: '123456',
city: 'Tampere'
}
})
.then(function (jennifer) {
console.log(jennifer.address.city); // --> Tampere
return Person.query().where('id', jennifer.id);
})
.then(function (jenniferFromDb) {
console.log(jenniferFromDb.address.city); // --> Tampere
})
.catch(function (err) {
console.log('oh noes');
});
```
# Validation
[JSON schema](http://json-schema.org/) validation can be enabled by setting the [jsonSchema](#models) property
of a model class. The validation is ran each time a `Model` instance is created. For example all these will trigger
the validation:
```js
Person.fromJson({firstName: 'jennifer', lastName: 'Lawrence'});
Person.query().insert({firstName: 'jennifer', lastName: 'Lawrence'});
Person.query().update({firstName: 'jennifer', lastName: 'Lawrence'}).where('id', 10);
// Patch operation ignores the `required` property of the schema and only validates the
// given properties. This allows a subset of model's properties to be updated.
Person.query().patch({age: 24}).where('age', '<', 24);
```
You rarely need to call [$validate](http://vincit.github.io/objection.js/Model.html#Svalidate) method explicitly, but you
can do it when needed. If validation fails a [ValidationError](http://vincit.github.io/objection.js/ValidationError.html)
will be thrown. Since we use Promises, this usually means that a promise will be rejected with an instance of
`ValidationError`.
```js
Person.query().insert({firstName: 'jennifer'}).catch(function (err) {
console.log(err instanceof objection.ValidationError); // --> true
console.log(err.data); // --> {lastName: 'required property missing'}
});
```
See [the recipe book](https://github.com/Vincit/objection.js/blob/master/RECIPES.md#custom-validation) for instructions
if you want to use some other validation library.
# Models
Models are created by inheriting from the [Model](http://vincit.github.io/objection.js/Model.html) base class.
In objection.js the inheritance is done as transparently as possible. There is no custom Class abstraction making you
wonder what the hell is happening. Just plain old ugly javascript inheritance.
## 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 does the basic prototype inheritance but also
// inherits all the static methods and properties like `Model.query()`
// and `Model.fromJson()`. This is consistent with ES6 class inheritance.
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() {
Model.apply(this, arguments);
}
Model.extend(Person);
module.exports = Person;
// You can add custom functionality to Models just as you would
// to any javascript class.
Person.prototype.fullName = function () {
return this.firstName + ' ' + this.lastName;
};
// Table name is the only required property.
Person.tableName = 'Person';
// Optional JSON schema. This is not the database schema! Nothing is generated
// based on this. This is only used for validation. Whenever a model instance
// is created it is checked against this schema. http://json-schema.org/.
Person.jsonSchema = {
type: 'object',
required: ['firstName', 'lastName'],
properties: {
id: {type: 'integer'},
parentId: {type: ['integer', 'null']},
firstName: {type: 'string', minLength: 1, maxLength: 255},
lastName: {type: 'string', minLength: 1, maxLength: 255},
age: {type: 'number'},
address: {
type: 'object',
properties: {
street: {type: 'string'},
city: {type: 'string'},
zipCode: {type: 'string'}
}
}
}
};
// This object defines the relations to other models.
Person.relationMappings = {
pets: {
relation: Model.OneToManyRelation,
// The related model. This can be either a Model subclass constructor or an
// absolute file path to a module that exports one. We use the file path version
// here to prevent require loops.
modelClass: __dirname + '/Animal',
join: {
from: 'Person.id',
to: 'Animal.ownerId'
}
},
movies: {
relation: Model.ManyToManyRelation,
modelClass: __dirname + '/Movie',
join: {
from: 'Person.id',
// ManyToMany relation needs the `through` object to describe the join table.
through: {
from: 'Person_Movie.personId',
to: 'Person_Movie.movieId'
},
to: 'Movie.id'
}
},
parent: {
relation: Model.OneToOneRelation,
modelClass: Person,
join: {
from: 'Person.parentId',
to: 'Person.id'
}
},
children: {
relation: Model.OneToManyRelation,
modelClass: Person,
join: {
from: 'Person.id',
to: 'Person.parentId'
}
}
};
```
# Testing
To run the tests, all you need to do is configure the databases and run `npm test`. Check out
[this](https://github.com/Vincit/objection.js/blob/master/tests/integration/index.js) file for the
test database configurations. If you don't want to run the tests against all databases you can
just comment out configurations from the `testDatabaseConfigs` list.
# Changelog
## 0.3.3
#### What's new
* fix regression: QueryBuilder.from is broken.
## 0.3.2
#### What's new
* Improved relation expression whitespace handling.
## 0.3.1
#### What's new
* `whereJson*` methods can now be used inside functions given to `where` methods.
* Added multiple missing knex methods to `QueryBuilder`.
## 0.3.0
#### What's new
* [insertWithRelated](http://vincit.github.io/objection.js/QueryBuilder.html#insertWithRelated) method for
inserting model trees
* [insertAndFetch](http://vincit.github.io/objection.js/QueryBuilder.html#insertAndFetch),
[updateAndFetchById](http://vincit.github.io/objection.js/QueryBuilder.html#updateAndFetchById) and
[patchAndFetchById](http://vincit.github.io/objection.js/QueryBuilder.html#patchAndFetchById) helper methods
* Filters for [eager expressions](#eager-queries)
* [New alternative way to use transactions](#transaction-object)
* Many performance updates related to cloning, serializing and deserializing model trees.
#### Breaking changes
* QueryBuilder methods `update`, `patch` and `delete` now return the number of affected rows.
The new methods `updateAndFetchById` and `patchAndFetchById` may help with the migration
* `modelInstance.$query()` instance method now returns a single model instead of an array
* Removed `Model.generateId()` method. `$beforeInsert` can be used instead
## 0.2.8
#### What's new
* ES6 inheritance support
* generator function support for transactions
* traverse,pick and omit methods for Model and QueryBuilder
* bugfix: issue #38
## 0.2.7
#### What's new
* bugfix: fix #37 also for `$query()`.
* Significant `toJson`/`fromJson` performance boost.
## 0.2.6
#### What's new
* bugfix: fix regression bug that broke dumpSql.
## 0.2.5
#### What's new
* bugfix: fix regression bug that prevented values assigned to `this` in `$before` callbacks from getting into
the actual database query
## 0.2.4
#### What's new
* bugfix: many-to-many relations didn't work correctly with a snake_case to camelCase conversion
in the related model class.
## 0.2.3
#### What's new
* Promise constructor is now exposed through `require('objection').Promise`.
## 0.2.2
#### What's new
* $beforeUpdate, $afterUpdate, $beforeInsert etc. are now asynchronous and you can return promises from them.
* Added `Model.fn()` shortcut to `knex.fn`.
* Added missing `asCallback` and `nodeify` methods for `QueryBuilder`.
## 0.2.1
#### What's new
* bugfix: Chaining `insert` with `returning` now returns all listed columns.
## 0.2.0
#### What's new
* 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.
# [Objection.js](http://vincit.github.io/objection.js)

Sorry, the diff of this file is too big to display

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