不要怂,就是干,撸起袖子干!

Json dot notation and postgres syntax in json helper

1 parent a8cfa7e0
......@@ -16,7 +16,7 @@ var Sequelize = require(__dirname + "/../../index")
var Content = sequelize.define('Content', {
title: { type: Sequelize.STRING },
type: { type: Sequelize.STRING }, // ENUM('Movie', 'Episode', 'Music Video') },
type: { type: Sequelize.STRING },
metadata: { type: Sequelize.JSON }
})
, movie = Content.build({
......@@ -30,6 +30,7 @@ var Content = sequelize.define('Content', {
})
, episode = Content.build({
title: 'Chapter 3',
type: 'Episode',
metadata: {
season: 1,
episode: 3,
......@@ -50,7 +51,8 @@ sequelize.sync({ force: true })
console.log('=====================================');
console.log('Searching for any content in Japanese');
console.log('-------------------------------------');
// Using nested object query syntax
return Content.find({ where: Sequelize.json({ metadata: { language: 'Japanese' } }) })
.then(function(content) {
console.log('Result:', content.dataValues);
......@@ -62,12 +64,21 @@ sequelize.sync({ force: true })
console.log('Searching for any content in English');
console.log('-------------------------------------');
return Content.find({ where: "metadata->>'language' = 'English'" })
// Using the postgres json syntax
return Content.find({ where: Sequelize.json("metadata->>'language'", 'English') })
.then(function(content) {
console.log('Result:', content.dataValues);
console.log('=====================================');
})
})
.error(function(error) {
console.log(error)
.then(function() {
console.log('===========================================');
console.log('Searching for series named "House of Cards"');
console.log('-------------------------------------------');
return Content.find({ where: Sequelize.json('metadata.seriesTitle', 'House of Cards') })
.then(function(content) {
console.log('Result:', content.dataValues);
console.log('===========================================');
})
});
\ No newline at end of file
......@@ -836,11 +836,12 @@ module.exports = (function() {
* @see {Model#find}
*
* @method json
* @param {Object} conditions A hash containing strings/numbers or other nested hashes
* @param {String|Object} conditions A hash containing strings/numbers or other nested hash, a string using dot notation or a string using postgres json syntax.
* @param {String|Number|Boolean} [value] An optional value to compare against. Produces a string of the form "<json path> = '<value>'".
* @return {Sequelize.json}
*/
Sequelize.json = Sequelize.prototype.json = function (conditions) {
return new Utils.json(conditions);
Sequelize.json = Sequelize.prototype.json = function (conditionsOrPath, value) {
return new Utils.json(conditionsOrPath, value);
};
/*
......
......@@ -547,8 +547,15 @@ var Utils = module.exports = {
this.args = args;
},
json: function(conditions) {
this.conditions = conditions;
json: function(conditionsOrPath, value) {
if (Utils._.isObject(conditionsOrPath)) {
this.conditions = conditionsOrPath;
} else {
this.path = conditionsOrPath;
if (value) {
this.value = value;
}
}
},
where: function(attribute, logic) {
......@@ -622,11 +629,11 @@ Utils.json.prototype.toString = function () {
var _ = Utils._;
// A recursive parser for nested where conditions
function parse(_conditions, path) {
function parseConditionObject(_conditions, path) {
path = path || [];
return _.reduce(_conditions, function (r, v, k) { // result, key, value
if (_.isObject(v)) {
r = r.concat(parse(v, path.concat(k))); // Recursively parse objects
r = r.concat(parseConditionObject(v, path.concat(k))); // Recursively parse objects
} else {
r.push({ path: path.concat(k), value: v });
}
......@@ -634,16 +641,36 @@ Utils.json.prototype.toString = function () {
}, []);
}
// TODO: Move this postgres specific logic to a more appropriate place
function generateSql(condition) {
return util.format("%s#>>'{%s}' = '%s'",
_.first(condition.path),
_.rest(condition.path).join(','),
condition.value);
}
// Parse nested object
if (this.conditions) {
var conditions = _.map(parseConditionObject(this.conditions), function generateSql(condition) {
// TODO: Move this postgres specific logic to a more appropriate place
return util.format("%s#>>'{%s}' = '%s'",
_.first(condition.path),
_.rest(condition.path).join(','),
condition.value);
});
return conditions.join(' and ');
} else if (this.path) {
var str;
var conditions = _.map(parse(this.conditions), generateSql);
return conditions.join(' and ');
// Allow specifying conditions using the postgres json syntax
if (_.any(['->', '->>', '#>'], _.partial(_.contains, this.path))) { // TODO: Move postgres stuff somewhere else
str = this.path;
} else {
// Also support json dot notation
var path = this.path.split('.');
str = util.format("%s#>>'{%s}'",
_.first(path),
_.rest(path).join(','));
}
if (this.value) {
str += util.format(" = '%s'", this.value);
}
return str;
}
};
Utils.CustomEventEmitter = require(__dirname + '/emitters/custom-event-emitter');
......
......@@ -87,15 +87,16 @@ if (dialect.match(/^postgres/)) {
it('should be able to retrieve element of array by index', function () {
var self = this;
var emergencyContact = { name: 'kate', phones: [1337,42] };
var emergencyContact = { name: 'kate', phones: [1337, 42] };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(function (user) {
expect(user.emergency_contact).to.eql(emergencyContact);
return self.User.find({ where: { username: 'swen' }, attributes: [['emergency_contact->\'phones\'->1', 'emergency_contact']] });
//return self.User.find({ where: { username: 'swen' }, attributes: [['emergency_contact->\'phones\'->1', 'emergency_contact']] });
return self.User.find({ where: { username: 'swen' }, attributes: [[sequelize.json('emergency_contact.phones.1'), 'emergency_contact']] });
})
.then(function (user) {
expect(user.emergency_contact).to.equal(42);
expect(parseInt(user.emergency_contact)).to.equal(42);
});
});
......@@ -106,10 +107,10 @@ if (dialect.match(/^postgres/)) {
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(function (user) {
expect(user.emergency_contact).to.eql(emergencyContact);
return self.User.find({ where: { username: 'swen' }, attributes: [['emergency_contact->\'kate\'', 'emergency_contact']] });
return self.User.find({ where: { username: 'swen' }, attributes: [[sequelize.json('emergency_contact.kate'), 'emergency_contact']] });
})
.then(function (user) {
expect(user.emergency_contact).to.equal(1337);
expect(parseInt(user.emergency_contact)).to.equal(1337);
});
});
......@@ -120,16 +121,16 @@ if (dialect.match(/^postgres/)) {
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(function (user) {
expect(user.emergency_contact).to.eql(emergencyContact);
return self.User.find({ where: { username: 'swen' }, attributes: [['emergency_contact#>\'{kate,email}\'', 'emergency_contact']] });
return self.User.find({ where: { username: 'swen' }, attributes: [[sequelize.json('emergency_contact.kate.email'), 'emergency_contact']] });
})
.then(function (user) {
expect(user.emergency_contact).to.equal('kate@kate.com');
})
.then(function () {
return self.User.find({ where: { username: 'swen' }, attributes: [['emergency_contact#>\'{kate,phones,1}\'', 'emergency_contact']] });
return self.User.find({ where: { username: 'swen' }, attributes: [[sequelize.json('emergency_contact.kate.phones.1'), 'emergency_contact']] });
})
.then(function (user) {
expect(user.emergency_contact).to.equal(42);
expect(parseInt(user.emergency_contact)).to.equal(42);
});
});
......
......@@ -157,7 +157,23 @@ describe(Support.getTestDialectTeaser("Utils"), function() {
another_json_field: { x: 1 }
};
expect((new Utils.json(conditions)).toString()).to.deep.equal("metadata#>>'{language}' = 'icelandic' and metadata#>>'{pg_rating,dk}' = 'G' and another_json_field#>>'{x}' = '1'");
})
});
it('successfully parses a string using dot notation', function () {
var path = 'metadata.pg_rating.dk';
expect((new Utils.json(path)).toString()).to.equal("metadata#>>'{pg_rating,dk}'");
});
it('successfully parses a string and value using dot notation', function () {
var path = 'metadata.pg_rating.dk';
var value = 'G';
expect((new Utils.json(path, value)).toString()).to.equal("metadata#>>'{pg_rating,dk}' = 'G'");
});
it('allows postgres json syntax', function () {
var path = 'metadata->pg_rating->>dk';
expect((new Utils.json(path)).toString()).to.equal(path);
});
});
describe('inflection', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!