feat(Collection/Redis): case insensitive indexes (#600)

Fixes vatesfr/xo-web#2337
This commit is contained in:
Julien Fontanet 2017-09-06 16:58:52 +02:00 committed by GitHub
parent f5fc17c10e
commit 4815af35cf

View File

@ -1,6 +1,6 @@
import { createClient as createRedisClient } from 'redis' import { createClient as createRedisClient } from 'redis'
import { difference, filter, forEach, isEmpty, keys as getKeys, map } from 'lodash' 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 { v4 as generateUuid } from 'uuid'
import Collection, { ModelAlreadyExists } from '../collection' import Collection, { ModelAlreadyExists } from '../collection'
@ -12,8 +12,8 @@ import { asyncMap } from '../utils'
// Data model: // Data model:
// - prefix +'_id': value of the last generated identifier; // - prefix +'_id': value of the last generated identifier;
// - prefix +'_ids': set containing identifier of all models; // - prefix +'_ids': set containing identifier of all models;
// - prefix +'_'+ index +':' + value: set of identifiers which have // - prefix +'_'+ index +':' + lowerCase(value): set of identifiers
// value for the given index. // which have value for the given index.
// - prefix +':'+ id: hash containing the properties of a model; // - prefix +':'+ id: hash containing the properties of a model;
// /////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////
@ -25,6 +25,8 @@ import { asyncMap } from '../utils'
// TODO: Remote events. // TODO: Remote events.
const VERSION = '20170905'
export default class Redis extends Collection { export default class Redis extends Collection {
constructor ({ constructor ({
connection, connection,
@ -36,7 +38,23 @@ export default class Redis extends Collection {
this.indexes = indexes this.indexes = indexes
this.prefix = prefix 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 () { rebuildIndexes () {
@ -55,7 +73,7 @@ export default class Redis extends Collection {
asyncMap(indexes, index => { asyncMap(indexes, index => {
const value = values[index] const value = values[index]
if (value !== undefined) { 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 => { await asyncMap(indexes, index => {
const value = previous[index] const value = previous[index]
if (value !== undefined) { 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 return
} }
const key = prefix + '_' + index + ':' + value const key = prefix + '_' + index + ':' + String(value).toLowerCase()
promises.push(redis.sadd(key, id)) promises.push(redis.sadd(key, id))
}) })
@ -173,7 +191,7 @@ export default class Redis extends Collection {
throw new Error('fields not indexed: ' + unfit.join()) 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)) return redis.sinter(...keys).then(ids => this._extract(ids))
} }
@ -194,7 +212,7 @@ export default class Redis extends Collection {
values != null && asyncMap(indexes, index => { values != null && asyncMap(indexes, index => {
const value = values[index] const value = values[index]
if (value !== undefined) { if (value !== undefined) {
return redis.srem(`${prefix}_${index}:${value}`, id) return redis.srem(`${prefix}_${index}:${String(value).toLowerCase()}`, id)
} }
}) })
) )