From 4815af35cfad4fc778cf71ed4abdb31444d47f6a Mon Sep 17 00:00:00 2001 From: Julien Fontanet Date: Wed, 6 Sep 2017 16:58:52 +0200 Subject: [PATCH] feat(Collection/Redis): case insensitive indexes (#600) Fixes vatesfr/xo-web#2337 --- src/collection/redis.js | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/collection/redis.js b/src/collection/redis.js index 3dca903ca..3fe4e1064 100644 --- a/src/collection/redis.js +++ b/src/collection/redis.js @@ -1,6 +1,6 @@ import { createClient as createRedisClient } from 'redis' import { difference, filter, forEach, isEmpty, keys as getKeys, map } from 'lodash' -import { promisifyAll } from 'promise-toolbox' +import { ignoreErrors, promisifyAll } from 'promise-toolbox' import { v4 as generateUuid } from 'uuid' import Collection, { ModelAlreadyExists } from '../collection' @@ -12,8 +12,8 @@ import { asyncMap } from '../utils' // Data model: // - prefix +'_id': value of the last generated identifier; // - prefix +'_ids': set containing identifier of all models; -// - prefix +'_'+ index +':' + value: set of identifiers which have -// value for the given index. +// - prefix +'_'+ index +':' + lowerCase(value): set of identifiers +// which have value for the given index. // - prefix +':'+ id: hash containing the properties of a model; // /////////////////////////////////////////////////////////////////// @@ -25,6 +25,8 @@ import { asyncMap } from '../utils' // TODO: Remote events. +const VERSION = '20170905' + export default class Redis extends Collection { constructor ({ connection, @@ -36,7 +38,23 @@ export default class Redis extends Collection { this.indexes = indexes this.prefix = prefix - this.redis = promisifyAll(connection || createRedisClient(uri)) + const redis = this.redis = promisifyAll(connection || createRedisClient(uri)) + + const key = `${prefix}:version` + redis.get(key).then(version => { + if (version === VERSION) { + return + } + + let p = redis.set(`${prefix}:version`, VERSION) + switch (version) { + case undefined: + // - clean indexes + // - indexes are now case insensitive + p = p.then(() => this.rebuildIndexes()) + } + return p + })::ignoreErrors() } rebuildIndexes () { @@ -55,7 +73,7 @@ export default class Redis extends Collection { asyncMap(indexes, index => { const value = values[index] if (value !== undefined) { - return redis.sadd(`${prefix}_${index}:${value}`, id) + return redis.sadd(`${prefix}_${index}:${String(value).toLowerCase()}`, id) } }) ) @@ -108,7 +126,7 @@ export default class Redis extends Collection { await asyncMap(indexes, index => { const value = previous[index] if (value !== undefined) { - return redis.srem(`${prefix}_${index}:${value}`, id) + return redis.srem(`${prefix}_${index}:${String(value).toLowerCase()}`, id) } }) } @@ -137,7 +155,7 @@ export default class Redis extends Collection { return } - const key = prefix + '_' + index + ':' + value + const key = prefix + '_' + index + ':' + String(value).toLowerCase() promises.push(redis.sadd(key, id)) }) @@ -173,7 +191,7 @@ export default class Redis extends Collection { throw new Error('fields not indexed: ' + unfit.join()) } - const keys = map(properties, (value, index) => `${prefix}_${index}:${value}`) + const keys = map(properties, (value, index) => `${prefix}_${index}:${String(value).toLowerCase()}`) return redis.sinter(...keys).then(ids => this._extract(ids)) } @@ -194,7 +212,7 @@ export default class Redis extends Collection { values != null && asyncMap(indexes, index => { const value = values[index] if (value !== undefined) { - return redis.srem(`${prefix}_${index}:${value}`, id) + return redis.srem(`${prefix}_${index}:${String(value).toLowerCase()}`, id) } }) )