Extract mergeQuery and setScopeValuesFromWhere

Related to #274 - in preparation of default scope
This commit is contained in:
Fabien Franzen 2014-09-06 11:13:47 +02:00
parent d3b30c3071
commit 610866bd7c
6 changed files with 129 additions and 93 deletions

View File

@ -13,11 +13,12 @@ var Relation = require('./relations.js');
var Inclusion = require('./include.js'); var Inclusion = require('./include.js');
var List = require('./list.js'); var List = require('./list.js');
var geo = require('./geo'); var geo = require('./geo');
var mergeQuery = require('./scope.js').mergeQuery;
var Memory = require('./connectors/memory').Memory; var Memory = require('./connectors/memory').Memory;
var utils = require('./utils'); var utils = require('./utils');
var fieldsToArray = utils.fieldsToArray; var fieldsToArray = utils.fieldsToArray;
var removeUndefined = utils.removeUndefined; var removeUndefined = utils.removeUndefined;
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
var mergeQuery = utils.mergeQuery;
var util = require('util'); var util = require('util');
var assert = require('assert'); var assert = require('assert');
@ -69,6 +70,18 @@ DataAccessObject._forDB = function (data) {
return res; return res;
}; };
DataAccessObject.applyProperties = function(data) {
var scope = this.definition.settings.scope || {};
if (typeof scope.where === 'object'
&& this.definition.settings.applyProperties !== false) {
setScopeValuesFromWhere(data, scope.where, this);
}
};
DataAccessObject.applyScope = function(cond) {
};
/** /**
* Create an instance of Model with given data and save to the attached data source. Callback is optional. * Create an instance of Model with given data and save to the attached data source. Callback is optional.
* Example: * Example:
@ -136,6 +149,7 @@ DataAccessObject.create = function (data, callback) {
} }
} }
var enforced = {};
var obj; var obj;
var idValue = getIdValue(this, data); var idValue = getIdValue(this, data);
@ -145,6 +159,10 @@ DataAccessObject.create = function (data, callback) {
} else { } else {
obj = new Model(data); obj = new Model(data);
} }
this.applyProperties(enforced);
obj.setAttributes(enforced);
data = obj.toObject(true); data = obj.toObject(true);
// validation required // validation required
@ -220,6 +238,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
inst = new Model(data); inst = new Model(data);
} }
update = inst.toObject(false); update = inst.toObject(false);
this.applyProperties(update);
update = removeUndefined(update); update = removeUndefined(update);
this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) { this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
var obj; var obj;
@ -931,8 +950,12 @@ DataAccessObject.prototype.save = function (options, callback) {
var data = inst.toObject(true); var data = inst.toObject(true);
var modelName = Model.modelName; var modelName = Model.modelName;
Model.applyProperties(data, this);
if (this.isNewRecord()) { if (this.isNewRecord()) {
return Model.create(this, callback); return Model.create(this, callback);
} else {
inst.setAttributes(data);
} }
// validate first // validate first
@ -1025,6 +1048,9 @@ DataAccessObject.updateAll = function (where, data, cb) {
cb && cb(err); cb && cb(err);
}); });
} }
this.applyProperties(data);
var connector = this.getDataSource().connector; var connector = this.getDataSource().connector;
connector.update(this.modelName, where, data, cb); connector.update(this.modelName, where, data, cb);
}; };
@ -1075,7 +1101,7 @@ DataAccessObject.prototype.remove =
* @param {Mixed} value Value of property * @param {Mixed} value Value of property
*/ */
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) { DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
this[name] = value; this[name] = value; // TODO [fabien] - currently not protected by applyProperties
}; };
/** /**
@ -1101,6 +1127,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
DataAccessObject.prototype.setAttributes = function setAttributes(data) { DataAccessObject.prototype.setAttributes = function setAttributes(data) {
if (typeof data !== 'object') return; if (typeof data !== 'object') return;
this.constructor.applyProperties(data);
var Model = this.constructor; var Model = this.constructor;
var inst = this; var inst = this;

View File

@ -61,6 +61,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
var properties = _extend({}, ctor.definition.properties); var properties = _extend({}, ctor.definition.properties);
data = data || {}; data = data || {};
if (typeof ctor.applyProperties === 'function') {
ctor.applyProperties(data);
}
options = options || {}; options = options || {};
var applySetters = options.applySetters; var applySetters = options.applySetters;
var strict = options.strict; var strict = options.strict;

View File

@ -3,9 +3,10 @@
*/ */
var assert = require('assert'); var assert = require('assert');
var util = require('util'); var util = require('util');
var utils = require('./utils');
var i8n = require('inflection'); var i8n = require('inflection');
var defineScope = require('./scope.js').defineScope; var defineScope = require('./scope.js').defineScope;
var mergeQuery = require('./scope.js').mergeQuery; var mergeQuery = utils.mergeQuery;
var ModelBaseClass = require('./model.js'); var ModelBaseClass = require('./model.js');
var applyFilter = require('./connectors/memory').applyFilter; var applyFilter = require('./connectors/memory').applyFilter;
var ValidationError = require('./validations.js').ValidationError; var ValidationError = require('./validations.js').ValidationError;

View File

@ -1,13 +1,14 @@
var i8n = require('inflection'); var i8n = require('inflection');
var utils = require('./utils'); var utils = require('./utils');
var defineCachedRelations = utils.defineCachedRelations; var defineCachedRelations = utils.defineCachedRelations;
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
var mergeQuery = utils.mergeQuery;
var DefaultModelBaseClass = require('./model.js'); var DefaultModelBaseClass = require('./model.js');
/** /**
* Module exports * Module exports
*/ */
exports.defineScope = defineScope; exports.defineScope = defineScope;
exports.mergeQuery = mergeQuery;
function ScopeDefinition(definition) { function ScopeDefinition(definition) {
this.isStatic = definition.isStatic; this.isStatic = definition.isStatic;
@ -229,35 +230,6 @@ function defineScope(cls, targetClass, name, params, methods, options) {
cls['__count__' + name] = fn_count; cls['__count__' + name] = fn_count;
/*
* Extracting fixed property values for the scope from the where clause into
* the data object
*
* @param {Object} The data object
* @param {Object} The where clause
*/
function setScopeValuesFromWhere(data, where, targetModel) {
for (var i in where) {
if (i === 'and') {
// Find fixed property values from each subclauses
for (var w = 0, n = where[i].length; w < n; w++) {
setScopeValuesFromWhere(data, where[i][w], targetModel);
}
continue;
}
var prop = targetModel.definition.properties[i];
if (prop) {
var val = where[i];
if (typeof val !== 'object' || val instanceof prop.type
|| prop.type.name === 'ObjectID') // MongoDB key
{
// Only pick the {propertyName: propertyValue}
data[i] = where[i];
}
}
}
}
// and it should have create/build methods with binded thisModelNameId param // and it should have create/build methods with binded thisModelNameId param
function build(data) { function build(data) {
data = data || {}; data = data || {};
@ -300,60 +272,3 @@ function defineScope(cls, targetClass, name, params, methods, options) {
return definition; return definition;
} }
/*!
* Merge query parameters
* @param {Object} base The base object to contain the merged results
* @param {Object} update The object containing updates to be merged
* @returns {*|Object} The base object
* @private
*/
function mergeQuery(base, update) {
if (!update) {
return;
}
base = base || {};
if (update.where && Object.keys(update.where).length > 0) {
if (base.where && Object.keys(base.where).length > 0) {
base.where = {and: [base.where, update.where]};
} else {
base.where = update.where;
}
}
// Merge inclusion
if (update.include) {
if (!base.include) {
base.include = update.include;
} else {
var saved = base.include;
base.include = {};
base.include[update.include] = saved;
}
}
if (update.collect) {
base.collect = update.collect;
}
// set order
if (!base.order && update.order) {
base.order = update.order;
}
// overwrite pagination
if (update.limit !== undefined) {
base.limit = update.limit;
}
if (update.skip !== undefined) {
base.skip = update.skip;
}
if (update.offset !== undefined) {
base.offset = update.offset;
}
// Overwrite fields
if (update.fields !== undefined) {
base.fields = update.fields;
}
return base;
}

View File

@ -7,6 +7,8 @@ exports.mergeSettings = mergeSettings;
exports.isPlainObject = isPlainObject; exports.isPlainObject = isPlainObject;
exports.defineCachedRelations = defineCachedRelations; exports.defineCachedRelations = defineCachedRelations;
exports.sortObjectsByIds = sortObjectsByIds; exports.sortObjectsByIds = sortObjectsByIds;
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
exports.mergeQuery = mergeQuery;
var traverse = require('traverse'); var traverse = require('traverse');
@ -21,6 +23,92 @@ function safeRequire(module) {
} }
} }
/*
* Extracting fixed property values for the scope from the where clause into
* the data object
*
* @param {Object} The data object
* @param {Object} The where clause
*/
function setScopeValuesFromWhere(data, where, targetModel) {
for (var i in where) {
if (i === 'and') {
// Find fixed property values from each subclauses
for (var w = 0, n = where[i].length; w < n; w++) {
setScopeValuesFromWhere(data, where[i][w], targetModel);
}
continue;
}
var prop = targetModel.definition.properties[i];
if (prop) {
var val = where[i];
if (typeof val !== 'object' || val instanceof prop.type
|| prop.type.name === 'ObjectID') // MongoDB key
{
// Only pick the {propertyName: propertyValue}
data[i] = where[i];
}
}
}
}
/*!
* Merge query parameters
* @param {Object} base The base object to contain the merged results
* @param {Object} update The object containing updates to be merged
* @returns {*|Object} The base object
* @private
*/
function mergeQuery(base, update) {
if (!update) {
return;
}
base = base || {};
if (update.where && Object.keys(update.where).length > 0) {
if (base.where && Object.keys(base.where).length > 0) {
base.where = {and: [base.where, update.where]};
} else {
base.where = update.where;
}
}
// Merge inclusion
if (update.include) {
if (!base.include) {
base.include = update.include;
} else {
var saved = base.include;
base.include = {};
base.include[update.include] = saved;
}
}
if (update.collect) {
base.collect = update.collect;
}
// set order
if (!base.order && update.order) {
base.order = update.order;
}
// overwrite pagination
if (update.limit !== undefined) {
base.limit = update.limit;
}
if (update.skip !== undefined) {
base.skip = update.skip;
}
if (update.offset !== undefined) {
base.offset = update.offset;
}
// Overwrite fields
if (update.fields !== undefined) {
base.fields = update.fields;
}
return base;
}
function fieldsToArray(fields, properties) { function fieldsToArray(fields, properties) {
if (!fields) return; if (!fields) return;