objection
Advanced tools
Comparing version 0.2.1 to 0.2.2
@@ -49,4 +49,9 @@ 'use strict'; | ||
var self = this; | ||
var ModelClass = relation.relatedModelClass; | ||
var QueryBuilder = relation.relatedModelClass.RelatedQueryBuilder; | ||
var queryBuilder = QueryBuilder.forClass(ModelClass); | ||
return relation.find(relation.relatedModelClass.query(), this.models).then(function (related) { | ||
relation.find(queryBuilder, this.models); | ||
return queryBuilder.then(function (related) { | ||
return self._fetchNextEager(relation, related, nextEager); | ||
@@ -53,0 +58,0 @@ }); |
226
lib/Model.js
@@ -192,15 +192,27 @@ 'use strict'; | ||
.findImpl(function () { | ||
this.where(ModelClass.getFullIdColumn(), self.$id()); | ||
var queryBuilder = this; | ||
queryBuilder.where(ModelClass.getFullIdColumn(), self.$id()); | ||
}) | ||
.insertImpl(function () { | ||
ModelClass.$$insert(this, self); | ||
.insertImpl(function (insertion) { | ||
var queryBuilder = this; | ||
insertion.setModels(self); | ||
queryBuilder.$$insert(self); | ||
}) | ||
.updateImpl(function (update) { | ||
ModelClass.$$update(this, update || self).where(ModelClass.getFullIdColumn(), self.$id()); | ||
var queryBuilder = this; | ||
if (!update.model()) { | ||
update.setModels([self]); | ||
} | ||
queryBuilder.$$update(update).where(ModelClass.getFullIdColumn(), self.$id()); | ||
}) | ||
.patchImpl(function (patch) { | ||
ModelClass.$$patch(this, patch || self).where(ModelClass.getFullIdColumn(), self.$id()); | ||
var queryBuilder = this; | ||
if (!patch.model()) { | ||
patch.setModels([self]); | ||
} | ||
queryBuilder.$$update(patch).where(ModelClass.getFullIdColumn(), self.$id()); | ||
}) | ||
.deleteImpl(function () { | ||
ModelClass.$$delete(this).where(ModelClass.getFullIdColumn(), self.$id()); | ||
var queryBuilder = this; | ||
queryBuilder.$$delete().where(ModelClass.getFullIdColumn(), self.$id()); | ||
}) | ||
@@ -340,24 +352,31 @@ .relateImpl(function () { | ||
return ModelClass.QueryBuilder | ||
return ModelClass.RelatedQueryBuilder | ||
.forClass(ModelClass) | ||
.findImpl(function () { | ||
relation.find(this, self); | ||
var queryBuilder = this; | ||
relation.find(queryBuilder, [self]); | ||
}) | ||
.insertImpl(function (modelsToInsert) { | ||
relation.insert(this, self, modelsToInsert); | ||
.insertImpl(function (insert) { | ||
var queryBuilder = this; | ||
relation.insert(queryBuilder, self, insert); | ||
}) | ||
.updateImpl(function (update) { | ||
relation.update(this, self, update); | ||
var queryBuilder = this; | ||
relation.update(queryBuilder, self, update); | ||
}) | ||
.patchImpl(function (patch) { | ||
relation.patch(this, self, patch); | ||
var queryBuilder = this; | ||
relation.patch(queryBuilder, self, patch); | ||
}) | ||
.deleteImpl(function () { | ||
relation.delete(this, self); | ||
var queryBuilder = this; | ||
relation.delete(queryBuilder, self); | ||
}) | ||
.relateImpl(function (ids) { | ||
relation.relate(this, self, ids); | ||
var queryBuilder = this; | ||
relation.relate(queryBuilder, self, ids); | ||
}) | ||
.unrelateImpl(function () { | ||
relation.unrelate(this, self); | ||
var queryBuilder = this; | ||
relation.unrelate(queryBuilder, self); | ||
}); | ||
@@ -477,2 +496,8 @@ }; | ||
* 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|*} | ||
*/ | ||
@@ -485,2 +510,6 @@ Model.prototype.$beforeInsert = 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. | ||
* | ||
* @returns {Promise|*} | ||
*/ | ||
@@ -494,2 +523,6 @@ Model.prototype.$afterInsert = function () { | ||
* | ||
* 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 | ||
@@ -517,2 +550,3 @@ * may not exist. You can check if the update operation is a patch by checking the `opt.patch` | ||
* @param {ModelOptions} opt | ||
* @returns {Promise|*} | ||
*/ | ||
@@ -526,2 +560,4 @@ Model.prototype.$beforeUpdate = function (opt) { | ||
* | ||
* 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 | ||
@@ -549,2 +585,3 @@ * may not exist. You can check if the update operation is a patch by checking the `opt.patch` | ||
* @param {ModelOptions} opt | ||
* @returns {Promise|*} | ||
*/ | ||
@@ -556,6 +593,6 @@ Model.prototype.$afterUpdate = function (opt) { | ||
/** | ||
* QueryBuilder subclass to use. | ||
* QueryBuilder subclass to use in `query()` or `$query()` methods. | ||
* | ||
* This constructor is used whenever a query builder is created. You can override this | ||
* to use your own `QueryBuilder` subclass. | ||
* 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. | ||
*/ | ||
@@ -565,2 +602,10 @@ Model.QueryBuilder = QueryBuilder; | ||
/** | ||
* 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; | ||
/** | ||
* one-to-many relation type. | ||
@@ -827,14 +872,2 @@ * | ||
.forClass(ModelClass) | ||
.insertImpl(function (models) { | ||
ModelClass.$$insert(this, models); | ||
}) | ||
.updateImpl(function (update) { | ||
ModelClass.$$update(this, update); | ||
}) | ||
.patchImpl(function (patch) { | ||
ModelClass.$$patch(this, patch); | ||
}) | ||
.deleteImpl(function () { | ||
ModelClass.$$delete(this); | ||
}) | ||
.relateImpl(function () { | ||
@@ -895,2 +928,10 @@ throw new Error('`relate` makes no sense in this context'); | ||
/** | ||
* Shortcut for `SomeModel.knex().fn`. | ||
*/ | ||
Model.fn = function () { | ||
var knex = this.knex(); | ||
return knex.fn; | ||
}; | ||
/** | ||
* Shortcut for `SomeModel.knex().client.formatter()`. | ||
@@ -1034,10 +1075,13 @@ * | ||
input = ensureArray(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; | ||
}; | ||
@@ -1200,112 +1244,2 @@ | ||
*/ | ||
Model.$$insert = function (builder, $models) { | ||
if (_.isArray($models) && $models.length > 1 && !utils.isPostgres(this.knex())) { | ||
throw new Error('batch insert only works with Postgresql'); | ||
} | ||
var ModelClass = this; | ||
var models = ModelClass.ensureModelArray($models); | ||
var json = _.map(models, function (model) { | ||
var id = ModelClass.generateId(); | ||
if (!_.isNull(id)) { | ||
model.$id(id); | ||
} | ||
model.$beforeInsert(); | ||
return model.$toDatabaseJson(); | ||
}); | ||
if (!builder.has('returning')) { | ||
// If the user hasn't specified a `returning` clause, we make sure | ||
// that at least the identifier is returned. | ||
builder.returning(ModelClass.idColumn); | ||
} | ||
return builder.insert(json).runAfterModelCreatePushFront(function (ret) { | ||
// If the user specified a `returning` clause the result may already be | ||
// an array of models. | ||
if (!_.isEmpty(ret) && _.isObject(ret[0])) { | ||
_.each(models, function (model, index) { | ||
model.$set(ret[index]); | ||
// The returning clause must return at least the identifier. | ||
if (!model.$id()) { | ||
throw new Error('the identifier column "' + ModelClass.idColumn + '"' + | ||
' must be listed in the `returning` clause. (`returning *` is fine also)'); | ||
} | ||
}); | ||
} else { | ||
// If the return value is not an array of models, it is an array of identifiers. | ||
_.each(models, function (model, idx) { | ||
model.$id(ret[idx]); | ||
}); | ||
} | ||
_.each(models, function (model) { | ||
model.$afterInsert(); | ||
}); | ||
if (_.isArray($models)) { | ||
return models; | ||
} else { | ||
return models[0]; | ||
} | ||
}); | ||
}; | ||
/** | ||
* @protected | ||
* @returns {QueryBuilder} | ||
*/ | ||
Model.$$update = function (builder, $update) { | ||
return this.$$updateWithOptions(builder, $update); | ||
}; | ||
/** | ||
* @protected | ||
* @returns {QueryBuilder} | ||
*/ | ||
Model.$$patch = function (builder, $patch) { | ||
return this.$$updateWithOptions(builder, $patch, {patch: true}); | ||
}; | ||
/** | ||
* @private | ||
* @returns {QueryBuilder} | ||
*/ | ||
Model.$$updateWithOptions = function (builder, $update, options) { | ||
if (!$update) { | ||
return builder; | ||
} | ||
var ModelClass = this; | ||
$update = ModelClass.ensureModel($update, options); | ||
$update.$beforeUpdate(options); | ||
// We never want to change the identifier of a model. Delete it from the copy. | ||
var update = $update.$clone(); | ||
delete update[ModelClass.getIdProperty()]; | ||
return builder.update(update.$toDatabaseJson()).runAfterModelCreatePushFront(function () { | ||
$update.$afterUpdate(options); | ||
return $update; | ||
}); | ||
}; | ||
/** | ||
* @protected | ||
* @returns {QueryBuilder} | ||
*/ | ||
Model.$$delete = function (builder) { | ||
return builder.delete().runAfterModelCreatePushFront(function () { | ||
return {}; | ||
}); | ||
}; | ||
/** | ||
* @protected | ||
* @returns {QueryBuilder} | ||
*/ | ||
Model.$$omitNonColumns = function (json) { | ||
@@ -1312,0 +1246,0 @@ if (this.jsonSchema) { |
@@ -390,2 +390,12 @@ "use strict"; | ||
/** | ||
* @private | ||
*/ | ||
ModelBase.$$colToProp = null; | ||
/** | ||
* @private | ||
*/ | ||
ModelBase.$$propToCol = null; | ||
/** | ||
* Makes the given constructor a subclass of this class. | ||
@@ -457,3 +467,29 @@ * | ||
/** | ||
* @ignore | ||
* @param {string} columnName | ||
* @returns {string} | ||
*/ | ||
ModelBase.columnNameToPropertyName = function (columnName) { | ||
this.$$propToCol = this.$$propToCol || Object.create(null); | ||
this.$$colToProp = this.$$colToProp || Object.create(null); | ||
if (this.$$colToProp[columnName]) { | ||
return this.$$colToProp[columnName]; | ||
} | ||
var propertyName = this.$$columnNameToPropertyName(columnName); | ||
this.$$propToCol[propertyName] = columnName; | ||
this.$$colToProp[columnName] = propertyName; | ||
return propertyName; | ||
}; | ||
/** | ||
* @ignore | ||
* @param {string} columnName | ||
* @returns {string} | ||
*/ | ||
ModelBase.$$columnNameToPropertyName = function (columnName) { | ||
var row = {}; | ||
@@ -476,3 +512,29 @@ var value = uuid.v4(); | ||
/** | ||
* @ignore | ||
* @param {string} propertyName | ||
* @returns {string} | ||
*/ | ||
ModelBase.propertyNameToColumnName = function (propertyName) { | ||
this.$$propToCol = this.$$propToCol || Object.create(null); | ||
this.$$colToProp = this.$$colToProp || Object.create(null); | ||
if (this.$$propToCol[propertyName]) { | ||
return this.$$propToCol[propertyName]; | ||
} | ||
var columnName = this.$$propertyNameToColumnName(propertyName); | ||
this.$$propToCol[propertyName] = columnName; | ||
this.$$colToProp[columnName] = propertyName; | ||
return columnName; | ||
}; | ||
/** | ||
* @ignore | ||
* @param {string} propertyName | ||
* @returns {string} | ||
*/ | ||
ModelBase.$$propertyNameToColumnName = function (propertyName) { | ||
var model = {}; | ||
@@ -479,0 +541,0 @@ var value = uuid.v4(); |
@@ -58,17 +58,38 @@ 'use strict'; | ||
*/ | ||
ManyToManyRelation.prototype.find = function (builder, $owners) { | ||
ManyToManyRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
var joinTable = this.joinTable; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
var joinTableAlias = this.joinTableAlias(); | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
return builder | ||
[joinMethod](joinTable + ' as ' + joinTableAlias, joinTableAlias + '.' + this.joinTableOwnerCol, this.fullOwnerCol()) | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, joinTableAlias + '.' + this.joinTableRelatedCol, relatedTableAlias + '.' + this.relatedCol) | ||
.call(this.filter); | ||
}; | ||
/** | ||
* @override | ||
* @inheritDoc | ||
*/ | ||
ManyToManyRelation.prototype.find = function (builder, owners) { | ||
var self = this; | ||
var owners = this.ownerModelClass.ensureModelArray($owners); | ||
if (!builder.has('select')) { | ||
builder.select(this.relatedModelClass.tableName + '.*'); | ||
} | ||
builder.onBuild(function (builder) { | ||
var ownerIds = _.pluck(owners, self.ownerProp); | ||
var ownerJoinColumn = self.fullJoinTableOwnerCol(); | ||
// Add the statements that select the owners' rows. | ||
this.findQuery(builder, _.unique(_.pluck(owners, this.ownerProp))); | ||
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 + '.*'); | ||
} | ||
// Select the joined identifier of the owner model. | ||
builder.select(this.fullJoinTableOwnerCol() + ' as ' + ownerJoinColumnAlias); | ||
self.findQuery(builder, ownerIds).select(ownerJoinColumn + ' as ' + ownerJoinColumnAlias); | ||
}); | ||
return builder.runAfterModelCreate(function (related) { | ||
builder.runAfterModelCreate(function (related) { | ||
var relatedByOwnerId = _.groupBy(related, ownerJoinColumnAlias); | ||
@@ -91,35 +112,20 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
ManyToManyRelation.prototype.insert = function (builder, owner, insertion) { | ||
var self = this; | ||
var joinTable = this.joinTable; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
builder.onBuild(function (builder) { | ||
builder.$$insert(insertion); | ||
}); | ||
var joinTableAlias = this.joinTableAlias(); | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
builder.runAfterModelCreate(function (related) { | ||
var ownerId = owner[self.ownerProp]; | ||
var relatedIds = _.pluck(related, self.relatedProp); | ||
var joinRows = self._createJoinRows(ownerId, relatedIds); | ||
return builder | ||
[joinMethod](joinTable + ' as ' + joinTableAlias, joinTableAlias + '.' + this.joinTableOwnerCol, this.fullOwnerCol()) | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, joinTableAlias + '.' + this.joinTableRelatedCol, relatedTableAlias + '.' + this.relatedCol) | ||
.call(this.filter); | ||
}; | ||
owner[self.name] = _.chain([owner[self.name], related]) | ||
.flatten() | ||
.compact() | ||
.value(); | ||
/** | ||
* @override | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.insert = function (builder, $owner, $insertion) { | ||
var self = this; | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
// This adds the insert operation and the needed runAfter* methods. | ||
this.relatedModelClass.$$insert(builder, $insertion); | ||
return builder.runAfterModelCreate(function (related) { | ||
var joinRows = self._createJoinRows(owner[self.ownerProp], _.isArray(related) ? _.pluck(related, self.relatedProp) : [related[self.relatedProp]]); | ||
owner[self.name] = _.compact(_.flatten([owner[self.name], related])); | ||
// Insert the join rows to the join table. | ||
@@ -139,14 +145,14 @@ return self.relatedModelClass | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.update = function (builder, $owner, $update) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var idSelectQuery = this._makeFindIdQuery(owner[this.ownerProp]); | ||
ManyToManyRelation.prototype.update = function (builder, owner, update) { | ||
var self = this; | ||
// This adds the update operation and the needed runAfter* methods. | ||
this.relatedModelClass.$$update(builder, $update); | ||
builder.onBuild(function (builder) { | ||
var idSelectQuery = self._makeFindIdQuery(owner[self.ownerProp]); | ||
return builder | ||
.whereIn(this.relatedModelClass.getFullIdColumn(), idSelectQuery) | ||
.call(this.filter); | ||
builder | ||
.$$update(update) | ||
.whereIn(self.relatedModelClass.getFullIdColumn(), idSelectQuery) | ||
.call(self.filter); | ||
}); | ||
}; | ||
@@ -157,14 +163,5 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.patch = function (builder, $owner, $patch) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var idSelectQuery = this._makeFindIdQuery(owner[this.ownerProp]); | ||
// This adds the patch operation and the needed runAfter* methods. | ||
this.relatedModelClass.$$patch(builder, $patch); | ||
return builder | ||
.whereIn(this.relatedModelClass.getFullIdColumn(), idSelectQuery) | ||
.call(this.filter); | ||
ManyToManyRelation.prototype.patch = function (builder, owner, patch) { | ||
return this.update(builder, owner, patch); | ||
}; | ||
@@ -175,14 +172,14 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.delete = function (builder, $owner) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var idSelectQuery = this._makeFindIdQuery(owner[this.ownerProp]); | ||
ManyToManyRelation.prototype.delete = function (builder, owner) { | ||
var self = this; | ||
// This adds the delete operation and the needed runAfter* methods. | ||
this.relatedModelClass.$$delete(builder); | ||
builder.onBuild(function (builder) { | ||
var idSelectQuery = self._makeFindIdQuery(owner[self.ownerProp]); | ||
return builder | ||
.whereIn(this.relatedModelClass.getFullIdColumn(), idSelectQuery) | ||
.call(this.filter); | ||
builder | ||
.$$delete() | ||
.whereIn(self.relatedModelClass.getFullIdColumn(), idSelectQuery) | ||
.call(self.filter); | ||
}); | ||
}; | ||
@@ -193,15 +190,11 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.relate = function (builder, $owner, $ids) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var joinRows = this._createJoinRows(owner[this.ownerProp], _.flatten([$ids])); | ||
var arrayInput = _.isArray($ids); | ||
ManyToManyRelation.prototype.relate = function (builder, owner, ids) { | ||
var self = this; | ||
// Insert join rows into the join table. | ||
return builder.insert(joinRows).into(this.joinTable).returning('id').runAfterModelCreate(function (ids) { | ||
_.each(joinRows, function (row, idx) { | ||
row.id = ids[idx] || null; | ||
}); | ||
return arrayInput ? joinRows : joinRows[0]; | ||
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); | ||
}); | ||
@@ -213,25 +206,23 @@ }; | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
ManyToManyRelation.prototype.unrelate = function (builder, $owner) { | ||
ManyToManyRelation.prototype.unrelate = function (builder, owner) { | ||
var self = this; | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var idSelectQuery = builder | ||
.clone() | ||
.clear('select') | ||
.clearCustomImpl() | ||
.select(this.fullRelatedCol()) | ||
.call(this.filter); | ||
builder.onBuild(function (builder) { | ||
var idSelectQuery = builder.cloneWhereQuery().select(self.fullRelatedCol()).call(self.filter); | ||
// Delete the join rows from the join table. | ||
return builder | ||
.clear() | ||
.delete() | ||
.from(self.joinTable) | ||
.where(self.fullJoinTableOwnerCol(), owner[this.ownerProp]) | ||
.whereIn(self.fullJoinTableRelatedCol(), idSelectQuery.build()) | ||
.runAfterModelCreate(_.constant({})); | ||
// 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); | ||
}); | ||
}; | ||
/** | ||
* @private | ||
*/ | ||
ManyToManyRelation.prototype._makeFindIdQuery = function (ownerId) { | ||
@@ -238,0 +229,0 @@ return this.ownerModelClass |
@@ -41,8 +41,26 @@ 'use strict'; | ||
*/ | ||
OneToManyRelation.prototype.find = function (builder, $owners) { | ||
OneToManyRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
return builder | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol()) | ||
.call(this.filter); | ||
}; | ||
/** | ||
* @override | ||
* @inheritDoc | ||
*/ | ||
OneToManyRelation.prototype.find = function (builder, owners) { | ||
var self = this; | ||
var owners = this.ownerModelClass.ensureModelArray($owners); | ||
var ownerIds = _.unique(_.pluck(owners, this.ownerProp)); | ||
return this.findQuery(builder, ownerIds).runAfterModelCreate(function (related) { | ||
builder.onBuild(function (builder) { | ||
self.findQuery(builder, ownerIds); | ||
}); | ||
builder.runAfterModelCreate(function (related) { | ||
var relatedByOwnerId = _.groupBy(related, self.relatedProp); | ||
@@ -61,13 +79,18 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
OneToManyRelation.prototype.insert = function (builder, owner, insertion) { | ||
var self = this; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
_.each(insertion.models(), function (insert) { | ||
insert[self.relatedProp] = owner[self.ownerProp]; | ||
}); | ||
return builder | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol()) | ||
.call(this.filter); | ||
builder.onBuild(function (builder) { | ||
builder.$$insert(insertion); | ||
}); | ||
builder.runAfterModelCreate(function (models) { | ||
owner[self.name] = _.compact(_.flatten([owner[self.name], models])); | ||
return models; | ||
}); | ||
}; | ||
@@ -78,17 +101,10 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.insert = function (builder, $owner, $insertion) { | ||
OneToManyRelation.prototype.update = function (builder, owner, update) { | ||
var self = this; | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var insertion = this.relatedModelClass.ensureModelArray($insertion); | ||
_.each(insertion, function (insert) { | ||
insert[self.relatedProp] = owner[self.ownerProp]; | ||
builder.onBuild(function (builder) { | ||
self.findQuery(builder, owner[self.ownerProp]); | ||
builder.$$update(update); | ||
}); | ||
return this.relatedModelClass.$$insert(builder, insertion).runAfterModelCreate(function (models) { | ||
owner[self.name] = _.compact(_.flatten([owner[self.name], models])); | ||
return _.isArray($insertion) ? models : models[0]; | ||
}); | ||
}; | ||
@@ -99,11 +115,5 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.update = function (builder, $owner, $update) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
this.findQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$update(builder, $update); | ||
return builder; | ||
OneToManyRelation.prototype.patch = function (builder, owner, patch) { | ||
return this.update(builder, owner, patch); | ||
}; | ||
@@ -114,11 +124,10 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.patch = function (builder, $owner, $patch) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToManyRelation.prototype.delete = function (builder, owner) { | ||
var self = this; | ||
this.findQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$patch(builder, $patch); | ||
return builder; | ||
builder.onBuild(function (builder) { | ||
self.findQuery(builder, owner[self.ownerProp]); | ||
builder.$$delete(); | ||
}); | ||
}; | ||
@@ -129,11 +138,28 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.delete = function (builder, $owner) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToManyRelation.prototype.relate = function (builder, owner, ids) { | ||
var self = this; | ||
this.findQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$delete(builder); | ||
builder.onBuild(function (builder) { | ||
var patch = relatePatch(self, owner[self.ownerProp]); | ||
return builder; | ||
// We build the input query, but we never actually execute it. Instead we resolve it here. | ||
// 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) | ||
.resolve(ids); | ||
}); | ||
builder.runAfterModelCreatePushFront(function (input) { | ||
var patch = relatePatch(self, owner[self.ownerProp]); | ||
// Here we execute the actual update query. | ||
return self.relatedModelClass | ||
.query() | ||
.patch(patch) | ||
.whereIn(self.relatedModelClass.getFullIdColumn(), ids) | ||
.return(input); | ||
}); | ||
}; | ||
@@ -144,38 +170,41 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToManyRelation.prototype.relate = function (builder, $owner, $ids) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToManyRelation.prototype.unrelate = function (builder, owner) { | ||
var self = this; | ||
var patch = {}; | ||
patch[this.relatedProp] = owner[this.ownerProp]; | ||
builder.onBuild(function (builder) { | ||
var patch = relatePatch(self, null); | ||
return this.relatedModelClass | ||
.$$patch(builder, patch) | ||
.whereIn(this.relatedModelClass.getFullIdColumn(), _.flatten([$ids])) | ||
.runAfterModelCreate(function () { | ||
return $ids; | ||
}); | ||
// We build the input query, but we never actually execute it. Instead we resolve it here. | ||
// 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) | ||
.resolve({}); | ||
}); | ||
builder.runAfterModelCreatePushFront(function (input) { | ||
var builder = this; | ||
var patch = relatePatch(self, null); | ||
// Here we execute the actual update query. | ||
return self.relatedModelClass | ||
.query() | ||
.patch(patch) | ||
.copyFrom(builder.cloneWhereQuery()) | ||
.return(input); | ||
}); | ||
}; | ||
/** | ||
* @override | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
* @private | ||
*/ | ||
OneToManyRelation.prototype.unrelate = function (builder, $owner) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
function relatePatch(relation, value) { | ||
var patch = {}; | ||
patch[this.relatedProp] = null; | ||
patch[relation.relatedProp] = value; | ||
return patch; | ||
} | ||
return this.relatedModelClass | ||
.$$patch(builder, patch) | ||
.where(this.fullRelatedCol(), owner[this.ownerProp]) | ||
.call(this.filter) | ||
.runAfterModelCreate(function () { | ||
return {}; | ||
}); | ||
}; | ||
module.exports = OneToManyRelation; |
@@ -27,3 +27,3 @@ 'use strict'; | ||
if (_.isArray(ownerCol)) { | ||
builder.whereIn(this.fullRelatedCol(), ownerCol); | ||
builder.whereIn(this.fullRelatedCol(), _.compact(ownerCol)); | ||
} else { | ||
@@ -42,8 +42,26 @@ builder.where(this.fullRelatedCol(), ownerCol) | ||
*/ | ||
OneToOneRelation.prototype.find = function (builder, $owners) { | ||
OneToOneRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
return builder | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol()) | ||
.call(this.filter); | ||
}; | ||
/** | ||
* @override | ||
* @inheritDoc | ||
*/ | ||
OneToOneRelation.prototype.find = function (builder, owners) { | ||
var self = this; | ||
var owners = this.ownerModelClass.ensureModelArray($owners); | ||
var relatedIds = _.unique(_.compact(_.pluck(owners, this.ownerProp))); | ||
return this._makeFindQuery(builder, relatedIds).runAfterModelCreate(function (related) { | ||
builder.onBuild(function (builder) { | ||
var relatedIds = _.unique(_.compact(_.pluck(owners, self.ownerProp))); | ||
self._makeFindQuery(builder, relatedIds); | ||
}); | ||
builder.runAfterModelCreate(function (related) { | ||
var relatedById = _.indexBy(related, self.relatedProp); | ||
@@ -60,32 +78,26 @@ | ||
/** | ||
* @override | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
* Person | ||
* .query() | ||
* .update({ | ||
* age: Person.query().avg('age'), | ||
* | ||
* }) | ||
*/ | ||
OneToOneRelation.prototype.join = function (builder, joinMethod) { | ||
joinMethod = joinMethod || 'join'; | ||
var relatedTable = this.relatedModelClass.tableName; | ||
var relatedTableAlias = this.relatedTableAlias(); | ||
return builder | ||
[joinMethod](relatedTable + ' as ' + relatedTableAlias, relatedTableAlias + '.' + this.relatedCol, this.fullOwnerCol()) | ||
.call(this.filter); | ||
}; | ||
/** | ||
* @override | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.insert = function (builder, $owner, $insertion) { | ||
OneToOneRelation.prototype.insert = function (builder, owner, insertion) { | ||
var self = this; | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var insertion = this.relatedModelClass.ensureModelArray($insertion); | ||
if (insertion.length > 1) { | ||
if (insertion.models().length > 1) { | ||
throw new Error('can only insert one model to a OneToOneRelation'); | ||
} | ||
return this.relatedModelClass.$$insert(builder, insertion).runAfterModelCreate(function (inserted) { | ||
builder.onBuild(function (builder) { | ||
builder.$$insert(insertion); | ||
}); | ||
builder.runAfterModelCreate(function (inserted) { | ||
owner[self.ownerProp] = inserted[0][self.relatedProp]; | ||
@@ -96,3 +108,2 @@ owner[self.name] = inserted[0]; | ||
patch[self.ownerProp] = inserted[0][self.relatedProp]; | ||
owner[self.ownerProp] = inserted[0][self.relatedProp]; | ||
@@ -103,5 +114,3 @@ return self.ownerModelClass | ||
.where(self.ownerModelClass.getFullIdColumn(), owner.$id()) | ||
.then(function () { | ||
return _.isArray($insertion) ? inserted : inserted[0]; | ||
}); | ||
.return(inserted); | ||
}); | ||
@@ -113,11 +122,10 @@ }; | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.update = function (builder, $owner, $update) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToOneRelation.prototype.update = function (builder, owner, update) { | ||
var self = this; | ||
this._makeFindQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$update(builder, $update); | ||
return builder; | ||
builder.onBuild(function (builder) { | ||
self._makeFindQuery(builder, owner[self.ownerProp]); | ||
builder.$$update(update); | ||
}); | ||
}; | ||
@@ -128,11 +136,5 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.patch = function (builder, $owner, $patch) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
this._makeFindQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$patch(builder, $patch); | ||
return builder; | ||
OneToOneRelation.prototype.patch = function (builder, owner, patch) { | ||
return this.update(builder, owner, patch); | ||
}; | ||
@@ -143,11 +145,10 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.delete = function (builder, $owner) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToOneRelation.prototype.delete = function (builder, owner) { | ||
var self = this; | ||
this._makeFindQuery(builder, [owner[this.ownerProp]]); | ||
this.relatedModelClass.$$delete(builder); | ||
return builder; | ||
builder.onBuild(function (builder) { | ||
self._makeFindQuery(builder, owner[self.ownerProp]); | ||
builder.$$delete(); | ||
}); | ||
}; | ||
@@ -158,7 +159,5 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.relate = function (builder, $owner, $ids) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
var ids = _.flatten([$ids]); | ||
OneToOneRelation.prototype.relate = function (builder, owner, ids) { | ||
var self = this; | ||
@@ -169,13 +168,30 @@ if (ids.length > 1) { | ||
var patch = {}; | ||
patch[this.ownerProp] = ids[0]; | ||
owner[this.ownerProp] = ids[0]; | ||
builder.onBuild(function (builder) { | ||
var patch = {}; | ||
patch[self.ownerProp] = ids[0]; | ||
owner[self.ownerProp] = ids[0]; | ||
return this.ownerModelClass | ||
.$$patch(builder, patch) | ||
.from(this.ownerModelClass.tableName) | ||
.where(this.ownerModelClass.getFullIdColumn(), owner.$id()) | ||
.runAfterModelCreate(function () { | ||
return $ids; | ||
}); | ||
// We build the input query, but we never actually execute it. Instead we resolve it here. | ||
// 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) | ||
.resolve({}); | ||
}); | ||
builder.runAfterModelCreatePushFront(function (input) { | ||
var builder = this; | ||
var patch = {}; | ||
patch[self.ownerProp] = ids[0]; | ||
// Here we execute the actual update query. | ||
return self.ownerModelClass | ||
.query() | ||
.patch(patch) | ||
.copyFrom(builder.cloneWhereQuery()) | ||
.return(input); | ||
}); | ||
}; | ||
@@ -186,18 +202,34 @@ | ||
* @inheritDoc | ||
* @returns {QueryBuilder} | ||
*/ | ||
OneToOneRelation.prototype.unrelate = function (builder, $owner) { | ||
var owner = this.ownerModelClass.ensureModel($owner); | ||
OneToOneRelation.prototype.unrelate = function (builder, owner) { | ||
var self = this; | ||
var patch = {}; | ||
patch[this.ownerProp] = null; | ||
owner[this.ownerProp] = null; | ||
builder.onBuild(function (builder) { | ||
var patch = {}; | ||
patch[self.ownerProp] = null; | ||
owner[self.ownerProp] = null; | ||
return this.ownerModelClass | ||
.$$patch(builder, patch) | ||
.from(this.ownerModelClass.tableName) | ||
.where(this.ownerModelClass.getFullIdColumn(), owner.$id()) | ||
.runAfterModelCreate(function () { | ||
return {}; | ||
}); | ||
// We build the input query, but we never actually execute it. Instead we resolve it here. | ||
// 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) | ||
.resolve({}); | ||
}); | ||
builder.runAfterModelCreatePushFront(function (input) { | ||
var builder = this; | ||
var patch = {}; | ||
patch[self.ownerProp] = null; | ||
// Here we execute the actual update query. | ||
return self.ownerModelClass | ||
.query() | ||
.patch(patch) | ||
.copyFrom(builder.cloneWhereQuery()) | ||
.return(input); | ||
}); | ||
}; | ||
@@ -209,5 +241,3 @@ | ||
OneToOneRelation.prototype._makeFindQuery = function (builder, relatedIds) { | ||
relatedIds = _.compact(relatedIds); | ||
if (_.isEmpty(relatedIds)) { | ||
if ((_.isArray(relatedIds) && _.isEmpty(relatedIds)) || !relatedIds) { | ||
return builder.resolve([]); | ||
@@ -214,0 +244,0 @@ } else { |
@@ -416,6 +416,6 @@ 'use strict'; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object|Array.<Model>|Array.<Object>} $owners | ||
* @param {string} joinMethod | ||
* @returns {QueryBuilder} | ||
*/ | ||
Relation.prototype.find = function (builder, $owners) { | ||
Relation.prototype.join = function (builder, joinMethod) { | ||
throw new Error('not implemented'); | ||
@@ -428,6 +428,5 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {string} joinMethod | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object|Array.<Model>|Array.<Object>} owners | ||
*/ | ||
Relation.prototype.join = function (builder, joinMethod) { | ||
Relation.prototype.find = function (builder, owners) { | ||
throw new Error('not implemented'); | ||
@@ -439,7 +438,6 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @param {Model|Object|Array.<Model>|Array.<Object>} $insertion | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object} owner | ||
* @param {InsertionOrUpdate} insertion | ||
*/ | ||
Relation.prototype.insert = function (builder, $owner, $insertion) { | ||
Relation.prototype.insert = function (builder, owner, insertion) { | ||
throw new Error('not implemented'); | ||
@@ -452,7 +450,6 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @param {Model|Object} $update | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object} owner | ||
* @param {InsertionOrUpdate} update | ||
*/ | ||
Relation.prototype.update = function (builder, $owner, $update) { | ||
Relation.prototype.update = function (builder, owner, update) { | ||
return builder; | ||
@@ -465,7 +462,6 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @param {Model|Object} $patch | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object} owner | ||
* @param {InsertionOrUpdate} patch | ||
*/ | ||
Relation.prototype.patch = function (builder, $owner, $patch) { | ||
Relation.prototype.patch = function (builder, owner, patch) { | ||
throw new Error('not implemented'); | ||
@@ -478,6 +474,5 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object} owner | ||
*/ | ||
Relation.prototype.delete = function (builder, $owner) { | ||
Relation.prototype.delete = function (builder, owner) { | ||
throw new Error('not implemented'); | ||
@@ -490,7 +485,6 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @param {Model|Object} owner | ||
* @param {number|string|Array.<number>|Array.<string>} ids | ||
* @returns {QueryBuilder} | ||
*/ | ||
Relation.prototype.relate = function (builder, $owner, ids) { | ||
Relation.prototype.relate = function (builder, owner, ids) { | ||
throw new Error('not implemented'); | ||
@@ -503,6 +497,5 @@ }; | ||
* @param {QueryBuilder} builder | ||
* @param {Model|Object} $owner | ||
* @returns {QueryBuilder} | ||
* @param {Model|Object} owner | ||
*/ | ||
Relation.prototype.unrelate = function (builder, $owner) { | ||
Relation.prototype.unrelate = function (builder, owner) { | ||
throw new Error('not implemented'); | ||
@@ -509,0 +502,0 @@ }; |
{ | ||
"name": "objection", | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"description": "An SQL-friendly ORM for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "objection.js", |
@@ -653,2 +653,8 @@ [![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) | ||
## 0.2.1 | ||
### What's new | ||
* bugfix: Chaining `insert` with `returning` now returns all listed columns. | ||
## 0.2.0 | ||
@@ -655,0 +661,0 @@ |
Sorry, the diff of this file is too big to display
219896
20
6517
681