Polymorphic lookup from all registered dataSources
Polymorphic model lookup was previously limited to the same dataSource as the modelFrom model, which turns out to be too restrictive. This was uncovered by the use of a Transient model, not being able to lookup a PersistedModel.
This commit is contained in:
parent
07dbbd4224
commit
2c0ffee2d3
6
index.js
6
index.js
|
@ -4,6 +4,12 @@ exports.ModelBaseClass = require('./lib/model.js');
|
||||||
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
||||||
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
||||||
|
|
||||||
|
var dataSources = exports.dataSources = {};
|
||||||
|
|
||||||
|
exports.registerDataSource = function(ds) {
|
||||||
|
dataSources[ds.name] = ds;
|
||||||
|
};
|
||||||
|
|
||||||
exports.__defineGetter__('version', function () {
|
exports.__defineGetter__('version', function () {
|
||||||
return require('./package.json').version;
|
return require('./package.json').version;
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var i8n = require('inflection');
|
var i8n = require('inflection');
|
||||||
|
var jdb = require('../');
|
||||||
var defineScope = require('./scope.js').defineScope;
|
var defineScope = require('./scope.js').defineScope;
|
||||||
var mergeQuery = require('./scope.js').mergeQuery;
|
var mergeQuery = require('./scope.js').mergeQuery;
|
||||||
var ModelBaseClass = require('./model.js');
|
var ModelBaseClass = require('./model.js');
|
||||||
|
@ -466,7 +467,7 @@ function findBelongsTo(modelFrom, modelTo, keyTo) {
|
||||||
* @param {String} modelName The model name
|
* @param {String} modelName The model name
|
||||||
* @returns {*} The matching model class
|
* @returns {*} The matching model class
|
||||||
*/
|
*/
|
||||||
function lookupModel(models, modelName) {
|
function lookupModel(models, modelName, internal) {
|
||||||
if(models[modelName]) {
|
if(models[modelName]) {
|
||||||
return models[modelName];
|
return models[modelName];
|
||||||
}
|
}
|
||||||
|
@ -476,6 +477,15 @@ function lookupModel(models, modelName) {
|
||||||
return models[name];
|
return models[name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (internal) return;
|
||||||
|
|
||||||
|
var keys = Object.keys(jdb.dataSources);
|
||||||
|
for (var k = 0; k < keys.length; k++) {
|
||||||
|
var ds = jdb.dataSources[keys[k]];
|
||||||
|
var models = ds.modelBuilder.models;
|
||||||
|
var model = lookupModel(models, modelName, true);
|
||||||
|
if (model) return model;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
||||||
|
@ -503,9 +513,9 @@ function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
||||||
* @param {Object|String} params Name of the polymorphic relation or params
|
* @param {Object|String} params Name of the polymorphic relation or params
|
||||||
* @returns {Object} The normalized parameters
|
* @returns {Object} The normalized parameters
|
||||||
*/
|
*/
|
||||||
function polymorphicParams(params) {
|
function polymorphicParams(params, as) {
|
||||||
if (typeof params === 'string') params = { as: params };
|
if (typeof params === 'string') params = { as: params };
|
||||||
if (typeof params.as !== 'string') params.as = 'reference'; // default
|
if (typeof params.as !== 'string') params.as = as || 'reference'; // default
|
||||||
params.foreignKey = params.foreignKey || i8n.camelize(params.as + '_id', true);
|
params.foreignKey = params.foreignKey || i8n.camelize(params.as + '_id', true);
|
||||||
params.discriminator = params.discriminator || i8n.camelize(params.as + '_type', true);
|
params.discriminator = params.discriminator || i8n.camelize(params.as + '_type', true);
|
||||||
return params;
|
return params;
|
||||||
|
@ -1079,21 +1089,23 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
|
||||||
|
|
||||||
var idName, relationName, fk;
|
var idName, relationName, fk;
|
||||||
if (params.polymorphic) {
|
if (params.polymorphic) {
|
||||||
|
relationName = params.as || (typeof modelTo === 'string' ? modelTo : null); // initially
|
||||||
|
|
||||||
if (params.polymorphic === true) {
|
if (params.polymorphic === true) {
|
||||||
// modelTo arg will be the name of the polymorphic relation (string)
|
// modelTo arg will be the name of the polymorphic relation (string)
|
||||||
polymorphic = polymorphicParams(modelTo);
|
polymorphic = polymorphicParams(modelTo, relationName);
|
||||||
} else {
|
} else {
|
||||||
polymorphic = polymorphicParams(params.polymorphic);
|
polymorphic = polymorphicParams(params.polymorphic, relationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
modelTo = null; // will lookup dynamically
|
modelTo = null; // will lookup dynamically
|
||||||
|
|
||||||
idName = params.idName || 'id';
|
idName = params.idName || 'id';
|
||||||
relationName = params.as || polymorphic.as;
|
relationName = params.as || polymorphic.as; // finally
|
||||||
fk = polymorphic.foreignKey;
|
fk = polymorphic.foreignKey;
|
||||||
discriminator = polymorphic.discriminator;
|
discriminator = polymorphic.discriminator;
|
||||||
|
|
||||||
if (typeof polymorphic.idType === 'string') { // explicit key type
|
if (polymorphic.idType) { // explicit key type
|
||||||
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true });
|
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true });
|
||||||
} else { // try to use the same foreign key type as modelFrom
|
} else { // try to use the same foreign key type as modelFrom
|
||||||
modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName);
|
modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName);
|
||||||
|
@ -2208,10 +2220,7 @@ EmbedsMany.prototype.build = function(targetModelData) {
|
||||||
|
|
||||||
var assignId = (forceId || targetModelData[pk] === undefined);
|
var assignId = (forceId || targetModelData[pk] === undefined);
|
||||||
|
|
||||||
if (assignId && typeof connector.generateId === 'function') {
|
if (assignId && pkType === Number) {
|
||||||
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
|
|
||||||
targetModelData[pk] = id;
|
|
||||||
} else if (assignId && pkType === Number) {
|
|
||||||
var ids = embeddedList.map(function(m) {
|
var ids = embeddedList.map(function(m) {
|
||||||
return (typeof m[pk] === 'number' ? m[pk] : 0);
|
return (typeof m[pk] === 'number' ? m[pk] : 0);
|
||||||
});
|
});
|
||||||
|
@ -2220,6 +2229,9 @@ EmbedsMany.prototype.build = function(targetModelData) {
|
||||||
} else {
|
} else {
|
||||||
targetModelData[pk] = 1;
|
targetModelData[pk] = 1;
|
||||||
}
|
}
|
||||||
|
} else if (assignId && typeof connector.generateId === 'function') {
|
||||||
|
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
|
||||||
|
targetModelData[pk] = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.definition.applyProperties(modelInstance, targetModelData);
|
this.definition.applyProperties(modelInstance, targetModelData);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// This test written in mocha+should.js
|
// This test written in mocha+should.js
|
||||||
var should = require('./init.js');
|
var should = require('./init.js');
|
||||||
var Schema = require('../').Schema;
|
var jdb = require('../');
|
||||||
|
var Schema = jdb.Schema;
|
||||||
|
|
||||||
var db, tmp, Book, Chapter, Author, Reader;
|
var db, tmp, Book, Chapter, Author, Reader;
|
||||||
var Category, Job;
|
var Category, Job;
|
||||||
|
@ -2420,11 +2421,20 @@ describe('relations', function () {
|
||||||
|
|
||||||
before(function (done) {
|
before(function (done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
|
tmp = getTransientSchema();
|
||||||
|
|
||||||
|
// register here, so transient models
|
||||||
|
// can lookup related models (polymorphic)
|
||||||
|
jdb.registerDataSource(db);
|
||||||
|
|
||||||
Book = db.define('Book', {name: String});
|
Book = db.define('Book', {name: String});
|
||||||
Author = db.define('Author', {name: String});
|
Author = db.define('Author', {name: String});
|
||||||
Reader = db.define('Reader', {name: String});
|
Reader = db.define('Reader', {name: String});
|
||||||
|
|
||||||
Link = db.define('Link', {name: String, notes: String}); // generic model
|
Link = tmp.define('Link', {
|
||||||
|
id: {type: Number, id: true},
|
||||||
|
name: String, notes: String
|
||||||
|
}); // generic model
|
||||||
Link.validatesPresenceOf('linkedId');
|
Link.validatesPresenceOf('linkedId');
|
||||||
Link.validatesPresenceOf('linkedType');
|
Link.validatesPresenceOf('linkedType');
|
||||||
|
|
||||||
|
@ -2438,13 +2448,15 @@ describe('relations', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be declared', function (done) {
|
it('can be declared', function (done) {
|
||||||
|
var idType = db.connector.getDefaultIdType();
|
||||||
|
|
||||||
Book.embedsMany(Link, { as: 'people',
|
Book.embedsMany(Link, { as: 'people',
|
||||||
polymorphic: 'linked',
|
polymorphic: 'linked',
|
||||||
scope: { include: 'linked' }
|
scope: { include: 'linked' }
|
||||||
});
|
});
|
||||||
Link.belongsTo('linked', {
|
Link.belongsTo('linked', {
|
||||||
polymorphic: true, // needs unique auto-id
|
polymorphic: { idType: idType }, // native type
|
||||||
properties: { name: 'name' }, // denormalized
|
properties: { name: 'name' }, // denormalized
|
||||||
options: { invertProperties: true }
|
options: { invertProperties: true }
|
||||||
});
|
});
|
||||||
db.automigrate(done);
|
db.automigrate(done);
|
||||||
|
|
Loading…
Reference in New Issue