mysql-live-select
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -16,2 +16,4 @@ /* mysql-live-select, MIT License ben@latenightsketches.com | ||
self._select = []; | ||
// Cache query results for any new, duplicate SELECT statements | ||
self._resultsBuffer = {}; | ||
@@ -22,8 +24,31 @@ db.connect(); | ||
if(event.getEventName() === 'tablemap') return; | ||
if(self._select.length === 0) return; | ||
// Cache query results within this update event | ||
var eventResults = {}; | ||
self._select.forEach(function(select){ | ||
if(select.matchRowEvent(event)){ | ||
select.update(); | ||
function _nextSelect(index){ | ||
var select; | ||
if(index < self._select.length){ | ||
select = self._select[index]; | ||
if(select.matchRowEvent(event)){ | ||
if(select.query in eventResults){ | ||
select._setRows(eventResults[select.query]); | ||
_nextSelect(index + 1); | ||
}else{ | ||
select.update(function(error, rows){ | ||
if(error === undefined){ | ||
eventResults[select.query] = rows; | ||
} | ||
_nextSelect(index + 1); | ||
}); | ||
} | ||
}else{ | ||
_nextSelect(index + 1); | ||
} | ||
} | ||
}); | ||
} | ||
_nextSelect(0); | ||
}); | ||
@@ -30,0 +55,0 @@ |
@@ -20,4 +20,9 @@ /* mysql-live-select, MIT License ben@latenightsketches.com | ||
self.query = self._escapeQueryFun(query); | ||
self.data = []; | ||
self.update(); | ||
if(self.query in base._resultsBuffer){ | ||
self._setRows(base._resultsBuffer[self.query]); | ||
}else{ | ||
self.update(); | ||
} | ||
} | ||
@@ -48,3 +53,3 @@ | ||
if(triggerDatabase === undefined){ | ||
self.emit('error', new Error('no database selected')); | ||
self.emit('error', new Error('no database selected on trigger')); | ||
return false; | ||
@@ -73,5 +78,30 @@ } | ||
LiveMysqlSelect.prototype.update = function(){ | ||
LiveMysqlSelect.prototype._setRows = function(rows){ | ||
var self = this; | ||
self.emit('update', rows); | ||
if(!self.base.settings.skipDiff){ | ||
rows.forEach(function(row, index){ | ||
if(self.data.length - 1 < index){ | ||
self.emit('added', row, index); | ||
self.data[index] = row; | ||
}else if(JSON.stringify(self.data[index]) !== JSON.stringify(row)){ | ||
self.emit('changed', self.data[index], row, index); | ||
self.data[index] = row; | ||
} | ||
}); | ||
if(self.data.length > rows.length){ | ||
for(var i = self.data.length - 1; i >= rows.length; i--){ | ||
self.emit('removed', self.data[i], i); | ||
} | ||
self.data.splice(rows.length, self.data.length - rows.length); | ||
} | ||
} | ||
self.lastUpdate = Date.now(); | ||
}; | ||
LiveMysqlSelect.prototype.update = function(callback){ | ||
var self = this; | ||
function _update(){ | ||
@@ -81,5 +111,7 @@ self.base.db.query(self.query, function(error, rows){ | ||
self.emit('error', error); | ||
callback && callback.call(self, error); | ||
}else{ | ||
self.lastUpdate = Date.now(); | ||
self.emit('update', rows); | ||
self.base._resultsBuffer[self.query] = rows; | ||
self._setRows(rows); | ||
callback && callback.call(self, undefined, rows); | ||
} | ||
@@ -86,0 +118,0 @@ }); |
{ | ||
"name": "mysql-live-select", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Live updating MySQL SELECT statements", | ||
@@ -5,0 +5,0 @@ "main": "lib/LiveMysql.js", |
@@ -44,3 +44,4 @@ # mysql-live-select | ||
`serverId` | `integer` | [Unique number (1 - 2<sup>32</sup>)](http://dev.mysql.com/doc/refman/5.0/en/replication-options.html#option_mysqld_server-id) to identify this replication slave instance. Must be specified if running more than one instance.<br>**Default:** `1` | ||
`minInterval` | `integer` | Pass a number of milliseconds to use as the minimum between result set updates. Omit to refresh results on every update. | ||
`minInterval` | `integer` | Pass a number of milliseconds to use as the minimum between result set updates. Omit to refresh results on every update. May be changed at runtime. | ||
`skipDiff` | `boolean` | If `true`, the `added`, `changed`, and `removed` events will not be emitted. May be changed at runtime.<br>**Default:** `false` | ||
@@ -75,3 +76,3 @@ ```javascript | ||
Returns `LiveMysqlSelect` object which inherits from [`EventEmitter`](http://nodejs.org/api/events.html), providing `update` and `error` events. | ||
Returns `LiveMysqlSelect` object | ||
@@ -104,4 +105,31 @@ #### Function as `query` | ||
## LiveMysqlSelect object | ||
Each call to the `select()` method on a LiveMysql object, returns a `LiveMysqlSelect` object with the following methods: | ||
Method Name | Arguments | Description | ||
------------|-----------|----------------------- | ||
`on`, `addListener` | `event`, `handler` | Add an event handler to the result set. See the following section for a list of the available event names. | ||
`update` | `callback` | Update the result set. Callback function accepts `error, rows` arguments. Events will be emitted. | ||
As well as all of the other methods available on [`EventEmitter`](http://nodejs.org/api/events.html)... | ||
### Available Events | ||
Event Name | Arguments | Description | ||
-----------|-----------|--------------------------- | ||
`update` | `rows` | Single argument contains complete result set array. Called before `added`, `changed`, and `removed` events. | ||
`added` | `row`, `index` | Row added to result set at index | ||
`changed` | `row`, `newRow`, `index` | Row contents mutated at index | ||
`removed` | `row`, `index` | Row removed at index | ||
`error` | `error` | Unhandled errors will be thrown | ||
## Running Tests | ||
Tests must be run with a properly configured MySQL server. Configure test settings in `test/settings.mysql.js`. | ||
Execute `nodeunit` using the `npm test` command. | ||
## License | ||
MIT |
@@ -15,2 +15,11 @@ /* mysql-live-select, MIT License ben@latenightsketches.com | ||
self.ready = false; | ||
// Log all queries | ||
self.queries = []; | ||
var origQueryMethod = self.conn.db.query; | ||
self.conn.db.query = function(query){ | ||
self.queries.push(query); | ||
return origQueryMethod.apply(this, arguments); | ||
} | ||
var escId = self.conn.db.escapeId; | ||
@@ -25,3 +34,3 @@ var esc = self.conn.db.escape.bind(self.conn.db); | ||
self.ready = true; | ||
self.emit('ready', self.conn, esc, escId); | ||
self.emit('ready', self.conn, esc, escId, self.queries); | ||
}); | ||
@@ -32,3 +41,3 @@ | ||
if(event === 'ready'){ | ||
if(self.ready) listener(self.conn, esc, escId); | ||
if(self.ready) listener(self.conn, esc, escId, self.queries); | ||
} | ||
@@ -35,0 +44,0 @@ }); |
@@ -11,4 +11,6 @@ /* mysql-live-select, MIT License ben@latenightsketches.com | ||
basic: function(test){ | ||
var table = 'simple'; | ||
server.on('ready', function(conn, esc, escId){ | ||
var table = 'basic'; | ||
// * Test that all events emit with correct arguments | ||
// [1] Test that duplicate queries are cached | ||
server.on('ready', function(conn, esc, escId, queries){ | ||
querySequence(conn.db, [ | ||
@@ -19,16 +21,82 @@ 'DROP TABLE IF EXISTS ' + escId(table), | ||
], function(results){ | ||
queries.splice(0, queries.length); | ||
conn.select('SELECT * FROM ' + escId(table), [ { | ||
var query = 'SELECT * FROM ' + escId(table); | ||
var triggers = [ { | ||
database: server.database, | ||
table: table | ||
} ]).on('update', function(data){ | ||
if(data[0].col === 15){ | ||
test.ok(true); | ||
test.done(); | ||
} ]; | ||
conn.select(query, triggers).on('update', function(data){ | ||
// After initial update | ||
if(data.length > 0 && data[0].col === 10){ | ||
// Second select instance to check resultsBuffer | ||
conn.select(query, triggers).on('update', function(data){ | ||
if(data.length > 0 && data[0].col === 15){ | ||
// [1] Test in LiveMysqlSelect created later, | ||
// Ensure only First select, update, second select occurred | ||
// i.e. No duplicate selects, resultsBuffer working | ||
test.equal(queries.length, 3); | ||
conn.db.query('DELETE FROM ' + escId(table)); | ||
} | ||
}); | ||
querySequence(conn.db, [ | ||
'UPDATE ' + escId(table) + | ||
' SET `col` = 15' | ||
], function(results){ | ||
// ... | ||
}); | ||
} | ||
}).on('added', function(row, index){ | ||
test.equal(index, 0); | ||
test.equal(row.col, 10); | ||
}).on('changed', function(row, newRow, index){ | ||
test.equal(index, 0); | ||
test.equal(row.col, 10); | ||
test.equal(newRow.col, 15); | ||
}).on('removed', function(row, index){ | ||
test.equal(index, 0); | ||
test.equal(row.col, 15); | ||
test.done(); | ||
}); | ||
}); | ||
}); | ||
}, | ||
skipDiff: function(test){ | ||
var table = 'skip_diff'; | ||
server.on('ready', function(conn, esc, escId, queries){ | ||
querySequence(conn.db, [ | ||
'DROP TABLE IF EXISTS ' + escId(table), | ||
'CREATE TABLE ' + escId(table) + ' (col INT UNSIGNED)', | ||
'INSERT INTO ' + escId(table) + ' (col) VALUES (10)', | ||
], function(results){ | ||
var error = function(){ | ||
throw new Error('diff events should not be called'); | ||
}; | ||
conn.settings.skipDiff = true; | ||
conn.select('SELECT * FROM ' + escId(table), [ { | ||
table: table, | ||
database: server.database | ||
} ]).on('update', function(rows){ | ||
if(rows.length > 0 && rows[0].col === 10){ | ||
test.ok(true); | ||
}else if(rows.length > 0 && rows[0].col === 15){ | ||
conn.db.query('DELETE FROM ' + escId(table)); | ||
}else if(rows.length === 0){ | ||
// Give time, just in case the `removed` event comes in | ||
setTimeout(function(){ | ||
test.done(); | ||
}, 100); | ||
} | ||
}) | ||
.on('added', error) | ||
.on('changed', error) | ||
.on('removed', error); | ||
querySequence(conn.db, [ | ||
'UPDATE ' + escId(table) + | ||
'SET `col` = 15' | ||
' SET `col` = 15' | ||
], function(results){ | ||
@@ -42,3 +110,3 @@ // ... | ||
var table = 'error_no_db'; | ||
server.on('ready', function(conn, esc, escId){ | ||
server.on('ready', function(conn, esc, escId, queries){ | ||
querySequence(conn.db, [ | ||
@@ -53,3 +121,4 @@ 'DROP TABLE IF EXISTS ' + escId(table), | ||
} ]).on('error', function(error){ | ||
test.equal(error.toString(), 'Error: no database selected'); | ||
test.equal(error.toString(), | ||
'Error: no database selected on trigger'); | ||
test.done(); | ||
@@ -60,3 +129,3 @@ }); | ||
'UPDATE ' + escId(table) + | ||
'SET `col` = 15' | ||
' SET `col` = 15' | ||
], function(results){ | ||
@@ -63,0 +132,0 @@ // ... |
20134
402
133