From 0a840b2cdd86a72fd9e440a08f0dde08d922bdf4 Mon Sep 17 00:00:00 2001 From: Sascha Gehlich Date: Mon, 6 Jan 2014 15:40:20 +0100 Subject: [PATCH] Only add indices if they don't exist anymore (syncing mechanism) --- index.js | 32 +++++---------- lib/indices-syncer.js | 93 +++++++++++++++++++++++++++++++++++++++++++ test/index.test.js | 18 ++++++++- test/models/user.js | 2 +- 4 files changed, 120 insertions(+), 25 deletions(-) create mode 100644 lib/indices-syncer.js diff --git a/index.js b/index.js index fefe0fa..0e67fc0 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var _ = require("underscore"); var Sequelize = require("sequelize"); var Utils = Sequelize.Utils; _.str = require("underscore.string"); +var IndicesSyncer = require("./lib/indices-syncer"); function Sequenice(sequelize, options) { if (!sequelize) throw new Error("sequenice needs an instance of sequelize"); @@ -305,28 +306,7 @@ Sequenice.prototype._extractMethodsFromModel = function(modelClass, instanceTarg */ Sequenice.prototype._overrideSyncWithIndices = function(model, indices) { var sync = model.sync; - var self = this; - - // Gets called after syncing has been done - // Creates indices for this model - var addIndices = function (emitter, m) { - var chain = new Sequelize.Utils.QueryChainer(); - var index; - for(var i = 0; i < indices.length; i++) { - index = indices[i]; - chain.add( - self.sequelize.queryInterface.addIndex(model.tableName, index.attributes, index.options) - ); - } - - chain.run() - .success(function () { - emitter.emit("success", m); - }) - .error(function (e) { - emitter.emit("error", e); - }); - }; + var sequelize = this.sequelize; // Override syncing method so that it calls // addIndices() afterwards @@ -335,7 +315,13 @@ Sequenice.prototype._overrideSyncWithIndices = function(model, indices) { return new Utils.CustomEventEmitter(function(emitter) { sync.apply(self, arguments) .success(function (m) { - addIndices(emitter, m); + + // Sequelize's syncing worked, run the index syncing mechanism + var indicesSyncer = new IndicesSyncer(sequelize, m, indices); + indicesSyncer + .sync() + .proxy(emitter); + }) .error(function (e) { emitter.emit("error", e); diff --git a/lib/indices-syncer.js b/lib/indices-syncer.js new file mode 100644 index 0000000..312d1d6 --- /dev/null +++ b/lib/indices-syncer.js @@ -0,0 +1,93 @@ +"use strict"; +var sequelize = require("sequelize"); +var Utils = sequelize.Utils; + +function IndicesSyncer(sequelize, model, indices) { + this.sequelize = sequelize; + this.model = model; + this.indices = indices; +} + +/** + * Checks whether syncing is necessary, then starts the + * syncing process + * @return {CustomEventEmitter} + * @public + */ +IndicesSyncer.prototype.sync = function() { + var self = this; + + // No indices to sync, just pretend we're fine + if (this.indices.length === 0) { + return new Utils.CustomEventEmitter(function (emitter) { + emitter.emit("success", self.model); + }).run(); + } + + // Fetch the current indices + return new Utils.CustomEventEmitter(function (emitter) { + self._fetchIndices(emitter); + }).run(); +}; + +/** + * Fetches the currently existing indices, then syncs them + * @param {CustomEventEmitter} emitter + * @private + */ +IndicesSyncer.prototype._fetchIndices = function(emitter) { + var queryInterface = this.sequelize.queryInterface; + var self = this; + + queryInterface.showIndex(this.model.tableName) + .success(function (existingIndices) { + self.existingIndices = existingIndices; + + self._syncIndices(emitter); + }) + .error(function (e) { + emitter.emit("error", e); + }); +}; + +/** + * Final step - sync the indices and pass the result of our + * chain to the emitter + * @param {CustomEventEmitter} emitter + * @private + */ +IndicesSyncer.prototype._syncIndices = function(emitter) { + var chainer = new Utils.QueryChainer(); + var queryInterface = this.sequelize.queryInterface; + + for (var i = 0, len = this.indices.length; i < len; i++) { + var index = this.indices[i]; + var indexName; + + // Build the index name + // @TODO + // Move the index name generation to sequelize maybe? + if (index.options && index.options.indexName) { + indexName = index.options.indexName; + } else { + indexName = Utils._.underscored(this.model.tableName + "_" + index.attributes.join("_")); + } + + // Does the index already exist? + var indexExists = + this.existingIndices + .filter(function (existingIndex) { + return existingIndex.name === indexName; + }).length > 0; + + if (!indexExists) { + chainer.add(queryInterface.addIndex(this.model.tableName, index.attributes, index.options)); + } + } + + chainer + .run() + .proxy(emitter); +}; + +module.exports = IndicesSyncer; diff --git a/test/index.test.js b/test/index.test.js index 20025d2..f96590c 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,4 +1,5 @@ "use strict"; +var _ = require("underscore"); var should = require("should"); var Sequelize = require("sequelize"); var Sequenice = require(".."); @@ -21,6 +22,10 @@ before(function (done) { chain.run().success(function () { done(); }).failure(function (err) { + if (err instanceof Array) { + err = _.flatten(err)[0]; + } + throw err; }); }); @@ -124,9 +129,20 @@ describe("sequenice example", function () { indices.length.should.equal(3); indices[1].name.should.equal("IdName"); - indices[2].name.should.equal("NameIsAdmin"); + indices[2].name.should.equal("users_name_is_admin"); + + done(); + }); + }); + it("only creates indices if they don't exist", function (done) { + sequelize.sync().success(function () { done(); + }).failure(function (err) { + if (err instanceof Array) { + err = _.flatten(err)[0]; + } + throw err; }); }); }); diff --git a/test/models/user.js b/test/models/user.js index 22269a3..86e1840 100644 --- a/test/models/user.js +++ b/test/models/user.js @@ -37,7 +37,7 @@ function User(s) { * Indices */ this.index(["id", "name"], { indexName: "IdName" }); - this.index(["name", "isAdmin"], { indexName: "NameIsAdmin" }); + this.index(["name", "isAdmin"]); } User.classMethod = function() {};