From f251ac9a5b405a342073408bad7654a59b576bc0 Mon Sep 17 00:00:00 2001 From: Anatoliy Chakkaev Date: Sun, 23 Oct 2011 23:43:53 +0400 Subject: [PATCH] Mysql --- lib/adapters/mysql.js | 125 ++++++++++++++++++++++++++++-------------- lib/schema.js | 9 +-- package.json | 3 +- test/common_test.js | 10 +++- 4 files changed, 99 insertions(+), 48 deletions(-) diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index ccf48f0b..493ff6cc 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -8,14 +8,12 @@ exports.initialize = function initializeSchema(schema, callback) { schema.client = mysql.createClient({ host: s.host || 'localhost', port: s.port || 3306, - user: s.user, + user: s.username, password: s.password, database: s.database, debug: s.debug }); - schema.client.auth(schema.settings.password, callback); - schema.adapter = new MySQL(schema.client); }; @@ -29,57 +27,107 @@ MySQL.prototype.define = function (descr) { }; MySQL.prototype.save = function (model, data, callback) { - this.client.query() - this.client.hmset(model + ':' + data.id, data, callback); + var sql = 'UPDATE ' + model + ' SET ' + this.toFields(model, data) + + ' WHERE id = ' + data.id; + + this.client.query(sql, function (err) { + callback(err); + }); }; +/** + * Must invoke callback(err, id) + */ MySQL.prototype.create = function (model, data, callback) { - this.client.incr(model + ':id', function (err, id) { - data.id = id; - this.save(model, data, function (err) { - if (callback) { - callback(err, id); - } - }); + var fields = this.toFields(model, data); + var sql = 'INSERT ' + model; + if (fields) { + sql += ' SET ' + fields; + } else { + sql += ' VALUES ()'; + } + this.client.query(sql, function (err, info) { + callback(err, info && info.insertId); + }); +}; + +MySQL.prototype.toFields = function (model, data) { + var fields = []; + var props = this._models[model].properties; + Object.keys(data).forEach(function (key) { + if (props[key]) { + fields.push(key + ' = ' + this.toDatabase(props[key], data[key])); + } }.bind(this)); + return fields.join(','); +}; + +MySQL.prototype.toDatabase = function (prop, val) { + if (prop.type.name === 'Number') return val; + if (val === null) return 'NULL'; + if (prop.type.name === 'Date') { + if (!val.toUTCString) { + val = new Date(val); + } + val = [ + val.getFullYear(), + val.getMonth() + 1, + val.getDate(), + val.getHours(), + val.getMinutes(), + val.getSeconds() + ].join('-'); + return this.client.escape(val); + } + return this.client.escape(val.toString()); +}; + +MySQL.prototype.fromDatabase = function (model, data) { + if (!data) return null; + var props = this._models[model].properties; + Object.keys(data).forEach(function (key) { + var val = data[key]; + if (props[key]) { + // if (props[key]) + } + data[key] = val; + }); + return data; }; MySQL.prototype.exists = function (model, id, callback) { - this.client.exists(model + ':' + id, function (err, exists) { - if (callback) { - callback(err, exists); - } + var sql = 'SELECT 1 FROM ' + model + ' WHERE id = ' + id + ' LIMIT 1'; + this.client.query(sql, function (err, data) { + if (err) return callback(err); + callback(null, data.length === 1); }); }; MySQL.prototype.find = function find(model, id, callback) { - this.client.hgetall(model + ':' + id, function (err, data) { - if (data && data.id) { - data.id = id; + var sql = 'SELECT * FROM ' + model + ' WHERE id = ' + id + ' LIMIT 1'; + this.client.query(sql, function (err, data) { + if (data && data.length === 1) { + data[0].id = id; } else { - data = null; + data = [null]; } - callback(err, data); - }); + callback(err, this.fromDatabase(model, data[0])); + }.bind(this)); }; MySQL.prototype.destroy = function destroy(model, id, callback) { - this.client.del(model + ':' + id, function (err) { + var sql = 'DELETE FROM ' + model + ' WHERE id = ' + id + ' LIMIT 1'; + this.client.query(sql, function (err) { callback(err); }); }; MySQL.prototype.all = function all(model, filter, callback) { - this.client.keys(model + ':*', function (err, keys) { + this.client.query('SELECT * FROM ' + model, function (err, data) { if (err) { return callback(err, []); } - var query = keys.map(function (key) { - return ['hgetall', key]; - }); - this.client.multi(query).exec(function (err, replies) { - callback(err, filter ? replies.filter(applyFilter(filter)) : replies); - }); + callback(err, filter ? data.filter(applyFilter(filter)) : data); }.bind(this)); }; @@ -108,27 +156,22 @@ function applyFilter(filter) { } MySQL.prototype.destroyAll = function destroyAll(model, callback) { - this.client.keys(model + ':*', function (err, keys) { + this.client.query('DELETE FROM ' + model, function (err) { if (err) { return callback(err, []); } - var query = keys.map(function (key) { - return ['del', key]; - }); - this.client.multi(query).exec(function (err, replies) { - callback(err); - }); + callback(err); }.bind(this)); }; MySQL.prototype.count = function count(model, callback) { - this.client.keys(model + ':*', function (err, keys) { - callback(err, err ? null : keys.length); + this.client.query('SELECT count(*) as cnt FROM ' + model, function (err, res) { + callback(err, err ? null : res[0].cnt); }); }; MySQL.prototype.updateAttributes = function updateAttrs(model, id, data, cb) { - this.client.hmset(model + ':' + id, data, cb); + data.id = id; + this.save(model, data, cb); }; - diff --git a/lib/schema.js b/lib/schema.js index eefe9010..b2fa18e6 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -3,6 +3,7 @@ */ var AbstractClass = require('./abstract-class').AbstractClass; var util = require('util'); +var path = require('path'); /** * Export public API @@ -34,9 +35,9 @@ function Schema(name, settings) { // this is only one initialization entry point of adapter // this module should define `adapter` member of `this` (schema) var adapter; - try { + if (path.existsSync(__dirname + '/adapters/' + name + '.js')) { adapter = require('./adapters/' + name); - } catch (e) { + } else { try { adapter = require(name); } catch (e) { @@ -146,10 +147,10 @@ Schema.prototype.defineForeignKey = function defineForeignKey(className, key) { if (this.adapter.defineForeignKey) { this.adapter.defineForeignKey(className, key, function (err, keyType) { if (err) throw err; - this.definitions[className].properties[key] = keyType; + this.definitions[className].properties[key] = {type: keyType}; }.bind(this)); } else { - this.definitions[className].properties[key] = Number; + this.definitions[className].properties[key] = {type: Number}; } }; diff --git a/package.json b/package.json index 42f65003..f5eb7fd2 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ }, "dependencies": { "redis": ">= 0.6.7", - "mongoose": ">= 2.2.3" + "mongoose": ">= 2.2.3", + "mysql": ">= 0.9.4" }, "devDependencies": {} } diff --git a/test/common_test.js b/test/common_test.js index 593149af..1bc9d9ae 100644 --- a/test/common_test.js +++ b/test/common_test.js @@ -11,6 +11,10 @@ var schemas = { database: 'sequ-test', username: 'root' }, + mysql: { + database: 'sequ-test', + username: 'root' + }, neo4j: { url: 'http://localhost:7474/' }, mongoose: { url: 'mongodb://localhost/test' }, redis: {}, @@ -128,7 +132,7 @@ function testOrm(schema) { title: title, date: date }, function (err, obj) { - test.ok(obj.id); + test.ok(obj.id, 'Object id should present'); test.equals(obj.title, title); // test.equals(obj.date, date); obj.title = title2; @@ -154,7 +158,8 @@ function testOrm(schema) { test.equals(obj.date, date); Post.find(obj.id, function () { test.equal(obj.title, title); - test.equal(obj.date, date.toString()); + console.log(obj.date.toString()); + test.equal(obj.date.toString(), date.toString()); test.done(); }); }); @@ -189,6 +194,7 @@ function testOrm(schema) { test.ok(exists, 'Object exists'); post.destroy(function () { Post.exists(post.id, function (err, exists) { + if (err) console.log(err); test.ok(!exists, 'Hey! ORM told me that object exists, but it looks like it doesn\'t. Something went wrong...'); Post.find(post.id, function (err, obj) { test.equal(obj, null, 'Param obj should be null');