From 4f942c00018eb5ba4fab5d02ae24ced544767d76 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 25 Jul 2014 17:18:05 -0700 Subject: [PATCH 1/3] Add support for GeoPoint type --- example/model.js | 2 +- lib/postgresql.js | 49 ++++++++++++++++++++++++++++++-- test/postgresql.discover.test.js | 14 +++++++++ test/postgresql.test.js | 19 ++++++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/example/model.js b/example/model.js index 54be835e..c12b7c22 100644 --- a/example/model.js +++ b/example/model.js @@ -9,7 +9,7 @@ var Account = ds.createModel('account', { name: String, emails: [String], age: Number}, - {strcit: true}); + {strict: true}); ds.automigrate('account', function(err) { // Create two instances diff --git a/lib/postgresql.js b/lib/postgresql.js index 5da604d4..b48f3839 100644 --- a/lib/postgresql.js +++ b/lib/postgresql.js @@ -245,6 +245,25 @@ PostgreSQL.prototype._categorizeProperties = function(model, data) { }; }; +PostgreSQL.prototype.mapToDB = function (model, data) { + var dbData = {}; + if (!data) { + return dbData; + } + var props = this._models[model].properties; + for (var p in data) { + if(props[p]) { + var pType = props[p].type && props[p].type.name; + if (pType === 'GeoPoint' && data[p]) { + dbData[p] = '(' + data[p].lat + ',' + data[p].lng + ')'; + } else { + dbData[p] = data[p]; + } + } + } + return dbData; +} + /** * Create the data model in PostgreSQL * @@ -256,6 +275,7 @@ PostgreSQL.prototype._categorizeProperties = function(model, data) { */ PostgreSQL.prototype.create = function (model, data, callback) { var self = this; + data = self.mapToDB(model, data); var props = self._categorizeProperties(model, data); var sql = []; @@ -294,6 +314,7 @@ PostgreSQL.prototype.create = function (model, data, callback) { /* PostgreSQL.prototype.updateOrCreate = function (model, data, callback) { var self = this; + data = self.mapToDB(model, data); var props = self._categorizeProperties(model, data); var idColumns = props.ids.map(function(key) { return self.columnEscaped(model, key); } @@ -342,6 +363,7 @@ PostgreSQL.prototype.updateOrCreate = function (model, data, callback) { */ PostgreSQL.prototype.save = function (model, data, callback) { var self = this; + data = self.mapToDB(model, data); var props = self._categorizeProperties(model, data); var sql = []; @@ -364,6 +386,7 @@ PostgreSQL.prototype.update = var sql = ['UPDATE ', this.tableEscaped(model), ' SET ', this.toFields(model, data), ' ', whereClause].join(''); + data = this.mapToDB(model, data); var props = this._categorizeProperties(model, data); this.query(sql, generateQueryParams(data, props), function (err, result) { @@ -571,12 +594,24 @@ PostgreSQL.prototype.fromDatabase = function (model, data) { } // console.log(key, val); var prop = props[p]; - if (prop && prop.type && prop.type.name === 'Boolean') { + var type = prop.type && prop.type.name; + if (prop && type === 'Boolean') { if(typeof val === 'boolean') { json[p] = val; } else { json[p] = (val === 'Y' || val === 'y' || val === 'T' || val === 't' || val === '1'); } + } else if (prop && type === 'GeoPoint' || type === 'Point') { + if (typeof val === 'string') { + // The point format is (x,y) + var point = val.split(/[\(\)\s,]+/).filter(Boolean); + json[p] = { + lat: point[0], + lng: point[1] + }; + } else { + json[p] = val; + } } else { json[p] = val; } @@ -609,7 +644,7 @@ PostgreSQL.prototype.escapeName = function (name) { if (!name) { return name; } - return '"' + name.replace(/\./g, '"."').toLowerCase() + '"'; + return '"' + name.replace(/\./g, '"."') + '"'; }; PostgreSQL.prototype.schemaName = function (model) { @@ -620,6 +655,13 @@ PostgreSQL.prototype.schemaName = function (model) { return schemaName; }; +PostgreSQL.prototype.table = function (model) { + // Check if there is a 'table' property for postgresql + var dbMeta = this._models[model].settings && this._models[model].settings.postgresql; + var tableName = (dbMeta && (dbMeta.table || dbMeta.tableName)) || model.toLowerCase(); + return tableName; +}; + PostgreSQL.prototype.tableEscaped = function (model) { return this.escapeName(this.schemaName(model)) + '.' + this.escapeName(this.table(model)); }; @@ -1419,6 +1461,9 @@ PostgreSQL.prototype.columnDataType = function (model, property) { return 'TIMESTAMP WITH TIME ZONE'; case 'Timestamp': return 'TIMESTAMP WITH TIME ZONE'; + case 'GeoPoint': + case 'Point': + return 'POINT'; case 'Boolean': return 'BOOLEAN'; // PostgreSQL doesn't have built-in boolean } diff --git a/test/postgresql.discover.test.js b/test/postgresql.discover.test.js index cc90dc58..055a7b56 100644 --- a/test/postgresql.discover.test.js +++ b/test/postgresql.discover.test.js @@ -195,3 +195,17 @@ describe('Discover LDL schema from a table', function () { }); }); }); + +describe('Discover and build models', function() { + it('should build a model from discovery', function(done) { + + db.discoverAndBuildModels('GeoPoint', {schema: 'strongloop'}, function(err, schema) { + schema.Geopoint.find(function(err, data) { + assert(!err); + assert(Array.isArray(data)); + assert(data[0].location); + done(); + }); + }); + }); +}); diff --git a/test/postgresql.test.js b/test/postgresql.test.js index 547876b3..c1c4036d 100644 --- a/test/postgresql.test.js +++ b/test/postgresql.test.js @@ -1,3 +1,4 @@ +var juggler = require('loopback-datasource-juggler'); require('loopback-datasource-juggler/test/common.batch.js'); require('loopback-datasource-juggler/test/include.test.js'); @@ -14,6 +15,7 @@ describe('postgresql connector', function () { Post = db.define('PostWithBoolean', { title: { type: String, length: 255, index: true }, content: { type: String }, + loc: 'GeoPoint', approved: Boolean }); }); @@ -61,6 +63,7 @@ describe('postgresql connector', function () { }); }); +<<<<<<< HEAD it('should return the model instance for upsert', function(done) { Post.upsert({id: post.id, title: 'T2_new', content: 'C2_new', approved: true}, function(err, p) { @@ -124,6 +127,20 @@ describe('postgresql connector', function () { }); }); + it('should support GeoPoint types', function(done) { + var GeoPoint = juggler.ModelBuilder.schemaTypes.geopoint; + var loc = new GeoPoint({lng: 10, lat: 20}); + Post.create({title: 'T1', content: 'C1', loc: loc}, function(err, p) { + should.not.exists(err); + Post.findById(p.id, function(err, p) { + should.not.exists(err); + p.loc.lng.should.be.eql(10); + p.loc.lat.should.be.eql(20); + done(); + }); + }); + }); + }); // FIXME: The following test cases are to be reactivated for PostgreSQL @@ -231,4 +248,4 @@ describe('postgresql connector', function () { }); }); }); - */ \ No newline at end of file + */ From 1e254b27cf01876c3953f89c206649624f68435d Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 24 Nov 2014 09:46:32 -0800 Subject: [PATCH 2/3] Typecast lat/lng values to number. --- lib/postgresql.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/postgresql.js b/lib/postgresql.js index b48f3839..7f670d6d 100644 --- a/lib/postgresql.js +++ b/lib/postgresql.js @@ -606,8 +606,8 @@ PostgreSQL.prototype.fromDatabase = function (model, data) { // The point format is (x,y) var point = val.split(/[\(\)\s,]+/).filter(Boolean); json[p] = { - lat: point[0], - lng: point[1] + lat: +point[0], + lng: +point[1] }; } else { json[p] = val; From 89382553b59c25bf5f88d1acb5af637b2db8fb3b Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 20 Jan 2015 16:38:32 -0800 Subject: [PATCH 3/3] Fix merging issue --- test/postgresql.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/postgresql.test.js b/test/postgresql.test.js index c1c4036d..3dd5999c 100644 --- a/test/postgresql.test.js +++ b/test/postgresql.test.js @@ -63,7 +63,6 @@ describe('postgresql connector', function () { }); }); -<<<<<<< HEAD it('should return the model instance for upsert', function(done) { Post.upsert({id: post.id, title: 'T2_new', content: 'C2_new', approved: true}, function(err, p) {