Merge branch 'master' of github.com:strongloop/jugglingdb

This commit is contained in:
Ritchie 2013-05-31 10:28:05 -07:00
commit 1bb8047b57
5 changed files with 190 additions and 88 deletions

View File

@ -16,7 +16,7 @@ function loadSchemasSync(schemaFile, dataSource) {
// Read the schema JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return DataSource.buildModels(dataSource, schemas);
return dataSource.buildModels(schemas);
}

View File

@ -415,89 +415,161 @@ function fromDBName(dbName, camelCase) {
return parts.join('');
}
DataSource.prototype.discoverSchema = function (owner, table, cb) {
this.discoverSchemas(owner, table, {visited: {}, associations: false}, function(err, schemas) {
if(err) {
cb && cb(err, schemas);
return;
}
for(var s in schemas) {
cb && cb(null, schemas[s]);
return;
}
});
}
/**
* Discover schema from a given table/view
* @param owner
* @param table
* @param cb
*/
DataSource.prototype.discoverSchema = function (owner, table, cb) {
DataSource.prototype.discoverSchemas = function (owner, table, options, cb) {
var self = this;
var dataSourceName = this.name || this.adapter.name;
async.parallel(
[
this.discoverModelProperties.bind(this, owner, table),
this.discoverPrimaryKeys.bind(this, owner, table),
this.discoverForeignKeys.bind(this, owner, table)
], function (err, results) {
var tasks = [
this.discoverModelProperties.bind(this, owner, table),
this.discoverPrimaryKeys.bind(this, owner, table) ];
if (err) {
cb && cb(err);
return;
}
if (options.associations) {
tasks.push(this.discoverForeignKeys.bind(this, owner, table));
}
var columns = results[0];
var primaryKeys = results[1];
var pks = {}, fks = {};
primaryKeys.forEach(function(pk) {
pks[pk.columnName] = pk.keySeq;
});
async.parallel(tasks, function (err, results) {
console.log(pks);
if (err) {
cb && cb(err);
return;
}
var foreignKeys = results[2];
foreignKeys.forEach(function(fk) {
fks[fk.fkColumnName] = {
keySeq: fk.keySeq,
owner: fk.pkOwner,
tableName: fk.pkTableName,
columnName: fk.pkColumnName
};
});
var columns = results[0];
if (!columns) {
cb && cb();
return;
}
console.log(fks);
if (!columns) {
cb && cb();
return;
}
var schema = {
name: fromDBName(table, false),
options: {
idInjection: false // DO NOT add id property
},
properties: {
}
};
schema.options[dataSourceName] = {
schema: owner,
table: table
};
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
schema.properties[propName] =
{
type: item.type,
required: (item.nullable === 'N'),
length: item.dataLength
};
if(pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][dataSourceName] = {
columnName: i.columnName,
dataType: i.dataType,
nullable: i.nullable
};
});
cb && cb(null, schema);
// Handle primary keys
var primaryKeys = results[1];
var pks = {};
primaryKeys.forEach(function (pk) {
pks[pk.columnName] = pk.keySeq;
});
if (self.settings.debug) {
console.log('Primary keys: ', pks);
}
var schema = {
name: fromDBName(table, false),
options: {
idInjection: false // DO NOT add id property
},
properties: {
}
};
schema.options[dataSourceName] = {
schema: columns[0].owner,
table: table
};
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
schema.properties[propName] = {
type: item.type,
required: (item.nullable === 'N'),
length: item.dataLength
};
if (pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][dataSourceName] = {
columnName: i.columnName,
dataType: i.dataType,
dataLength: i.dataLength,
nullable: i.nullable
};
});
// Add current table to the visited tables
options.visited = options.visited || {};
var schemaKey = columns[0].owner + '.' + table;
if (!options.visited.hasOwnProperty(schemaKey)) {
if(self.settings.debug) {
console.log('Adding schema for ' + schemaKey);
}
options.visited[schemaKey] = schema;
}
var otherTables = {};
if (options.associations) {
// Handle foreign keys
var fks = {};
var foreignKeys = results[2];
foreignKeys.forEach(function (fk) {
var fkInfo = {
keySeq: fk.keySeq,
owner: fk.pkOwner,
tableName: fk.pkTableName,
columnName: fk.pkColumnName
};
if (fks[fk.fkName]) {
fks[fk.fkName].push(fkInfo);
} else {
fks[fk.fkName] = [fkInfo];
}
});
if (self.settings.debug) {
console.log('Foreign keys: ', fks);
}
foreignKeys.forEach(function (fk) {
var propName = fromDBName(fk.pkTableName, true);
schema.properties[propName] = {
type: fromDBName(fk.pkTableName, false),
association: {
type: 'belongsTo',
foreignKey: fromDBName(fk.pkColumnName, true)
}
};
var key = fk.pkOwner + '.' + fk.pkTableName;
if (!options.visited.hasOwnProperty(key) && !otherTables.hasOwnProperty(key)) {
otherTables[key] = {owner: fk.pkOwner, tableName: fk.pkTableName};
}
});
}
if (Object.keys(otherTables).length === 0) {
cb && cb(null, options.visited);
} else {
var moreTasks = [];
for (var t in otherTables) {
if(self.settings.debug) {
console.log('Discovering related schema for ' + schemaKey);
}
moreTasks.push(DataSource.prototype.discoverSchemas.bind(self, otherTables[t].owner, otherTables[t].tableName, options));
}
async.parallel(moreTasks, function (err, results) {
var result = results && results[0];
cb && cb(err, result);
});
}
});
}
/**
@ -609,9 +681,9 @@ DataSource.prototype.idColumnName = function(modelName) {
}
/**
* Find the ID column name
* Find the ID property name
* @param modelName
* @returns {String} columnName for ID
* @returns {String} property for ID
*/
DataSource.prototype.idName = function(modelName) {
var props = this.definitions[modelName].properties;
@ -623,6 +695,28 @@ DataSource.prototype.idName = function(modelName) {
return null;
}
/**
* Find the ID property names sorted by the index
* @param modelName
* @returns {[String]} property names for IDs
*/
DataSource.prototype.idNames = function (modelName) {
var ids = [];
var props = this.definitions[modelName].properties;
for (var key in props) {
if (props[key].id && props[key].id > 0) {
ids.push({name: key, id: props[key].id});
}
}
ids.sort(function (a, b) {
return a.key - b.key;
});
var names = ids.map(function (id) {
return id.name;
});
return names;
}
/**
* Define foreign key

View File

@ -433,14 +433,13 @@ function buildSchema(name, properties, associations) {
/**
* Build models from schema definitions
* @param modelBuilder The model builder
* @param schemas The schemas can be one of the following three formats:
* 1. An array of named schema definition JSON objects
* 2. A schema definition JSON object
* 3. A list of property definitions (anonymous)
* @returns {Object} A map of model constructors keyed by model name
*/
function buildModels(modelBuilder, schemas) {
ModelBuilder.prototype.buildModels = function (schemas) {
var models = {};
if (Array.isArray(schemas)) {
@ -462,15 +461,15 @@ function buildModels(modelBuilder, schemas) {
for (var s in schemas) {
var name = schemas[s].name;
var schema = buildSchema(name, schemas[s].properties, associations);
var model = modelBuilder.define(name, schema);
models[name.toLowerCase()] = model;
var model = this.define(name, schema);
models[name] = model;
}
// Connect the models based on the associations
for (var i = 0; i < associations.length; i++) {
var association = associations[i];
var sourceModel = models[association.source.toLowerCase()];
var targetModel = models[association.target.toLowerCase()];
var sourceModel = models[association.source];
var targetModel = models[association.target];
if (sourceModel && targetModel) {
if(typeof sourceModel[association.relation] === 'function') {
sourceModel[association.relation](targetModel, {as: association.as});
@ -480,5 +479,5 @@ function buildModels(modelBuilder, schemas) {
return models;
}
ModelBuilder.buildModels = buildModels;

View File

@ -29,7 +29,7 @@ BaseSQL.prototype.queryOne = function (sql, callback) {
BaseSQL.prototype.dataSource = function(model) {
var m = this._models[model];
if(!m) {
console.log(new Error('Model not found: ' + model).stack);
console.trace('Model not found: ' + model);
}
return m.model.schema;
}
@ -98,6 +98,15 @@ BaseSQL.prototype.idName = function (model) {
return this.dataSource(model).idName(model);
};
/**
* Get the id property names
* @param model The model name
* @returns {[String]} The id property names
*/
BaseSQL.prototype.idNames = function (model) {
return this.dataSource(model).idNames(model);
};
/**
* Get the id column name
* @param model The model name
@ -130,7 +139,7 @@ BaseSQL.prototype.idColumnEscaped = function (model) {
BaseSQL.prototype.id = function (model, prop) {
var p = this._models[model].properties[prop];
if(!p) {
console.log(new Error('Property not found: ' + model +'.' + prop).stack);
console.trace('Property not found: ' + model +'.' + prop);
}
return p.id;
};

View File

@ -168,23 +168,23 @@ describe('Load models from json', function () {
// Read the schema JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return DataSource.buildModels(dataSource, schemas);
return dataSource.buildModels(schemas);
}
var models = loadSchemasSync(path.join(__dirname, 'test1-schemas.json'));
models.should.have.property('anonymous');
models.anonymous.should.have.property('modelName', 'Anonymous');
models.should.have.property('Anonymous');
models.Anonymous.should.have.property('modelName', 'Anonymous');
var m1 = new models.anonymous({title: 'Test'});
var m1 = new models.Anonymous({title: 'Test'});
m1.should.have.property('title', 'Test');
m1.should.have.property('author', 'Raymond');
models = loadSchemasSync(path.join(__dirname, 'test2-schemas.json'));
models.should.have.property('address');
models.should.have.property('account');
models.should.have.property('customer');
models.should.have.property('Address');
models.should.have.property('Account');
models.should.have.property('Customer');
for (var s in models) {
var m = models[s];
console.log(m.modelName, new m());