-
Notifications
You must be signed in to change notification settings - Fork 182
Add basic json query support #304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -321,6 +321,37 @@ function escapeLiteral(str) { | |
return escaped; | ||
} | ||
|
||
/* | ||
* Check if a value is attempting to use nested json keys | ||
* @param {String} property The property being queried from where clause | ||
* @returns {Boolean} True of the property contains dots for nested json | ||
*/ | ||
function isNested(property) { | ||
return property.split('.').length > 1; | ||
} | ||
|
||
/* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSDocs would be great here as well. |
||
* Overwrite the loopback-connector column escape | ||
* to allow querying nested json keys | ||
* @param {String} model The model name | ||
* @param {String} property The property name | ||
* @returns {String} The escaped column name, or column with nested keys for deep json columns | ||
*/ | ||
PostgreSQL.prototype.columnEscaped = function(model, property) { | ||
if (isNested(property)) { | ||
// Convert column to PostgreSQL json style query: "model"->>'val' | ||
var self = this; | ||
return property | ||
.split('.') | ||
.map(function(val, idx) { return (idx === 0 ? self.columnEscaped(model, val) : escapeLiteral(val)); }) | ||
.reduce(function(prev, next, idx, arr) { | ||
return idx == 0 ? next : idx < arr.length - 1 ? prev + '->' + next : prev + '->>' + next; | ||
}); | ||
} else { | ||
return this.escapeName(this.column(model, property)); | ||
} | ||
}; | ||
|
||
/*! | ||
* Escape the name for PostgreSQL DB | ||
* @param {String} name The name | ||
|
@@ -499,6 +530,12 @@ PostgreSQL.prototype._buildWhere = function(model, where) { | |
// The value is not an array, fall back to regular fields | ||
} | ||
var p = props[key]; | ||
|
||
if (p == null && isNested(key)) { | ||
// See if we are querying nested json | ||
p = props[key.split('.')[0]]; | ||
} | ||
|
||
if (p == null) { | ||
// Unknown property, ignore it | ||
debug('Unknown property %s is skipped for model %s', key, model); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -658,6 +658,86 @@ describe('postgresql connector', function() { | |
}); | ||
}); | ||
}); | ||
|
||
context('json data type', function() { | ||
var Customer; | ||
|
||
before(function(done) { | ||
db = getDataSource(); | ||
|
||
Customer = db.define('Customer', { | ||
address: { | ||
type: 'object', | ||
postgresql: { | ||
dataType: 'json', | ||
}, | ||
}, | ||
}); | ||
|
||
db.automigrate(function(err) { | ||
if (err) return done(err); | ||
Customer.create([{ | ||
address: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add test/s for 1 more level of nested keys (for checking the recursive calls in https://github.com/strongloop/loopback-connector-postgresql/pull/304/files#diff-ac54005f9bbf753aa45ceebc4a032628R341, or would that be redundant? I.e.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely not redundant since it uncovered a bug 😅 Syntax for deeply nested needed to be https://www.postgresql.org/docs/9.5/static/functions-json.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah great! thank you for the link :-). |
||
city: 'Springfield', | ||
street: { | ||
number: 42, | ||
}, | ||
}, | ||
}, { | ||
address: { | ||
city: 'Hill Valley', | ||
street: { | ||
number: 56, | ||
}, | ||
}, | ||
}], function(err, customers) { | ||
return done(err); | ||
}); | ||
}); | ||
}); | ||
|
||
it('allows querying for nested json properties', function(done) { | ||
Customer.find({ | ||
where: { | ||
'address.city': 'Hill Valley', | ||
}, | ||
}, function(err, results) { | ||
if (err) return done(err); | ||
results.length.should.eql(1); | ||
results[0].address.city.should.eql('Hill Valley'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('queries multiple levels of nesting', function(done) { | ||
Customer.find({ | ||
where: { | ||
'address.street.number': 56, | ||
}, | ||
}, function(err, results) { | ||
if (err) return done(err); | ||
results.length.should.eql(1); | ||
results[0].address.city.should.eql('Hill Valley'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('allows ordering by nested json properties', function(done) { | ||
Customer.find({ | ||
order: ['address.city DESC'], | ||
}, function(err, results1) { | ||
if (err) return done(err); | ||
results1[0].address.city.should.eql('Springfield'); | ||
Customer.find({ | ||
order: ['address.city ASC'], | ||
}, function(err, results2) { | ||
if (err) return done(err); | ||
results2[0].address.city.should.eql('Hill Valley'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Serial properties', function() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add JSDocs here for the
property
param?