kvao: implement key filter
This commit is contained in:
parent
01ce7df60f
commit
3b653a192b
|
@ -9,3 +9,4 @@ benchmark.js
|
||||||
analyse.r
|
analyse.r
|
||||||
docs/html
|
docs/html
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
.travis.yml
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var Connector = require('loopback-connector').Connector;
|
var Connector = require('loopback-connector').Connector;
|
||||||
var debug = require('debug')('loopback:connector:kv-memory');
|
var debug = require('debug')('loopback:connector:kv-memory');
|
||||||
|
var minimatch = require('minimatch');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
exports.initialize = function initializeDataSource(dataSource, cb) {
|
exports.initialize = function initializeDataSource(dataSource, cb) {
|
||||||
|
@ -160,8 +161,10 @@ KeyValueMemoryConnector.prototype.iterateKeys =
|
||||||
function(modelName, filter, options, callback) {
|
function(modelName, filter, options, callback) {
|
||||||
var store = this._getStoreForModel(modelName);
|
var store = this._getStoreForModel(modelName);
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var checkFilter = createMatcher(filter.match);
|
||||||
|
|
||||||
var keys = Object.keys(store).filter(function(key) {
|
var keys = Object.keys(store).filter(function(key) {
|
||||||
return !self._removeIfExpired(modelName, key);
|
return !self._removeIfExpired(modelName, key) && checkFilter(key);
|
||||||
});
|
});
|
||||||
|
|
||||||
debug('ITERATE KEYS %j -> %s keys', modelName, keys.length);
|
debug('ITERATE KEYS %j -> %s keys', modelName, keys.length);
|
||||||
|
@ -175,6 +178,18 @@ function(modelName, filter, options, callback) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function createMatcher(pattern) {
|
||||||
|
if (!pattern) return function matchAll() { return true; };
|
||||||
|
|
||||||
|
return minimatch.filter(pattern, {
|
||||||
|
nobrace: true,
|
||||||
|
noglobstar: true,
|
||||||
|
dot: true,
|
||||||
|
noext: true,
|
||||||
|
nocomment: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
KeyValueMemoryConnector.prototype.disconnect = function(callback) {
|
||||||
if (this._cleanupTimer)
|
if (this._cleanupTimer)
|
||||||
clearInterval(this._cleanupTimer);
|
clearInterval(this._cleanupTimer);
|
||||||
|
|
|
@ -9,6 +9,10 @@ var utils = require('../utils');
|
||||||
* @param {Object} filter An optional filter object with the following
|
* @param {Object} filter An optional filter object with the following
|
||||||
* properties:
|
* properties:
|
||||||
* - `match` - glob string to use to filter returned keys, e.g. 'userid.*'
|
* - `match` - glob string to use to filter returned keys, e.g. 'userid.*'
|
||||||
|
* All connectors are required to support `*` and `?`.
|
||||||
|
* They may also support additional special characters that are specific
|
||||||
|
* to the backing store.
|
||||||
|
*
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*
|
*
|
||||||
* @returns {AsyncIterator} An object implementing "next(cb) -> Promise"
|
* @returns {AsyncIterator} An object implementing "next(cb) -> Promise"
|
||||||
|
|
|
@ -13,6 +13,9 @@ var utils = require('../utils');
|
||||||
* @param {Object} filter An optional filter object with the following
|
* @param {Object} filter An optional filter object with the following
|
||||||
* properties:
|
* properties:
|
||||||
* - `match` - glob string to use to filter returned keys, e.g. 'userid.*'
|
* - `match` - glob string to use to filter returned keys, e.g. 'userid.*'
|
||||||
|
* All connectors are required to support `*` and `?`.
|
||||||
|
* They may also support additional special characters that are specific
|
||||||
|
* to the backing store.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @callback callback
|
* @callback callback
|
||||||
* @param {Error=} err
|
* @param {Error=} err
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
"depd": "^1.0.0",
|
"depd": "^1.0.0",
|
||||||
"inflection": "^1.6.0",
|
"inflection": "^1.6.0",
|
||||||
"loopback-connector": "^2.1.0",
|
"loopback-connector": "^2.1.0",
|
||||||
|
"minimatch": "^3.0.3",
|
||||||
"node-uuid": "^1.4.2",
|
"node-uuid": "^1.4.2",
|
||||||
"qs": "^3.1.0",
|
"qs": "^3.1.0",
|
||||||
"strong-globalize": "^2.6.2",
|
"strong-globalize": "^2.6.2",
|
||||||
|
|
|
@ -9,6 +9,12 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
|
||||||
var CacheItem;
|
var CacheItem;
|
||||||
beforeEach(function unpackContext() {
|
beforeEach(function unpackContext() {
|
||||||
CacheItem = helpers.givenCacheItem(dataSourceFactory);
|
CacheItem = helpers.givenCacheItem(dataSourceFactory);
|
||||||
|
CacheItem.sortedKeys = function(filter, options) {
|
||||||
|
return this.keys(filter, options).then(function(keys) {
|
||||||
|
keys.sort();
|
||||||
|
return keys;
|
||||||
|
});
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns all keys - Callback API', function(done) {
|
it('returns all keys - Callback API', function(done) {
|
||||||
|
@ -41,10 +47,9 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
|
||||||
return helpers.givenKeys(AnotherModel, ['otherKey1', 'otherKey2']);
|
return helpers.givenKeys(AnotherModel, ['otherKey1', 'otherKey2']);
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return CacheItem.keys();
|
return CacheItem.sortedKeys();
|
||||||
})
|
})
|
||||||
.then(function(keys) {
|
.then(function(keys) {
|
||||||
keys.sort();
|
|
||||||
should(keys).eql(['key1', 'key2']);
|
should(keys).eql(['key1', 'key2']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -53,16 +58,48 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
|
||||||
var expectedKeys = [];
|
var expectedKeys = [];
|
||||||
for (var ix = 0; ix < 1000; ix++)
|
for (var ix = 0; ix < 1000; ix++)
|
||||||
expectedKeys.push('key-' + ix);
|
expectedKeys.push('key-' + ix);
|
||||||
|
expectedKeys.sort();
|
||||||
|
|
||||||
return helpers.givenKeys(CacheItem, expectedKeys)
|
return helpers.givenKeys(CacheItem, expectedKeys)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return CacheItem.keys();
|
return CacheItem.sortedKeys();
|
||||||
})
|
})
|
||||||
.then(function(keys) {
|
.then(function(keys) {
|
||||||
keys.sort();
|
|
||||||
expectedKeys.sort();
|
|
||||||
should(keys).eql(expectedKeys);
|
should(keys).eql(expectedKeys);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('with "filter.match"', function() {
|
||||||
|
beforeEach(function createTestData() {
|
||||||
|
return helpers.givenKeys(CacheItem, [
|
||||||
|
'hallo',
|
||||||
|
'hello',
|
||||||
|
'hxllo',
|
||||||
|
'hllo',
|
||||||
|
'heeello',
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports "?" operator', function() {
|
||||||
|
return CacheItem.sortedKeys({ match: 'h?llo' }).then(function(keys) {
|
||||||
|
should(keys).eql(['hallo', 'hello', 'hxllo']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports "*" operator', function() {
|
||||||
|
return CacheItem.sortedKeys({ match: 'h*llo' }).then(function(keys) {
|
||||||
|
should(keys).eql(['hallo', 'heeello', 'hello', 'hllo', 'hxllo']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles no matches found', function() {
|
||||||
|
return CacheItem.sortedKeys({ match: 'not-found' })
|
||||||
|
.then(function(keys) {
|
||||||
|
should(keys).eql([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue