Skip to content

Commit 2a33fc7

Browse files
committed
feat: add stringNumbers option to return numbers as JavaScript strings (#282)
Closes #273
1 parent 3ca30d8 commit 2a33fc7

File tree

4 files changed

+117
-26
lines changed

4 files changed

+117
-26
lines changed

API.md

+58-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## Classes
2+
23
<dl>
34
<dt><a href="#Redis">Redis</a> ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code></dt>
45
<dd></dd>
@@ -7,7 +8,9 @@
78
<dt><a href="#Commander">Commander</a></dt>
89
<dd></dd>
910
</dl>
11+
1012
## Members
13+
1114
<dl>
1215
<dt><a href="#defaultOptions">defaultOptions</a></dt>
1316
<dd><p>Default options</p>
@@ -16,26 +19,29 @@
1619
<dd><p>Default options</p>
1720
</dd>
1821
</dl>
22+
1923
<a name="Redis"></a>
24+
2025
## Redis ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
2126
**Kind**: global class
2227
**Extends:** <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>, <code>[Commander](#Commander)</code>
2328

2429
* [Redis](#Redis) ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
25-
* [new Redis([port], [host], [options])](#new_Redis_new)
26-
* _instance_
27-
* [.connect(callback)](#Redis+connect) ⇒ <code>Promise</code>
28-
* [.disconnect()](#Redis+disconnect)
29-
* ~~[.end()](#Redis+end)~~
30-
* [.duplicate()](#Redis+duplicate)
31-
* [.monitor([callback])](#Redis+monitor)
32-
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
33-
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
34-
* [.defineCommand(name, definition)](#Commander+defineCommand)
35-
* _static_
36-
* ~~[.createClient()](#Redis.createClient)~~
30+
* [new Redis([port], [host], [options])](#new_Redis_new)
31+
* _instance_
32+
* [.connect(callback)](#Redis+connect) ⇒ <code>Promise</code>
33+
* [.disconnect()](#Redis+disconnect)
34+
* ~~[.end()](#Redis+end)~~
35+
* [.duplicate()](#Redis+duplicate)
36+
* [.monitor([callback])](#Redis+monitor)
37+
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
38+
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
39+
* [.defineCommand(name, definition)](#Commander+defineCommand)
40+
* _static_
41+
* ~~[.createClient()](#Redis.createClient)~~
3742

3843
<a name="new_Redis_new"></a>
44+
3945
### new Redis([port], [host], [options])
4046
Creates a Redis instance
4147

@@ -63,6 +69,7 @@ Creates a Redis instance
6369
| [options.retryStrategy] | <code>function</code> | | See "Quick Start" section |
6470
| [options.reconnectOnError] | <code>function</code> | | See "Quick Start" section |
6571
| [options.readOnly] | <code>boolean</code> | <code>false</code> | Enable READONLY mode for the connection. Only available for cluster mode. |
72+
| [options.stringNumbers] | <code>boolean</code> | <code>false</code> | Force numbers to be always returned as JavaScript strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range). Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified because only JavaScript parser supports this feature for the time being. |
6673

6774
**Example**
6875
```js
@@ -80,6 +87,7 @@ var urlRedis2 = new Redis('//localhost:6379');
8087
var authedRedis = new Redis(6380, '192.168.100.1', { password: 'password' });
8188
```
8289
<a name="Redis+connect"></a>
90+
8391
### redis.connect(callback) ⇒ <code>Promise</code>
8492
Create a connection to Redis.
8593
This method will be invoked automatically when creating a new Redis instance.
@@ -92,6 +100,7 @@ This method will be invoked automatically when creating a new Redis instance.
92100
| callback | <code>function</code> |
93101

94102
<a name="Redis+disconnect"></a>
103+
95104
### redis.disconnect()
96105
Disconnect from Redis.
97106

@@ -102,13 +111,15 @@ If you want to wait for the pending replies, use Redis#quit instead.
102111
**Kind**: instance method of <code>[Redis](#Redis)</code>
103112
**Access:** public
104113
<a name="Redis+end"></a>
114+
105115
### ~~redis.end()~~
106116
***Deprecated***
107117

108118
Disconnect from Redis.
109119

110120
**Kind**: instance method of <code>[Redis](#Redis)</code>
111121
<a name="Redis+duplicate"></a>
122+
112123
### redis.duplicate()
113124
Create a new instance with the same options as the current one.
114125

@@ -120,6 +131,7 @@ var redis = new Redis(6380);
120131
var anotherRedis = redis.duplicate();
121132
```
122133
<a name="Redis+monitor"></a>
134+
123135
### redis.monitor([callback])
124136
Listen for all requests received by the server in real time.
125137

@@ -152,13 +164,15 @@ redis.monitor().then(function (monitor) {
152164
});
153165
```
154166
<a name="Commander+getBuiltinCommands"></a>
167+
155168
### redis.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
156169
Return supported builtin commands
157170

158171
**Kind**: instance method of <code>[Redis](#Redis)</code>
159172
**Returns**: <code>Array.&lt;string&gt;</code> - command list
160173
**Access:** public
161174
<a name="Commander+createBuiltinCommand"></a>
175+
162176
### redis.createBuiltinCommand(commandName) ⇒ <code>object</code>
163177
Create a builtin command
164178

@@ -171,6 +185,7 @@ Create a builtin command
171185
| commandName | <code>string</code> | command name |
172186

173187
<a name="Commander+defineCommand"></a>
188+
174189
### redis.defineCommand(name, definition)
175190
Define a custom command using lua script
176191

@@ -184,28 +199,31 @@ Define a custom command using lua script
184199
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |
185200

186201
<a name="Redis.createClient"></a>
202+
187203
### ~~Redis.createClient()~~
188204
***Deprecated***
189205

190206
Create a Redis instance
191207

192208
**Kind**: static method of <code>[Redis](#Redis)</code>
193209
<a name="Cluster"></a>
210+
194211
## Cluster ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
195212
**Kind**: global class
196213
**Extends:** <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>, <code>[Commander](#Commander)</code>
197214

198215
* [Cluster](#Cluster) ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
199-
* [new Cluster(startupNodes, options)](#new_Cluster_new)
200-
* [.connect()](#Cluster+connect) ⇒ <code>Promise</code>
201-
* [.disconnect()](#Cluster+disconnect)
202-
* [.nodes([role])](#Cluster+nodes) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
203-
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
204-
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
205-
* [.defineCommand(name, definition)](#Commander+defineCommand)
206-
* *[.sendCommand()](#Commander+sendCommand)*
216+
* [new Cluster(startupNodes, options)](#new_Cluster_new)
217+
* [.connect()](#Cluster+connect) ⇒ <code>Promise</code>
218+
* [.disconnect()](#Cluster+disconnect)
219+
* [.nodes([role])](#Cluster+nodes) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
220+
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
221+
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
222+
* [.defineCommand(name, definition)](#Commander+defineCommand)
223+
* *[.sendCommand()](#Commander+sendCommand)*
207224

208225
<a name="new_Cluster_new"></a>
226+
209227
### new Cluster(startupNodes, options)
210228
Creates a Redis Cluster instance
211229

@@ -225,18 +243,21 @@ Creates a Redis Cluster instance
225243
| [options.redisOptions] | <code>Object</code> | | Passed to the constructor of `Redis`. |
226244

227245
<a name="Cluster+connect"></a>
246+
228247
### cluster.connect() ⇒ <code>Promise</code>
229248
Connect to a cluster
230249

231250
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
232251
**Access:** public
233252
<a name="Cluster+disconnect"></a>
253+
234254
### cluster.disconnect()
235255
Disconnect from every node in the cluster.
236256

237257
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
238258
**Access:** public
239259
<a name="Cluster+nodes"></a>
260+
240261
### cluster.nodes([role]) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
241262
Get nodes with the specified role
242263

@@ -249,13 +270,15 @@ Get nodes with the specified role
249270
| [role] | <code>string</code> | <code>&quot;all&quot;</code> | role, "master", "slave" or "all" |
250271

251272
<a name="Commander+getBuiltinCommands"></a>
273+
252274
### cluster.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
253275
Return supported builtin commands
254276

255277
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
256278
**Returns**: <code>Array.&lt;string&gt;</code> - command list
257279
**Access:** public
258280
<a name="Commander+createBuiltinCommand"></a>
281+
259282
### cluster.createBuiltinCommand(commandName) ⇒ <code>object</code>
260283
Create a builtin command
261284

@@ -268,6 +291,7 @@ Create a builtin command
268291
| commandName | <code>string</code> | command name |
269292

270293
<a name="Commander+defineCommand"></a>
294+
271295
### cluster.defineCommand(name, definition)
272296
Define a custom command using lua script
273297

@@ -281,24 +305,27 @@ Define a custom command using lua script
281305
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |
282306

283307
<a name="Commander+sendCommand"></a>
308+
284309
### *cluster.sendCommand()*
285310
Send a command
286311

287312
**Kind**: instance abstract method of <code>[Cluster](#Cluster)</code>
288313
**Overrides:** <code>[sendCommand](#Commander+sendCommand)</code>
289314
**Access:** public
290315
<a name="Commander"></a>
316+
291317
## Commander
292318
**Kind**: global class
293319

294320
* [Commander](#Commander)
295-
* [new Commander()](#new_Commander_new)
296-
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
297-
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
298-
* [.defineCommand(name, definition)](#Commander+defineCommand)
299-
* *[.sendCommand()](#Commander+sendCommand)*
321+
* [new Commander()](#new_Commander_new)
322+
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
323+
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
324+
* [.defineCommand(name, definition)](#Commander+defineCommand)
325+
* *[.sendCommand()](#Commander+sendCommand)*
300326

301327
<a name="new_Commander_new"></a>
328+
302329
### new Commander()
303330
Commander
304331

@@ -310,13 +337,15 @@ This is the base class of Redis, Redis.Cluster and Pipeline
310337
| [options.showFriendlyErrorStack] | <code>boolean</code> | <code>false</code> | Whether to show a friendly error stack. Will decrease the performance significantly. |
311338

312339
<a name="Commander+getBuiltinCommands"></a>
340+
313341
### commander.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
314342
Return supported builtin commands
315343

316344
**Kind**: instance method of <code>[Commander](#Commander)</code>
317345
**Returns**: <code>Array.&lt;string&gt;</code> - command list
318346
**Access:** public
319347
<a name="Commander+createBuiltinCommand"></a>
348+
320349
### commander.createBuiltinCommand(commandName) ⇒ <code>object</code>
321350
Create a builtin command
322351

@@ -329,6 +358,7 @@ Create a builtin command
329358
| commandName | <code>string</code> | command name |
330359

331360
<a name="Commander+defineCommand"></a>
361+
332362
### commander.defineCommand(name, definition)
333363
Define a custom command using lua script
334364

@@ -342,18 +372,21 @@ Define a custom command using lua script
342372
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |
343373

344374
<a name="Commander+sendCommand"></a>
375+
345376
### *commander.sendCommand()*
346377
Send a command
347378

348379
**Kind**: instance abstract method of <code>[Commander](#Commander)</code>
349380
**Access:** public
350381
<a name="defaultOptions"></a>
382+
351383
## defaultOptions
352384
Default options
353385

354386
**Kind**: global variable
355387
**Access:** protected
356388
<a name="defaultOptions"></a>
389+
357390
## defaultOptions
358391
Default options
359392

lib/redis.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ var ScanStream = require('./scan_stream');
7474
* @param {function} [options.reconnectOnError] - See "Quick Start" section
7575
* @param {boolean} [options.readOnly=false] - Enable READONLY mode for the connection.
7676
* Only available for cluster mode.
77+
* @param {boolean} [options.stringNumbers=false] - Force numbers to be always returned as JavaScript
78+
* strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range).
79+
* Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified
80+
* because only JavaScript parser supports this feature for the time being.
7781
* @extends [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)
7882
* @extends Commander
7983
* @example
@@ -169,7 +173,8 @@ Redis.defaultOptions = {
169173
lazyConnect: false,
170174
keyPrefix: '',
171175
reconnectOnError: null,
172-
readOnly: false
176+
readOnly: false,
177+
stringNumbers: false
173178
};
174179

175180
Redis.prototype.resetCommandQueue = function () {

lib/redis/parser.js

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ exports.initParser = function () {
1919

2020
this.replyParser = new Parser({
2121
name: this.options.parser,
22+
stringNumbers: this.options.stringNumbers,
2223
returnBuffers: true,
2324
returnError: function (err) {
2425
_this.returnError(new ReplyError(err.message));

test/functional/string_numbers.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
var MAX_NUMBER = 9007199254740991; // Number.MAX_SAFE_INTEGER
4+
5+
describe('stringNumbers', function () {
6+
context('enabled', function () {
7+
it('returns numbers as strings', function (done) {
8+
var redis = new Redis({
9+
stringNumbers: true
10+
});
11+
12+
var pending = 0;
13+
14+
redis.set('foo', MAX_NUMBER);
15+
redis.incr('foo', check('9007199254740992'));
16+
redis.incr('foo', check('9007199254740993'));
17+
redis.incr('foo', check('9007199254740994'));
18+
19+
// also works for small interger
20+
redis.set('foo', 123);
21+
redis.incr('foo', check('124'));
22+
23+
// and floats
24+
redis.set('foo', 123.23);
25+
redis.incrbyfloat('foo', 1.2, check('124.43'));
26+
27+
function check(expected) {
28+
pending += 1;
29+
return function (err, res) {
30+
expect(res).to.eql(expected);
31+
if (!--pending) {
32+
redis.disconnect();
33+
done();
34+
}
35+
};
36+
}
37+
});
38+
});
39+
40+
context('disabled', function () {
41+
it('returns numbers', function (done) {
42+
var redis = new Redis();
43+
44+
redis.set('foo', '123');
45+
redis.incr('foo', function (err, res) {
46+
expect(res).to.eql(124);
47+
redis.disconnect();
48+
done();
49+
});
50+
});
51+
});
52+
});

0 commit comments

Comments
 (0)