Merge branch 'walkonsocial-merge-inclusion'
This commit is contained in:
commit
844ee17306
|
@ -60,7 +60,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
||||||
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
||||||
|| actualRefresh) {
|
|| actualRefresh) {
|
||||||
// It either doesn't hit the cache or refresh is required
|
// It either doesn't hit the cache or refresh is required
|
||||||
var params = mergeQuery(actualCond, scopeParams);
|
var params = mergeQuery(actualCond, scopeParams, {nestedInclude: true});
|
||||||
var targetModel = this.targetModel(receiver);
|
var targetModel = this.targetModel(receiver);
|
||||||
targetModel.find(params, function (err, data) {
|
targetModel.find(params, function (err, data) {
|
||||||
if (!err && saveOnCache) {
|
if (!err && saveOnCache) {
|
||||||
|
|
98
lib/utils.js
98
lib/utils.js
|
@ -9,6 +9,7 @@ exports.defineCachedRelations = defineCachedRelations;
|
||||||
exports.sortObjectsByIds = sortObjectsByIds;
|
exports.sortObjectsByIds = sortObjectsByIds;
|
||||||
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
|
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
|
||||||
exports.mergeQuery = mergeQuery;
|
exports.mergeQuery = mergeQuery;
|
||||||
|
exports.mergeIncludes = mergeIncludes;
|
||||||
exports.createPromiseCallback = createPromiseCallback
|
exports.createPromiseCallback = createPromiseCallback
|
||||||
|
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
@ -53,6 +54,87 @@ function setScopeValuesFromWhere(data, where, targetModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge include options of default scope with runtime include option.
|
||||||
|
* exhibits the _.extend behaviour. Property value of source overrides
|
||||||
|
* property value of destination if property name collision occurs
|
||||||
|
* @param {String|Array|Object} destination The default value of `include` option
|
||||||
|
* @param {String|Array|Object} source The runtime value of `include` option
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function mergeIncludes(destination, source) {
|
||||||
|
var destArray = convertToArray(destination);
|
||||||
|
var sourceArray = convertToArray(source);
|
||||||
|
if (destArray.length === 0) {
|
||||||
|
return sourceArray;
|
||||||
|
}
|
||||||
|
if (sourceArray.length === 0) {
|
||||||
|
return destArray;
|
||||||
|
}
|
||||||
|
var relationNames = [];
|
||||||
|
var resultArray = [];
|
||||||
|
for (var j in sourceArray) {
|
||||||
|
var sourceEntry = sourceArray[j];
|
||||||
|
var sourceEntryRelationName = (typeof (sourceEntry.rel || sourceEntry.relation) === 'string') ?
|
||||||
|
sourceEntry.relation : Object.keys(sourceEntry)[0];
|
||||||
|
relationNames.push(sourceEntryRelationName);
|
||||||
|
resultArray.push(sourceEntry);
|
||||||
|
}
|
||||||
|
for (var i in destArray) {
|
||||||
|
var destEntry = destArray[i];
|
||||||
|
var destEntryRelationName = (typeof (destEntry.rel || destEntry.relation) === 'string') ?
|
||||||
|
destEntry.relation : Object.keys(destEntry)[0];
|
||||||
|
if (relationNames.indexOf(destEntryRelationName) === -1) {
|
||||||
|
resultArray.push(destEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts input parameter into array of objects which wraps the value.
|
||||||
|
* "someValue" is converted to [{"someValue":true}]
|
||||||
|
* ["someValue"] is converted to [{"someValue":true}]
|
||||||
|
* {"someValue":true} is converted to [{"someValue":true}]
|
||||||
|
* @param {String|Array|Object} param - Input parameter to be converted
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
function convertToArray(include) {
|
||||||
|
if (typeof include === 'string') {
|
||||||
|
var obj = {};
|
||||||
|
obj[include] = true;
|
||||||
|
return [obj];
|
||||||
|
} else if (isPlainObject(include)) {
|
||||||
|
//if include is of the form - {relation:'',scope:''}
|
||||||
|
if (include.rel || include.relation) {
|
||||||
|
return [include];
|
||||||
|
}
|
||||||
|
// Build an array of key/value pairs
|
||||||
|
var newInclude = [];
|
||||||
|
for (var key in include) {
|
||||||
|
var obj = {};
|
||||||
|
obj[key] = include[key];
|
||||||
|
newInclude.push(obj);
|
||||||
|
}
|
||||||
|
return newInclude;
|
||||||
|
} else if (Array.isArray(include)) {
|
||||||
|
var normalized = [];
|
||||||
|
for (var i in include) {
|
||||||
|
var includeEntry = include[i];
|
||||||
|
if (typeof includeEntry === 'string') {
|
||||||
|
var obj = {};
|
||||||
|
obj[includeEntry] = true;
|
||||||
|
normalized.push(obj)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
normalized.push(includeEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Merge query parameters
|
* Merge query parameters
|
||||||
* @param {Object} base The base object to contain the merged results
|
* @param {Object} base The base object to contain the merged results
|
||||||
|
@ -81,9 +163,19 @@ function mergeQuery(base, update, spec) {
|
||||||
if (!base.include) {
|
if (!base.include) {
|
||||||
base.include = update.include;
|
base.include = update.include;
|
||||||
} else {
|
} else {
|
||||||
var saved = base.include;
|
if (spec.nestedInclude === true){
|
||||||
base.include = {};
|
//specify nestedInclude=true to force nesting of inclusions on scoped
|
||||||
base.include[update.include] = saved;
|
//queries. e.g. In physician.patients.getAsync({include: 'address'}),
|
||||||
|
//inclusion should be on patient model, not on physician model.
|
||||||
|
var saved = base.include;
|
||||||
|
base.include = {};
|
||||||
|
base.include[update.include] = saved;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//default behaviour of inclusion merge - merge inclusions at the same
|
||||||
|
//level. - https://github.com/strongloop/loopback-datasource-juggler/pull/569#issuecomment-95310874
|
||||||
|
base.include = mergeIncludes(base.include, update.include);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ var utils = require('../lib/utils');
|
||||||
var fieldsToArray = utils.fieldsToArray;
|
var fieldsToArray = utils.fieldsToArray;
|
||||||
var removeUndefined = utils.removeUndefined;
|
var removeUndefined = utils.removeUndefined;
|
||||||
var mergeSettings = utils.mergeSettings;
|
var mergeSettings = utils.mergeSettings;
|
||||||
|
var mergeIncludes = utils.mergeIncludes;
|
||||||
var sortObjectsByIds = utils.sortObjectsByIds;
|
var sortObjectsByIds = utils.sortObjectsByIds;
|
||||||
|
|
||||||
describe('util.fieldsToArray', function () {
|
describe('util.fieldsToArray', function () {
|
||||||
|
@ -218,3 +219,146 @@ describe('sortObjectsByIds', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('util.mergeIncludes', function () {
|
||||||
|
|
||||||
|
function checkInputOutput(baseInclude, updateInclude, expectedInclude) {
|
||||||
|
var mergedInclude = mergeIncludes(baseInclude, updateInclude);
|
||||||
|
should.deepEqual(mergedInclude, expectedInclude,
|
||||||
|
'Merged include should match the expectation');
|
||||||
|
}
|
||||||
|
|
||||||
|
it('Merge string values to object', function () {
|
||||||
|
var baseInclude = 'relation1';
|
||||||
|
var updateInclude = 'relation2';
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: true},
|
||||||
|
{relation1: true}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge string & array values to object', function () {
|
||||||
|
var baseInclude = 'relation1';
|
||||||
|
var updateInclude = ['relation2'];
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: true},
|
||||||
|
{relation1: true}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge string & object values to object', function () {
|
||||||
|
var baseInclude = ['relation1'];
|
||||||
|
var updateInclude = {relation2: 'relation2Include'};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: 'relation2Include'},
|
||||||
|
{relation1: true}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge array & array values to object', function () {
|
||||||
|
var baseInclude = ['relation1'];
|
||||||
|
var updateInclude = ['relation2'];
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: true},
|
||||||
|
{relation1: true}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge array & object values to object', function () {
|
||||||
|
var baseInclude = ['relation1'];
|
||||||
|
var updateInclude = {relation2: 'relation2Include'};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: 'relation2Include'},
|
||||||
|
{relation1: true}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge object & object values to object', function () {
|
||||||
|
var baseInclude = {relation1: 'relation1Include'};
|
||||||
|
var updateInclude = {relation2: 'relation2Include'};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation2: 'relation2Include'},
|
||||||
|
{relation1: 'relation1Include'}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Override property collision with update value', function () {
|
||||||
|
var baseInclude = {relation1: 'baseValue'};
|
||||||
|
var updateInclude = {relation1: 'updateValue'};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation1: 'updateValue'}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge string includes & include with relation syntax properly',
|
||||||
|
function () {
|
||||||
|
var baseInclude = 'relation1';
|
||||||
|
var updateInclude = {relation: 'relation1'};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation: 'relation1'}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge string includes & include with scope properly', function () {
|
||||||
|
var baseInclude = 'relation1';
|
||||||
|
var updateInclude = {
|
||||||
|
relation: 'relation1',
|
||||||
|
scope: {include: 'relation2'}
|
||||||
|
};
|
||||||
|
var expectedInclude = [
|
||||||
|
{relation: 'relation1', scope: {include: 'relation2'}}
|
||||||
|
];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge includes with and without relation syntax properly',
|
||||||
|
function () {
|
||||||
|
//w & w/o relation syntax - no collision
|
||||||
|
var baseInclude = ['relation2'];
|
||||||
|
var updateInclude = {
|
||||||
|
relation: 'relation1',
|
||||||
|
scope: {include: 'relation2'}
|
||||||
|
};
|
||||||
|
var expectedInclude = [{
|
||||||
|
relation: 'relation1',
|
||||||
|
scope: {include: 'relation2'}
|
||||||
|
}, {relation2: true}];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
|
||||||
|
//w & w/o relation syntax - collision
|
||||||
|
baseInclude = ['relation1'];
|
||||||
|
updateInclude = {relation: 'relation1', scope: {include: 'relation2'}};
|
||||||
|
expectedInclude =
|
||||||
|
[{relation: 'relation1', scope: {include: 'relation2'}}];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
|
||||||
|
//w & w/o relation syntax - collision
|
||||||
|
baseInclude = {relation: 'relation1', scope: {include: 'relation2'}};
|
||||||
|
updateInclude = ['relation1'];
|
||||||
|
expectedInclude = [{relation1: true}];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Merge includes with mixture of strings, arrays & objects properly', function () {
|
||||||
|
var baseInclude = ['relation1', {relation2: true},
|
||||||
|
{relation: 'relation3', scope: {where: {id: 'some id'}}},
|
||||||
|
{relation: 'relation5', scope: {where: {id: 'some id'}}}
|
||||||
|
];
|
||||||
|
var updateInclude = ['relation4', {relation3: true},
|
||||||
|
{relation: 'relation2', scope: {where: {id: 'some id'}}}];
|
||||||
|
var expectedInclude = [{relation4: true}, {relation3: true},
|
||||||
|
{relation: 'relation2', scope: {where: {id: 'some id'}}},
|
||||||
|
{relation1: true},
|
||||||
|
{relation: 'relation5', scope: {where: {id: 'some id'}}}];
|
||||||
|
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue