deepbase-redis 1.2.2 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +180 -69
- package/package.json +20 -27
- package/src/RedisDriver.js +138 -0
- package/src/index.js +4 -0
- package/test/test.js +290 -116
- package/demo/demo.js +0 -59
- package/index.cjs +0 -1
- package/index.js +0 -169
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,100 +1,211 @@
|
|
|
1
|
-
#
|
|
1
|
+
# deepbase-redis
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Redis Stack driver for DeepBase.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install deepbase deepbase-redis
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
Requires Redis Stack (includes RedisJSON module):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
docker run -d -p 6379:6379 --name redis redis/redis-stack-server:latest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Note:** Standard Redis won't work - you need Redis Stack for JSON support.
|
|
20
|
+
|
|
21
|
+
## Description
|
|
22
|
+
|
|
23
|
+
Stores data in Redis using the RedisJSON module. Perfect for:
|
|
24
|
+
|
|
25
|
+
- ✅ High-performance caching
|
|
26
|
+
- ✅ Real-time applications
|
|
27
|
+
- ✅ Session storage
|
|
28
|
+
- ✅ Fast reads and writes
|
|
29
|
+
- ✅ In-memory speed with persistence
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import DeepBase from 'deepbase';
|
|
35
|
+
import RedisDriver from 'deepbase-redis';
|
|
36
|
+
|
|
37
|
+
const db = new DeepBase(new RedisDriver({
|
|
38
|
+
url: 'redis://localhost:6379',
|
|
39
|
+
prefix: 'myapp'
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
await db.connect();
|
|
43
|
+
|
|
44
|
+
await db.set('users', 'alice', { name: 'Alice', age: 30 });
|
|
45
|
+
const alice = await db.get('users', 'alice');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Options
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
new RedisDriver({
|
|
52
|
+
url: 'redis://localhost:6379', // Redis connection URL
|
|
53
|
+
prefix: 'db', // Key prefix (or use 'name')
|
|
54
|
+
nidAlphabet: 'ABC...', // Alphabet for ID generation
|
|
55
|
+
nidLength: 10 // Length of generated IDs
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Data Structure
|
|
60
|
+
|
|
61
|
+
Data is stored as Redis JSON keys:
|
|
11
62
|
|
|
12
|
-
|
|
63
|
+
```javascript
|
|
64
|
+
// Keys created
|
|
65
|
+
myapp:users -> { alice: {...}, bob: {...} }
|
|
66
|
+
myapp:config -> { theme: "dark", lang: "en" }
|
|
13
67
|
```
|
|
14
68
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
### RedisJSON Support
|
|
72
|
+
|
|
73
|
+
Uses Redis JSON module for native JSON operations:
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
await db.set('users', 'alice', 'address', { city: 'NYC' });
|
|
77
|
+
// Stored at JSON path: $.alice.address
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Atomic Increment
|
|
81
|
+
|
|
82
|
+
Uses Redis `JSON.NUMINCRBY` for atomic operations:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
await db.inc('stats', 'views', 1);
|
|
86
|
+
await db.dec('stats', 'views', 1);
|
|
20
87
|
```
|
|
21
88
|
|
|
22
|
-
###
|
|
23
|
-
|
|
24
|
-
|
|
89
|
+
### Key Scanning
|
|
90
|
+
|
|
91
|
+
Efficiently scans keys with patterns:
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const allUsers = await db.get('users');
|
|
95
|
+
// Scans all keys matching prefix
|
|
96
|
+
```
|
|
25
97
|
|
|
26
|
-
|
|
27
|
-
|
|
98
|
+
## Three-Tier Architecture
|
|
99
|
+
|
|
100
|
+
Use Redis as a cache layer:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
import DeepBase from '@deepbase/core';
|
|
104
|
+
import MongoDriver from 'deepbase-mongodb';
|
|
105
|
+
import { JsonDriver } from 'deepbase';
|
|
106
|
+
import RedisDriver from '@deepbase/redis';
|
|
107
|
+
|
|
108
|
+
const db = new DeepBase([
|
|
109
|
+
new MongoDriver({ url: 'mongodb://localhost:27017' }), // Primary
|
|
110
|
+
new JsonDriver({ path: './backup' }), // Backup
|
|
111
|
+
new RedisDriver({ url: 'redis://localhost:6379' }) // Cache
|
|
112
|
+
], {
|
|
113
|
+
writeAll: true, // Write to all three
|
|
114
|
+
readFirst: true, // Read from MongoDB first
|
|
115
|
+
failOnPrimaryError: false // Fallback through layers
|
|
116
|
+
});
|
|
28
117
|
```
|
|
29
118
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const path = await mem.add("user", { name: "martin" });
|
|
119
|
+
**Read priority**: MongoDB → JSON → Redis
|
|
120
|
+
**Write replication**: All three updated simultaneously
|
|
33
121
|
|
|
34
|
-
|
|
35
|
-
console.log(path) // [ 'user', 'CqtOILTDUg' ]
|
|
122
|
+
## Connection String Formats
|
|
36
123
|
|
|
37
|
-
|
|
38
|
-
|
|
124
|
+
```javascript
|
|
125
|
+
// Local
|
|
126
|
+
url: 'redis://localhost:6379'
|
|
127
|
+
|
|
128
|
+
// With password
|
|
129
|
+
url: 'redis://:password@localhost:6379'
|
|
130
|
+
|
|
131
|
+
// With database number
|
|
132
|
+
url: 'redis://localhost:6379/0'
|
|
133
|
+
|
|
134
|
+
// Redis Cloud
|
|
135
|
+
url: 'redis://username:password@host:port'
|
|
136
|
+
|
|
137
|
+
// TLS/SSL
|
|
138
|
+
url: 'rediss://host:port'
|
|
39
139
|
```
|
|
40
140
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await mem.inc(...path, "balance", 420);
|
|
141
|
+
## Performance
|
|
142
|
+
|
|
143
|
+
Redis is extremely fast:
|
|
45
144
|
|
|
46
|
-
|
|
47
|
-
|
|
145
|
+
- **Reads**: Sub-millisecond response times
|
|
146
|
+
- **Writes**: Thousands of operations per second
|
|
147
|
+
- **In-memory**: Data stored in RAM with optional persistence
|
|
148
|
+
- **Atomic operations**: Lock-free increments and updates
|
|
149
|
+
|
|
150
|
+
## Use Cases
|
|
151
|
+
|
|
152
|
+
### Session Storage
|
|
153
|
+
```javascript
|
|
154
|
+
const sessions = new DeepBase(new RedisDriver({ prefix: 'session' }));
|
|
155
|
+
await sessions.set(sessionId, 'user', userData);
|
|
48
156
|
```
|
|
49
157
|
|
|
50
|
-
###
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
|
|
158
|
+
### Real-time Stats
|
|
159
|
+
```javascript
|
|
160
|
+
const stats = new DeepBase(new RedisDriver({ prefix: 'stats' }));
|
|
161
|
+
await stats.inc('page', 'views', 1);
|
|
162
|
+
await stats.inc('page', 'unique_visitors', 1);
|
|
54
163
|
```
|
|
55
164
|
|
|
56
|
-
###
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// {
|
|
65
|
-
// config: { lang: 'EN' },
|
|
66
|
-
// user: {
|
|
67
|
-
// CqtOILTDUg: { name: 'martin', balance: 580 },
|
|
68
|
-
// MXOlTBSmEf: { name: 'anya' }
|
|
69
|
-
// }
|
|
70
|
-
// }
|
|
165
|
+
### Cache Layer
|
|
166
|
+
```javascript
|
|
167
|
+
const cache = new DeepBase([
|
|
168
|
+
new RedisDriver({ prefix: 'cache' }),
|
|
169
|
+
new MongoDriver({ url: '...' })
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
// Fast reads from Redis, persistent in MongoDB
|
|
71
173
|
```
|
|
72
174
|
|
|
73
|
-
##
|
|
74
|
-
- 🔍 Easily access and modify nested objects in JSON storage.
|
|
75
|
-
- 📁 Provides an easy-to-use interface for connecting to RedisJSON and performing data operations, saving development.
|
|
76
|
-
- 🌱 Simple and intuitive API for managing complex JSON structures.
|
|
175
|
+
## Best Practices
|
|
77
176
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
-
|
|
177
|
+
1. **Use as cache layer** - Not as primary storage
|
|
178
|
+
2. **Set appropriate TTLs** - Expire old data
|
|
179
|
+
3. **Monitor memory usage** - Redis is in-memory
|
|
180
|
+
4. **Enable persistence** - RDB or AOF for durability
|
|
181
|
+
5. **Use with persistent drivers** - MongoDB or JSON backup
|
|
82
182
|
|
|
83
|
-
##
|
|
84
|
-
Contributions to DeepBase are welcome! If you have an idea or a bug to report, please open an issue. If you would like to contribute to the code, please open a pull request.
|
|
183
|
+
## Persistence Options
|
|
85
184
|
|
|
86
|
-
|
|
87
|
-
|
|
185
|
+
Redis Stack supports:
|
|
186
|
+
- **RDB**: Periodic snapshots
|
|
187
|
+
- **AOF**: Append-only file for durability
|
|
88
188
|
|
|
89
|
-
|
|
189
|
+
Configure in Redis:
|
|
190
|
+
```bash
|
|
191
|
+
docker run -d -p 6379:6379 \
|
|
192
|
+
-v redis-data:/data \
|
|
193
|
+
redis/redis-stack-server:latest \
|
|
194
|
+
--appendonly yes
|
|
195
|
+
```
|
|
90
196
|
|
|
91
|
-
##
|
|
92
|
-
The MIT License (MIT)
|
|
197
|
+
## Error Handling
|
|
93
198
|
|
|
94
|
-
|
|
199
|
+
```javascript
|
|
200
|
+
try {
|
|
201
|
+
await db.connect();
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('Redis connection failed:', error);
|
|
204
|
+
// Fallback to other drivers
|
|
205
|
+
}
|
|
206
|
+
```
|
|
95
207
|
|
|
96
|
-
|
|
208
|
+
## License
|
|
97
209
|
|
|
98
|
-
|
|
210
|
+
MIT - Copyright (c) Martin Clasen
|
|
99
211
|
|
|
100
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,54 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepbase-redis",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "⚡
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "3.0.2",
|
|
4
|
+
"description": "⚡ DeepBase Redis Stack - driver",
|
|
6
5
|
"type": "module",
|
|
7
|
-
"
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"module": "src/index.js",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
"import": "./index.js"
|
|
10
|
+
"import": "./src/index.js"
|
|
12
11
|
}
|
|
13
12
|
},
|
|
14
13
|
"dependencies": {
|
|
15
|
-
"nanoid": "^5.1.5",
|
|
16
14
|
"redis": "^4.7.0"
|
|
17
15
|
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"deepbase": "^3.0.0"
|
|
18
|
+
},
|
|
18
19
|
"scripts": {
|
|
19
|
-
"test": "mocha"
|
|
20
|
+
"test": "mocha test/test.js"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"mocha": "^10.8.2"
|
|
20
24
|
},
|
|
21
25
|
"repository": {
|
|
22
26
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/clasen/
|
|
27
|
+
"url": "git+https://github.com/clasen/DeepBase.git"
|
|
24
28
|
},
|
|
25
29
|
"keywords": [
|
|
30
|
+
"deepbase",
|
|
26
31
|
"redis",
|
|
27
|
-
"redis-json",
|
|
28
32
|
"redis-stack",
|
|
29
|
-
"json",
|
|
33
|
+
"redis-json",
|
|
34
|
+
"driver",
|
|
30
35
|
"database",
|
|
31
36
|
"persist",
|
|
32
37
|
"nested",
|
|
33
|
-
"objects"
|
|
34
|
-
"user-friendly",
|
|
35
|
-
"intuitive",
|
|
36
|
-
"reliable",
|
|
37
|
-
"synchronization",
|
|
38
|
-
"structure",
|
|
39
|
-
"lowdb",
|
|
40
|
-
"clasen"
|
|
38
|
+
"objects"
|
|
41
39
|
],
|
|
42
40
|
"author": "Martin Clasen",
|
|
43
41
|
"license": "MIT",
|
|
44
42
|
"bugs": {
|
|
45
|
-
"url": "https://github.com/clasen/
|
|
46
|
-
},
|
|
47
|
-
"homepage": "https://github.com/clasen/DeepBaseRedis#readme",
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"mocha": "^11.1.0"
|
|
43
|
+
"url": "https://github.com/clasen/DeepBase/issues"
|
|
50
44
|
},
|
|
51
|
-
"
|
|
52
|
-
"test": "test"
|
|
53
|
-
}
|
|
45
|
+
"homepage": "https://github.com/clasen/DeepBase#readme"
|
|
54
46
|
}
|
|
47
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { DeepBaseDriver } from 'deepbase';
|
|
2
|
+
import { createClient } from 'redis';
|
|
3
|
+
import { customAlphabet } from 'nanoid';
|
|
4
|
+
|
|
5
|
+
export class RedisDriver extends DeepBaseDriver {
|
|
6
|
+
constructor({name, prefix, url, ...opts} = {}) {
|
|
7
|
+
super(opts);
|
|
8
|
+
|
|
9
|
+
this.name = name || prefix || "db";
|
|
10
|
+
this.url = url || "redis://localhost:6379";
|
|
11
|
+
|
|
12
|
+
this.nanoid = customAlphabet(this.nidAlphabet, this.nidLength);
|
|
13
|
+
|
|
14
|
+
this.client = createClient({ url: this.url });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async connect() {
|
|
18
|
+
await this.client.connect();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async disconnect() {
|
|
22
|
+
await this.client.disconnect();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async get(...args) {
|
|
26
|
+
if (args.length === 0) {
|
|
27
|
+
const dic = {};
|
|
28
|
+
for (let key of await this._zeroKeys()) {
|
|
29
|
+
dic[key] = await this.get(key);
|
|
30
|
+
}
|
|
31
|
+
return dic;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const key = args.shift();
|
|
35
|
+
const path = args.length == 0 ? "." : args.join(".");
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
return await this.client.json.get(this.name + ":" + key, { path });
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async set(...args) {
|
|
45
|
+
if (args.length < 2) return;
|
|
46
|
+
|
|
47
|
+
const keys = args.slice(0, -1);
|
|
48
|
+
const value = args[args.length - 1];
|
|
49
|
+
|
|
50
|
+
const key = keys.shift();
|
|
51
|
+
const keyPath = keys.length == 0 ? "." : keys.join(".");
|
|
52
|
+
await this._set(key, keyPath, value);
|
|
53
|
+
|
|
54
|
+
return args.slice(0, -1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async inc(...args) {
|
|
58
|
+
const i = args.pop();
|
|
59
|
+
const key = args.shift();
|
|
60
|
+
const path = args.length == 0 ? "." : args.join(".");
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await this.client.json.numIncrBy(this.name + ":" + key, path, i);
|
|
64
|
+
return [key, ...args];
|
|
65
|
+
} catch (error) {
|
|
66
|
+
args.unshift(key);
|
|
67
|
+
return this.upd(...args, n => n + i);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async dec(...args) {
|
|
72
|
+
const i = args.pop();
|
|
73
|
+
args.push(-i);
|
|
74
|
+
return this.inc(...args);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async del(...args) {
|
|
78
|
+
if (args.length === 0) {
|
|
79
|
+
for (let key of await this._zeroKeys()) {
|
|
80
|
+
await this.del(key);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const key = args.shift();
|
|
86
|
+
const path = args.length == 0 ? "." : args.join(".");
|
|
87
|
+
await this.client.json.del(this.name + ":" + key, path);
|
|
88
|
+
|
|
89
|
+
return [key, ...args];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async add(...keys) {
|
|
93
|
+
const value = keys.pop();
|
|
94
|
+
const id = this.nanoid();
|
|
95
|
+
await this.set(...[...keys, id], value);
|
|
96
|
+
return [...keys, id];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async upd(...args) {
|
|
100
|
+
const func = args.pop();
|
|
101
|
+
return this.set(...args, func(await this.get(...args)));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async _set(key, path, value) {
|
|
105
|
+
if (value === undefined) {
|
|
106
|
+
await this.client.json.del(this.name + ":" + key, path);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await this.client.json.set(this.name + ":" + key, path, value);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const keys = path.split('.');
|
|
114
|
+
keys.pop();
|
|
115
|
+
const keyPath = keys.length == 0 ? "." : keys.join(".");
|
|
116
|
+
await this._set(key, keyPath, {});
|
|
117
|
+
await this._set(key, path, value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async _zeroKeys() {
|
|
122
|
+
const scan = {
|
|
123
|
+
TYPE: "ReJSON-RL",
|
|
124
|
+
MATCH: this.name + ":*",
|
|
125
|
+
COUNT: 1000,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const keys = [];
|
|
129
|
+
for await (let key of this.client.scanIterator(scan)) {
|
|
130
|
+
keys.push(key.substring(this.name.length + 1));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return keys;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default RedisDriver;
|
|
138
|
+
|
package/src/index.js
ADDED
package/test/test.js
CHANGED
|
@@ -1,158 +1,332 @@
|
|
|
1
|
-
/* eslint-env mocha */
|
|
2
|
-
|
|
3
|
-
import DeepBase from '../index.js';
|
|
4
1
|
import assert from 'assert';
|
|
2
|
+
import { DeepBase } from '../../core/src/index.js';
|
|
3
|
+
import { RedisDriver } from '../src/RedisDriver.js';
|
|
5
4
|
|
|
6
|
-
describe('
|
|
7
|
-
|
|
5
|
+
describe('RedisDriver', function() {
|
|
6
|
+
let db;
|
|
7
|
+
let testCounter = 0;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
await db.connect();
|
|
12
|
-
});
|
|
9
|
+
// Increase timeout for Redis operations
|
|
10
|
+
this.timeout(10000);
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
beforeEach(async function() {
|
|
13
|
+
testCounter++;
|
|
14
|
+
db = new DeepBase(new RedisDriver({
|
|
15
|
+
url: 'redis://localhost:6379',
|
|
16
|
+
prefix: `test_${testCounter}`
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await db.connect();
|
|
21
|
+
} catch (error) {
|
|
22
|
+
this.skip(); // Skip tests if Redis is not available
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async function() {
|
|
27
|
+
if (db) {
|
|
28
|
+
try {
|
|
29
|
+
await db.del(); // Clear all data
|
|
16
30
|
await db.disconnect();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// Ignore cleanup errors
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('Connection', function() {
|
|
38
|
+
it('should connect to Redis', async function() {
|
|
39
|
+
const driver = db.getDriver(0);
|
|
40
|
+
assert.ok(driver.client);
|
|
41
|
+
assert.ok(driver.client.isOpen);
|
|
17
42
|
});
|
|
43
|
+
});
|
|
18
44
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
it('should overwrite an existing value at the same path', async () => {
|
|
27
|
-
await db.set('foo', 'bar', 'baz');
|
|
28
|
-
await db.set('foo', 'bar', 'qux');
|
|
29
|
-
const qux = await db.get('foo', 'bar')
|
|
30
|
-
assert.deepEqual(qux, 'qux');
|
|
31
|
-
});
|
|
45
|
+
describe('Basic Operations', function() {
|
|
46
|
+
it('should set and get a simple value', async function() {
|
|
47
|
+
await db.set('key1', 'value');
|
|
48
|
+
const result = await db.get('key1');
|
|
49
|
+
assert.strictEqual(result, 'value');
|
|
50
|
+
});
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
it('should set and get nested values', async function() {
|
|
53
|
+
await db.set('user', 'name', 'Alice');
|
|
54
|
+
await db.set('user', 'age', 30);
|
|
55
|
+
|
|
56
|
+
const name = await db.get('user', 'name');
|
|
57
|
+
const age = await db.get('user', 'age');
|
|
58
|
+
|
|
59
|
+
assert.strictEqual(name, 'Alice');
|
|
60
|
+
assert.strictEqual(age, 30);
|
|
61
|
+
});
|
|
39
62
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
it('should get entire nested object', async function() {
|
|
64
|
+
await db.set('user', 'name', 'Bob');
|
|
65
|
+
await db.set('user', 'age', 25);
|
|
66
|
+
|
|
67
|
+
const user = await db.get('user');
|
|
68
|
+
assert.strictEqual(user.name, 'Bob');
|
|
69
|
+
assert.strictEqual(user.age, 25);
|
|
47
70
|
});
|
|
48
71
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
});
|
|
72
|
+
it('should return null for non-existent keys', async function() {
|
|
73
|
+
const result = await db.get('nonexistent');
|
|
74
|
+
assert.strictEqual(result, null);
|
|
75
|
+
});
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
it('should get all keys', async function() {
|
|
78
|
+
await db.set('key1', 'value1');
|
|
79
|
+
await db.set('key2', 'value2');
|
|
80
|
+
|
|
81
|
+
const all = await db.get();
|
|
82
|
+
assert.ok(all.key1);
|
|
83
|
+
assert.ok(all.key2);
|
|
84
|
+
assert.strictEqual(all.key1, 'value1');
|
|
85
|
+
assert.strictEqual(all.key2, 'value2');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
58
88
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
89
|
+
describe('Delete Operations', function() {
|
|
90
|
+
it('should delete a key', async function() {
|
|
91
|
+
await db.set('temp', 'value');
|
|
92
|
+
await db.set('keep', 'value');
|
|
93
|
+
await db.del('temp');
|
|
94
|
+
|
|
95
|
+
const temp = await db.get('temp');
|
|
96
|
+
const keep = await db.get('keep');
|
|
97
|
+
|
|
98
|
+
assert.strictEqual(temp, null);
|
|
99
|
+
assert.strictEqual(keep, 'value');
|
|
64
100
|
});
|
|
65
101
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
102
|
+
it('should delete nested field', async function() {
|
|
103
|
+
await db.set('user', 'name', 'Alice');
|
|
104
|
+
await db.set('user', 'age', 30);
|
|
105
|
+
await db.del('user', 'age');
|
|
106
|
+
|
|
107
|
+
const user = await db.get('user');
|
|
108
|
+
assert.strictEqual(user.name, 'Alice');
|
|
109
|
+
assert.strictEqual(user.age, undefined);
|
|
110
|
+
});
|
|
72
111
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
112
|
+
it('should clear all data', async function() {
|
|
113
|
+
await db.set('key1', 'value1');
|
|
114
|
+
await db.set('key2', 'value2');
|
|
115
|
+
await db.del();
|
|
116
|
+
|
|
117
|
+
const all = await db.get();
|
|
118
|
+
assert.deepStrictEqual(all, {});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
77
121
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
122
|
+
describe('Add Operation', function() {
|
|
123
|
+
it('should add item with auto-generated ID', async function() {
|
|
124
|
+
const path = await db.add('items', 'Charlie');
|
|
125
|
+
|
|
126
|
+
assert.strictEqual(path.length, 2);
|
|
127
|
+
assert.strictEqual(path[0], 'items');
|
|
128
|
+
assert.strictEqual(typeof path[1], 'string');
|
|
129
|
+
assert.strictEqual(path[1].length, 10);
|
|
130
|
+
|
|
131
|
+
const item = await db.get(...path);
|
|
132
|
+
assert.strictEqual(item, 'Charlie');
|
|
85
133
|
});
|
|
86
134
|
|
|
87
|
-
|
|
135
|
+
it('should add multiple items with unique IDs', async function() {
|
|
136
|
+
const path1 = await db.add('items', 'item1');
|
|
137
|
+
const path2 = await db.add('items', 'item2');
|
|
138
|
+
|
|
139
|
+
assert.notStrictEqual(path1[1], path2[1]);
|
|
140
|
+
});
|
|
88
141
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
142
|
+
it('should add complex objects', async function() {
|
|
143
|
+
const path = await db.add('users', { name: 'Alice', age: 30 });
|
|
144
|
+
const user = await db.get(...path);
|
|
145
|
+
|
|
146
|
+
assert.deepStrictEqual(user, { name: 'Alice', age: 30 });
|
|
94
147
|
});
|
|
148
|
+
});
|
|
95
149
|
|
|
96
|
-
|
|
150
|
+
describe('Increment/Decrement', function() {
|
|
151
|
+
it('should increment a value', async function() {
|
|
152
|
+
await db.set('key', 'counter', 10);
|
|
153
|
+
await db.inc('key', 'counter', 5);
|
|
154
|
+
|
|
155
|
+
const result = await db.get('key', 'counter');
|
|
156
|
+
assert.strictEqual(result, 15);
|
|
157
|
+
});
|
|
97
158
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
159
|
+
it('should decrement a value', async function() {
|
|
160
|
+
await db.set('key', 'counter', 20);
|
|
161
|
+
await db.dec('key', 'counter', 8);
|
|
162
|
+
|
|
163
|
+
const result = await db.get('key', 'counter');
|
|
164
|
+
assert.strictEqual(result, 12);
|
|
103
165
|
});
|
|
104
166
|
|
|
105
|
-
|
|
167
|
+
it('should increment nested value', async function() {
|
|
168
|
+
await db.set('user', 'balance', 100);
|
|
169
|
+
await db.inc('user', 'balance', 50);
|
|
170
|
+
|
|
171
|
+
const balance = await db.get('user', 'balance');
|
|
172
|
+
assert.strictEqual(balance, 150);
|
|
173
|
+
});
|
|
106
174
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
175
|
+
it('should handle multiple increments', async function() {
|
|
176
|
+
await db.set('key', 'views', 0);
|
|
177
|
+
await db.inc('key', 'views', 1);
|
|
178
|
+
await db.inc('key', 'views', 1);
|
|
179
|
+
await db.inc('key', 'views', 1);
|
|
180
|
+
|
|
181
|
+
const views = await db.get('key', 'views');
|
|
182
|
+
assert.strictEqual(views, 3);
|
|
183
|
+
});
|
|
114
184
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
185
|
+
it('should increment non-existent value from zero', async function() {
|
|
186
|
+
await db.inc('key', 'new_counter', 10);
|
|
187
|
+
|
|
188
|
+
const result = await db.get('key', 'new_counter');
|
|
189
|
+
assert.strictEqual(result, 10);
|
|
119
190
|
});
|
|
191
|
+
});
|
|
120
192
|
|
|
121
|
-
|
|
193
|
+
describe('Update Operation', function() {
|
|
194
|
+
it('should update value with function', async function() {
|
|
195
|
+
await db.set('key', 'name', 'alice');
|
|
196
|
+
await db.upd('key', 'name', name => name.toUpperCase());
|
|
197
|
+
|
|
198
|
+
const result = await db.get('key', 'name');
|
|
199
|
+
assert.strictEqual(result, 'ALICE');
|
|
200
|
+
});
|
|
122
201
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
202
|
+
it('should update nested value with function', async function() {
|
|
203
|
+
await db.set('user', 'age', 25);
|
|
204
|
+
await db.upd('user', 'age', age => age + 1);
|
|
205
|
+
|
|
206
|
+
const age = await db.get('user', 'age');
|
|
207
|
+
assert.strictEqual(age, 26);
|
|
128
208
|
});
|
|
129
209
|
|
|
130
|
-
|
|
210
|
+
it('should update complex objects', async function() {
|
|
211
|
+
await db.set('config', 'settings', { theme: 'dark', lang: 'en' });
|
|
212
|
+
await db.upd('config', 'settings', settings => ({
|
|
213
|
+
...settings,
|
|
214
|
+
lang: 'es'
|
|
215
|
+
}));
|
|
216
|
+
|
|
217
|
+
const settings = await db.get('config', 'settings');
|
|
218
|
+
assert.strictEqual(settings.theme, 'dark');
|
|
219
|
+
assert.strictEqual(settings.lang, 'es');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('Keys, Values, Entries', function() {
|
|
224
|
+
beforeEach(async function() {
|
|
225
|
+
await db.set('users', 'alice', { age: 30 });
|
|
226
|
+
await db.set('users', 'bob', { age: 25 });
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should get keys', async function() {
|
|
230
|
+
const keys = await db.keys('users');
|
|
231
|
+
assert.strictEqual(keys.length, 2);
|
|
232
|
+
assert.ok(keys.includes('alice'));
|
|
233
|
+
assert.ok(keys.includes('bob'));
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should get values', async function() {
|
|
237
|
+
const values = await db.values('users');
|
|
238
|
+
assert.strictEqual(values.length, 2);
|
|
239
|
+
assert.ok(values.some(v => v.age === 30));
|
|
240
|
+
assert.ok(values.some(v => v.age === 25));
|
|
241
|
+
});
|
|
131
242
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
243
|
+
it('should get entries', async function() {
|
|
244
|
+
const entries = await db.entries('users');
|
|
245
|
+
assert.strictEqual(entries.length, 2);
|
|
246
|
+
assert.ok(entries.some(([k, v]) => k === 'alice' && v.age === 30));
|
|
247
|
+
assert.ok(entries.some(([k, v]) => k === 'bob' && v.age === 25));
|
|
137
248
|
});
|
|
249
|
+
});
|
|
138
250
|
|
|
139
|
-
|
|
251
|
+
describe('Complex Nested Operations', function() {
|
|
252
|
+
it('should handle deeply nested objects', async function() {
|
|
253
|
+
await db.set('app', 'config', 'database', 'host', 'localhost');
|
|
254
|
+
await db.set('app', 'config', 'database', 'port', 6379);
|
|
255
|
+
|
|
256
|
+
const host = await db.get('app', 'config', 'database', 'host');
|
|
257
|
+
const port = await db.get('app', 'config', 'database', 'port');
|
|
258
|
+
|
|
259
|
+
assert.strictEqual(host, 'localhost');
|
|
260
|
+
assert.strictEqual(port, 6379);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle arrays', async function() {
|
|
264
|
+
await db.set('key', 'tags', ['redis', 'cache', 'database']);
|
|
265
|
+
|
|
266
|
+
const tags = await db.get('key', 'tags');
|
|
267
|
+
assert.deepStrictEqual(tags, ['redis', 'cache', 'database']);
|
|
268
|
+
});
|
|
140
269
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
270
|
+
it('should handle mixed types', async function() {
|
|
271
|
+
await db.set('key', 'string', 'text');
|
|
272
|
+
await db.set('key', 'number', 42);
|
|
273
|
+
await db.set('key', 'boolean', true);
|
|
274
|
+
await db.set('key', 'null', null);
|
|
275
|
+
await db.set('key', 'array', [1, 2, 3]);
|
|
276
|
+
|
|
277
|
+
const doc = await db.get('key');
|
|
278
|
+
assert.strictEqual(doc.string, 'text');
|
|
279
|
+
assert.strictEqual(doc.number, 42);
|
|
280
|
+
assert.strictEqual(doc.boolean, true);
|
|
281
|
+
assert.strictEqual(doc.null, null);
|
|
282
|
+
assert.deepStrictEqual(doc.array, [1, 2, 3]);
|
|
146
283
|
});
|
|
284
|
+
});
|
|
147
285
|
|
|
148
|
-
|
|
286
|
+
describe('JSON Path Operations', function() {
|
|
287
|
+
it('should create intermediate objects automatically', async function() {
|
|
288
|
+
await db.set('key', 'level1', 'level2', 'value', 'deep');
|
|
289
|
+
|
|
290
|
+
const result = await db.get('key', 'level1', 'level2', 'value');
|
|
291
|
+
assert.strictEqual(result, 'deep');
|
|
292
|
+
});
|
|
149
293
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
294
|
+
it('should handle object replacement', async function() {
|
|
295
|
+
await db.set('key', 'obj', { old: 'value' });
|
|
296
|
+
await db.set('key', 'obj', { new: 'value' });
|
|
297
|
+
|
|
298
|
+
const obj = await db.get('key', 'obj');
|
|
299
|
+
assert.deepStrictEqual(obj, { new: 'value' });
|
|
155
300
|
});
|
|
301
|
+
});
|
|
156
302
|
|
|
303
|
+
describe('Performance', function() {
|
|
304
|
+
it('should handle rapid sequential operations', async function() {
|
|
305
|
+
const operations = 50;
|
|
306
|
+
|
|
307
|
+
for (let i = 0; i < operations; i++) {
|
|
308
|
+
await db.set('perf', `key${i}`, i);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (let i = 0; i < operations; i++) {
|
|
312
|
+
const value = await db.get('perf', `key${i}`);
|
|
313
|
+
assert.strictEqual(value, i);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should handle parallel operations', async function() {
|
|
318
|
+
const operations = 20;
|
|
319
|
+
const promises = [];
|
|
320
|
+
|
|
321
|
+
for (let i = 0; i < operations; i++) {
|
|
322
|
+
promises.push(db.set('parallel', `key${i}`, i));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await Promise.all(promises);
|
|
326
|
+
|
|
327
|
+
const keys = await db.keys('parallel');
|
|
328
|
+
assert.strictEqual(keys.length, operations);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
});
|
|
157
332
|
|
|
158
|
-
});
|
package/demo/demo.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import DeepBase from "../index.js";
|
|
2
|
-
|
|
3
|
-
const mem = new DeepBase({ name: "demo" });
|
|
4
|
-
|
|
5
|
-
await mem.connect();
|
|
6
|
-
|
|
7
|
-
// Reset
|
|
8
|
-
await mem.del();
|
|
9
|
-
|
|
10
|
-
// SET
|
|
11
|
-
await mem.set("config", "lang", "en");
|
|
12
|
-
|
|
13
|
-
const configLang = await mem.get("config", "lang");
|
|
14
|
-
console.log(configLang); // "en"
|
|
15
|
-
|
|
16
|
-
// ADD
|
|
17
|
-
const path = await mem.add("user", { name: "martin" });
|
|
18
|
-
console.log(path) // [ 'user', 'CqtOILTDUg' ] / CqtOILTDUg is a random string
|
|
19
|
-
|
|
20
|
-
const userName = await mem.get(...path, "name");
|
|
21
|
-
console.log(userName); // "martin"
|
|
22
|
-
|
|
23
|
-
// INC
|
|
24
|
-
await mem.inc(...path, "count", 1);
|
|
25
|
-
await mem.inc(...path, "count", 1);
|
|
26
|
-
|
|
27
|
-
const userBalance = await mem.get(...path, "count");
|
|
28
|
-
console.log(userBalance); // 2
|
|
29
|
-
|
|
30
|
-
await mem.add("user", { name: "anya" });
|
|
31
|
-
|
|
32
|
-
const userIds = await mem.keys("user")
|
|
33
|
-
console.log(userIds) // [ 'CqtOILTDUg', 'MXOlTBSmEf' ]
|
|
34
|
-
|
|
35
|
-
const userValues = await mem.values("user")
|
|
36
|
-
console.log(userValues)
|
|
37
|
-
// [ { name: 'martin', count: 2 }, { name: 'anya' }]
|
|
38
|
-
|
|
39
|
-
// UPDATE
|
|
40
|
-
await mem.upd("config", "lang", v => v.toUpperCase());
|
|
41
|
-
const lang = await mem.get("config", "lang"); // EN
|
|
42
|
-
|
|
43
|
-
console.log(await mem.get())
|
|
44
|
-
|
|
45
|
-
await mem.disconnect();
|
|
46
|
-
// {
|
|
47
|
-
// "config": {
|
|
48
|
-
// "lang": "EN"
|
|
49
|
-
// },
|
|
50
|
-
// "user": {
|
|
51
|
-
// "CqtOILTDUg": {
|
|
52
|
-
// "name": "martin",
|
|
53
|
-
// "count": 2
|
|
54
|
-
// },
|
|
55
|
-
// "MXOlTBSmEf": {
|
|
56
|
-
// "name": "anya"
|
|
57
|
-
// }
|
|
58
|
-
// }
|
|
59
|
-
// }
|
package/index.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('./index.js').default;
|
package/index.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { createClient } from "redis";
|
|
2
|
-
import { customAlphabet } from "nanoid";
|
|
3
|
-
|
|
4
|
-
class DeepBaseRedis {
|
|
5
|
-
|
|
6
|
-
constructor(opts = {}) {
|
|
7
|
-
this.nidAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
8
|
-
this.nidLength = 10;
|
|
9
|
-
this.name = "db";
|
|
10
|
-
this.url = "redis://localhost:6379";
|
|
11
|
-
this.client = createClient({ url: this.url });
|
|
12
|
-
this.nanoid = customAlphabet(this.nidAlphabet, this.nidLength);
|
|
13
|
-
Object.assign(this, opts);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async disconnect() {
|
|
17
|
-
await this.client.disconnect();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async connect() {
|
|
21
|
-
await this.client.connect();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async set(...args) {
|
|
25
|
-
if (args.length < 2) return;
|
|
26
|
-
const keys = args.slice(0, -1);
|
|
27
|
-
const value = args[args.length - 1];
|
|
28
|
-
|
|
29
|
-
const key = keys.shift();
|
|
30
|
-
const keyPath = keys.length == 0 ? "." : keys.join(".");
|
|
31
|
-
await this._set(key, keyPath, value);
|
|
32
|
-
return args.slice(0, -1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async _set(key, path, value) {
|
|
36
|
-
|
|
37
|
-
if (value === undefined) {
|
|
38
|
-
await this.client.json.del(this.name + ":" + key, path);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let r = null;
|
|
43
|
-
try {
|
|
44
|
-
r = await this.client.json.set(this.name + ":" + key, path, value);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (r === null) {
|
|
50
|
-
const keys = path.split('.');
|
|
51
|
-
keys.pop();
|
|
52
|
-
const keyPath = keys.length == 0 ? "." : keys.join(".");
|
|
53
|
-
await this._set(key, keyPath, {});
|
|
54
|
-
await this._set(key, path, value);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async get(...args) {
|
|
59
|
-
|
|
60
|
-
if (args.length === 0) {
|
|
61
|
-
|
|
62
|
-
const dic = {};
|
|
63
|
-
for (let key of await this._zeroKeys()) {
|
|
64
|
-
dic[key] = await this.get(key);
|
|
65
|
-
}
|
|
66
|
-
return dic;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const key = args.shift();
|
|
70
|
-
const path = args.length == 0 ? "." : args.join(".");
|
|
71
|
-
|
|
72
|
-
let r = null;
|
|
73
|
-
try {
|
|
74
|
-
r = await this.client.json.get(this.name + ":" + key, { path });
|
|
75
|
-
} catch (error) {
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
return r;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async keys(...args) {
|
|
82
|
-
const r = await this.get(...args);
|
|
83
|
-
return (r !== null && typeof r === "object") ? Object.keys(r) : [];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async values(...args) {
|
|
87
|
-
const r = await this.get(...args);
|
|
88
|
-
return (r !== null && typeof r === "object") ? Object.values(r) : [];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async entries(...args) {
|
|
92
|
-
const r = await this.get(...args);
|
|
93
|
-
return (r !== null && typeof r === "object") ? Object.entries(r) : [];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async upd(...args) {
|
|
97
|
-
const func = args.pop();
|
|
98
|
-
return this.set(...args, func(await this.get(...args)));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async inc(...args) {
|
|
102
|
-
|
|
103
|
-
const i = args.pop();
|
|
104
|
-
const key = args.shift();
|
|
105
|
-
const path = args.length == 0 ? "." : args.join(".");
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
await this.client.json.numIncrBy(this.name + ":" + key, path, i);
|
|
109
|
-
return args.slice(0, -1);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
args.unshift(key);
|
|
112
|
-
return this.upd(...args, n => n + i);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async dec(...args) {
|
|
117
|
-
const i = args.pop();
|
|
118
|
-
args.push(-i);
|
|
119
|
-
return this.inc(...args);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async _zeroKeys() {
|
|
123
|
-
const scan = {
|
|
124
|
-
TYPE: "ReJSON-RL",
|
|
125
|
-
MATCH: this.name + ":*",
|
|
126
|
-
COUNT: 1000,
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const keys = [];
|
|
130
|
-
for await (let key of this.client.scanIterator(scan)) {
|
|
131
|
-
keys.push(key.substring(this.name.length + 1));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return keys;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async del(...args) {
|
|
138
|
-
|
|
139
|
-
if (args.length === 0) {
|
|
140
|
-
|
|
141
|
-
for (let key of await this._zeroKeys()) {
|
|
142
|
-
await this.del(key);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const key = args.shift();
|
|
147
|
-
const path = args.length == 0 ? "." : args.join(".");
|
|
148
|
-
await this.client.json.del(this.name + ":" + key, path);
|
|
149
|
-
return [key, ...args];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async add(...keys) {
|
|
153
|
-
const value = keys.pop();
|
|
154
|
-
const id = this.nanoid();
|
|
155
|
-
await this.set(...[...keys, id, value]);
|
|
156
|
-
return [...keys, id];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
use(plugin) {
|
|
160
|
-
const prototype = Object.getPrototypeOf(plugin);
|
|
161
|
-
Object.getOwnPropertyNames(prototype).forEach(method => {
|
|
162
|
-
if (method !== "constructor") {
|
|
163
|
-
this[method] = plugin[method].bind(this);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export default DeepBaseRedis;
|