1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-07 13:22:56 +03:00

feat: accept Map and Set and flatten arguments

This commit is contained in:
Ruben Bridgewater
2017-05-26 10:30:27 +02:00
parent 4182059b7c
commit 6ea202132b
9 changed files with 224 additions and 332 deletions

View File

@@ -4,9 +4,10 @@ const betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\
// TODO: Change the arguments to an object
// callOnWrite could be two things now
function Command (command, args, callOnWrite, transformer) {
this.command = command
function Command (name, args, callOnWrite, transformer) {
this.command = name
this.args = args
this.argsLength = 0
this.bufferArgs = false
var callback
transformer = transformer || function (err, res) {

View File

@@ -5,7 +5,8 @@ const Multi = require('./multi')
const RedisClient = require('../').RedisClient
const Command = require('./command')
const EMPTY_ARRAY = []
const clientProto = RedisClient.prototype
const multiProto = Multi.prototype
// TODO: Rewrite this including the individual commands into a Commands class
// that provided a functionality to add new commands to the client
@@ -15,63 +16,35 @@ commands.list.forEach((command) => {
const commandName = command.replace(/(?:^([0-9])|[^a-zA-Z0-9_$])/g, '_$1')
// Do not override existing functions
if (!RedisClient.prototype[command]) {
RedisClient.prototype[command] = function () {
if (!clientProto[command]) {
clientProto[command] = function () {
const len = arguments.length
var arr, i
if (len === 0) {
arr = EMPTY_ARRAY
} else if (arguments[0].shift) {
arr = arguments[0]
} else if (len > 1 && arguments[1].shift) {
const innerLen = arguments[1].length
arr = new Array(innerLen + 1)
arr[0] = arguments[0]
for (i = 0; i < innerLen; i += 1) {
arr[i + 1] = arguments[1][i]
}
} else {
arr = new Array(len)
for (i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
return this.internalSendCommand(new Command(command, arr))
}
if (RedisClient.prototype[command] !== commandName) {
Object.defineProperty(RedisClient.prototype[command], 'name', {
if (clientProto[command] !== commandName) {
Object.defineProperty(clientProto[command], 'name', {
value: commandName
})
}
}
// Do not override existing functions
if (!Multi.prototype[command]) {
Multi.prototype[command] = function () {
if (!multiProto[command]) {
multiProto[command] = function () {
const len = arguments.length
var arr, i
if (len === 0) {
arr = EMPTY_ARRAY
} else if (arguments[0].shift) {
arr = arguments[0]
} else if (len > 1 && arguments[1].shift) {
const innerLen = arguments[1].length
arr = new Array(innerLen + 1)
arr[0] = arguments[0]
for (i = 0; i < innerLen; i += 1) {
arr[i + 1] = arguments[1][i]
}
} else {
arr = new Array(len)
for (i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
this.queue.push(new Command(command, arr))
return this
}
if (Multi.prototype[command] !== commandName) {
Object.defineProperty(Multi.prototype[command], 'name', {
if (multiProto[command] !== commandName) {
Object.defineProperty(multiProto[command], 'name', {
value: commandName
})
}

View File

@@ -158,18 +158,12 @@ function infoCallback (self) {
// Store info in this.serverInfo after each call
RedisClient.prototype.info = function info (section) {
var args = []
if (section !== undefined) {
args = Array.isArray(section) ? section : [section]
}
const args = section ? [section] : []
return this.internalSendCommand(new Command('info', args, null, infoCallback(this)))
}
Multi.prototype.info = function info (section) {
var args = []
if (section !== undefined) {
args = Array.isArray(section) ? section : [section]
}
const args = section ? [section] : []
this.queue.push(new Command('info', args, null, infoCallback(this._client)))
return this
}
@@ -210,24 +204,10 @@ Multi.prototype.auth = function auth (pass) {
}
RedisClient.prototype.client = function client () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0]
} else if (Array.isArray(arguments[1])) {
len = arguments[1].length
arr = new Array(len + 1)
arr[0] = arguments[0]
for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]
}
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this
var callOnWrite
@@ -280,75 +260,11 @@ Multi.prototype.client = function client () {
return this
}
RedisClient.prototype.hmset = function hmset () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0]
} else if (Array.isArray(arguments[1])) {
len = arguments[1].length
arr = new Array(len + 1)
arr[0] = arguments[0]
for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]
}
} else if (typeof arguments[1] === 'object' && (arguments.length === 2)) {
arr = [arguments[0]]
for (const field in arguments[1]) {
arr.push(field, arguments[1][field])
}
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
}
return this.internalSendCommand(new Command('hmset', arr))
}
Multi.prototype.hmset = function hmset () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0]
} else if (Array.isArray(arguments[1])) {
len = arguments[1].length
arr = new Array(len + 1)
arr[0] = arguments[0]
for (; i < len; i += 1) {
arr[i + 1] = arguments[1][i]
}
} else if (typeof arguments[1] === 'object' && (arguments.length === 2)) {
arr = [arguments[0]]
for (const field in arguments[1]) {
arr.push(field, arguments[1][field])
}
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
}
this.queue.push(new Command('hmset', arr))
return this
}
RedisClient.prototype.subscribe = function subscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this
const callOnWrite = function () {
@@ -358,17 +274,10 @@ RedisClient.prototype.subscribe = function subscribe () {
}
Multi.prototype.subscribe = function subscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this._client
const callOnWrite = function () {
@@ -379,17 +288,10 @@ Multi.prototype.subscribe = function subscribe () {
}
RedisClient.prototype.unsubscribe = function unsubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this
const callOnWrite = function () {
@@ -400,17 +302,10 @@ RedisClient.prototype.unsubscribe = function unsubscribe () {
}
Multi.prototype.unsubscribe = function unsubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this._client
const callOnWrite = function () {
@@ -422,17 +317,10 @@ Multi.prototype.unsubscribe = function unsubscribe () {
}
RedisClient.prototype.psubscribe = function psubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this
const callOnWrite = function () {
@@ -442,17 +330,10 @@ RedisClient.prototype.psubscribe = function psubscribe () {
}
Multi.prototype.psubscribe = function psubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this._client
const callOnWrite = function () {
@@ -463,17 +344,10 @@ Multi.prototype.psubscribe = function psubscribe () {
}
RedisClient.prototype.punsubscribe = function punsubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this
const callOnWrite = function () {
@@ -484,17 +358,10 @@ RedisClient.prototype.punsubscribe = function punsubscribe () {
}
Multi.prototype.punsubscribe = function punsubscribe () {
var arr
var len = arguments.length
var i = 0
if (Array.isArray(arguments[0])) {
arr = arguments[0].slice(0)
} else {
len = arguments.length
arr = new Array(len)
for (; i < len; i += 1) {
arr[i] = arguments[i]
}
const len = arguments.length
const arr = new Array(len)
for (var i = 0; i < len; i += 1) {
arr[i] = arguments[i]
}
const self = this._client
const callOnWrite = function () {

142
lib/writeCommands.js Normal file
View File

@@ -0,0 +1,142 @@
'use strict'
const Commands = require('redis-commands')
const utils = require('./utils')
const debug = require('./debug')
// const isUint8Array = (() => {
// try {
// return process.binding('util').isUint8Array
// } catch (e) {
// // Fallback
// return (val) => {
// return Buffer.isBuffer(val) || ArrayBuffer.isView(val)
// }
// }
// })()
const copy = []
var bufferCount = 0
var errors = null
function writeBuffers (client) {
client.fireStrings = false
while (copy.length) {
const arg = copy.shift()
// TODO: Consider to convert the strings to buffers
// This might actually improve the performance at
// least in more modern Node versions
if (typeof arg === 'string') {
client.write(`$${Buffer.byteLength(arg)}\r\n${arg}\r\n`)
} else { // buffer
client.write(`$${arg.length}\r\n`)
client.write(arg)
client.write('\r\n')
}
debug('sendCommand: buffer send %s bytes', arg.length)
}
}
function toString (arg) {
if (typeof arg === 'string') {
copy.push(arg)
} else if (typeof arg === 'number') {
copy.push('' + arg)
} else if (arg instanceof Array) {
for (var i = 0; i < arg.length; i += 1) {
toString(arg[i])
}
} else if (arg && arg.constructor.name === 'Buffer') {
copy.push(arg)
bufferCount++
} else if (typeof arg === 'boolean') { // TODO: Remove this support and use hooks instead
copy.push('' + arg)
} else if (arg && arg.constructor.name === 'Object') { // Check if this is actually a good check or not
// TODO: As soon as we add support for JSON
// We could simple stringify this right here.
// This might interfere with nested Objects though.
// So we should only do this for the first level.
const keys = Object.keys(arg)
for (var j = 0; j < keys.length; j++) {
copy.push(keys[j])
toString(arg[keys[j]])
}
} else if (arg instanceof Map) {
arg.forEach((val, key) => {
toString(key)
toString(val)
})
} else if (arg instanceof Set) {
arg.forEach((val) => toString(val))
} else if (arg && arg.constructor.name === 'Date') { // Check if this is actually a good check or not
copy.push(arg.toString())
} else {
if (errors === null) {
errors = []
}
errors.push(arg)
}
}
function returnErr (client, command) {
const err = new TypeError('NodeRedis can not handle the provided arguments (see "error.issues" property).\n\nFurther information https://github.com/asd')
err.command = command.name.toUpperCase()
err.args = command.args
err.issues = errors
errors = null
utils.replyInOrder(client, command.callback, err, undefined, client.commandQueue)
}
// Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg.
// This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer.
// TODO: It is faster to move this part somewhere else
// We could move this to the function creation as well
// if we use hooks for our individual commands!
function normalizeAndWrite (client, command) {
const args = command.args
const origName = command.command
const renameCommands = client.renameCommands
const name = renameCommands[origName] !== undefined
? renameCommands[origName]
: origName
bufferCount = 0
for (var i = 0; i < args.length; i++) {
toString(args[i])
}
if (errors) {
return returnErr(client, command)
}
if (typeof client.options.prefix === 'string') {
const prefixKeys = Commands.getKeyIndexes(origName, copy)
prefixKeys.forEach((i) => {
// Attention it would be to expensive to detect if the input is non utf8 Buffer
// In that case the prefix *might* destroys user information
copy[i] = client.options.prefix + copy[i]
})
}
const bufferArgs = bufferCount !== 0
const len = copy.length
var commandStr = `*${len + 1}\r\n$${name.length}\r\n${name}\r\n`
command.bufferArgs = bufferArgs
command.argsLength = len
if (bufferArgs === false) {
while (copy.length) {
const arg = copy.shift()
commandStr += `$${Buffer.byteLength(arg)}\r\n${arg}\r\n`
}
debug('Send %s id %s: %s', client.address, client.connectionId, commandStr)
client.write(commandStr)
} else {
client.write(commandStr)
writeBuffers(client)
}
}
module.exports = normalizeAndWrite