├── .eslintrc.json
├── .github
├── CODEOWNERS
├── images
│ ├── ci.png
│ └── logo.png
└── workflows
│ ├── ci.yml
│ ├── deploybot.yml
│ └── docs-deploy.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── index.ts
├── modules
├── bloom-cmk
│ ├── redisbloom-cmk.commander.ts
│ ├── redisbloom-cmk.ts
│ └── redisbloom-cmk.types.ts
├── bloom-cuckoo
│ ├── redisbloom-cuckoo.commander.ts
│ ├── redisbloom-cuckoo.ts
│ └── redisbloom-cuckoo.types.ts
├── bloom-tdigest
│ ├── redisbloom-tdigest.commander.ts
│ ├── redisbloom-tdigest.ts
│ └── redisbloom-tdigest.types.ts
├── bloom-topk
│ ├── redisbloom-topk.commander.ts
│ ├── redisbloom-topk.ts
│ └── redisbloom-topk.types.ts
├── bloom
│ ├── redisbloom.commander.ts
│ ├── redisbloom.ts
│ └── redisbloom.types.ts
├── module.base.ts
├── redis-ai
│ ├── redis-ai.commander.ts
│ ├── redis-ai.ts
│ └── redis-ai.types.ts
├── redis-modules.ts
├── redisearch
│ ├── redisearch.commander.ts
│ ├── redisearch.helpers.ts
│ ├── redisearch.ts
│ └── redisearch.types.ts
├── redisgears
│ ├── redisgears.commander.ts
│ ├── redisgears.ts
│ └── redisgears.types.ts
├── redisgraph
│ ├── redisgraph.commander.ts
│ ├── redisgraph.ts
│ └── redisgraph.types.ts
├── rejson
│ ├── rejson.commander.ts
│ ├── rejson.ts
│ └── rejson.types.ts
├── ris
│ ├── ris.commander.ts
│ ├── ris.helpers.ts
│ ├── ris.ts
│ └── ris.types.ts
└── rts
│ ├── rts.commander.ts
│ ├── rts.ts
│ └── rts.types.ts
├── package-lock.json
├── package.json
├── readme
├── _config.yml
├── contributing.md
└── doc.md
├── tests
├── data
│ ├── models
│ │ ├── graph.pb
│ │ ├── model1.onnx
│ │ └── sample1.json
│ └── scripts
│ │ └── script.txt
├── module-base.ts
├── redis-ai.ts
├── redisbloom-cmk.ts
├── redisbloom-cuckoo.ts
├── redisbloom-tdigest.ts
├── redisbloom-topk.ts
├── redisbloom.ts
├── redisearch.ts
├── redisgears.ts
├── redisgraph.ts
├── rejson.ts
├── ris.ts
└── rts.ts
├── tsconfig.json
└── tsdoc.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": ["@typescript-eslint"],
4 | "extends": [
5 | "eslint:recommended",
6 | "plugin:@typescript-eslint/recommended"
7 | ],
8 | "ignorePatterns": ["docs/*"],
9 | "overrides": [{
10 | "files": ["*.ts", "*/*.ts", "*/*/*.ts", "*/*/*/*.ts"],
11 | "rules": {
12 | "keyword-spacing": ["error", {
13 | "overrides":{
14 | "for": {
15 | "before": false,
16 | "after": false
17 | },
18 | "while": {
19 | "before": false,
20 | "after": false
21 | },
22 | "catch": {
23 | "before": true,
24 | "after": false
25 | },
26 | "from": {
27 | "before": true,
28 | "after": true
29 | },
30 | "if": {
31 | "before": false,
32 | "after": false
33 | }
34 | }
35 | }]
36 | }
37 | }]
38 | }
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @danitseitlin
2 |
--------------------------------------------------------------------------------
/.github/images/ci.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danitseitlin/redis-modules-sdk-ts/bd3dc8f381a9f16cda1ba09d00710e27f67664e7/.github/images/ci.png
--------------------------------------------------------------------------------
/.github/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danitseitlin/redis-modules-sdk-ts/bd3dc8f381a9f16cda1ba09d00710e27f67664e7/.github/images/logo.png
--------------------------------------------------------------------------------
/.github/workflows/deploybot.yml:
--------------------------------------------------------------------------------
1 | name: DeployBot
2 | on:
3 | push:
4 | branches: [master]
5 | jobs:
6 | deployment:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - name: Setup
11 | run: npm install
12 | - name: Build
13 | run: npm run build
14 | - name: Deploying version
15 | uses: danitseitlin/package-deployer@master
16 | with:
17 | pkg_name: redis-modules-sdk
18 | main_pkg_manager: npm
19 | pkg_managers: '[github, npm]'
20 | npm_access_token: ${{secrets.NPM_AUTH_TOKEN}}
21 | github_access_token: ${{secrets.G_AUTH_TOKEN}}
22 | debug: true
23 |
--------------------------------------------------------------------------------
/.github/workflows/docs-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Docs
2 | on:
3 | push:
4 | branches: [master]
5 | jobs:
6 | deploy:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - name: Setup
11 | run: npm install
12 | - name: Generating Docs
13 | run: npm run generate-docs
14 | - name: Deploy 🚀
15 | uses: JamesIves/github-pages-deploy-action@4.1.7
16 | with:
17 | branch: gh-pages # The branch the action should deploy to.
18 | folder: docs # The folder the action should deploy.
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | yarn.lock
4 | package-lock.json
5 | .DS_Store
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github
2 | node_modules
3 | modules/**
4 | tests
5 | **/tests/*.ts
6 | **/tests/*.js
7 | package-lock.json
8 | index.ts
9 | tsconfig.json
10 | .eslintrc.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | #### A Software development kit for easier connection and execution of Redis Modules commands
23 |
24 |
25 |
26 |
27 |
28 | # A bit about us :speech_balloon:
29 | An open source SDK of all the available Redis modules, built with TypeScript for better typing experiences and better usages.
30 | ## Benefits :zap: :speak_no_evil:
31 | 1. All in 1, all Redis modules are covered in 1 project
32 | 2. Amazing :fire: typing experiences, all types are well documented and easy to use!
33 | 3. Easy to find your Redis command, each Redis module command has a referenced function in it's class!
34 |
35 | # Let's get started :memo:
36 | ### Installing latest version:
37 | ```
38 | npm install redis-modules-sdk@latest
39 | ```
40 |
41 | ### Versions & Releases
42 | * A list of existing versions can be found [here](https://www.npmjs.com/package/redis-modules-sdk-ts?activeTab=versions)
43 | * A list of releases will be found [here](https://github.com/danitseitlin/redis-modules-sdk-ts/releases)
44 |
45 | # Documentation :book:
46 | Come and read our documentation [here](https://danitseitlin.github.io/redis-modules-sdk-ts/modules.html) before starting
47 |
48 | # (RAIO) Redis "All in One"! :scream:
49 | A class built for integrating more than one Redis module without creating more than one class!
50 | ```
51 | const client = new Redis(....);
52 | await client.connect();
53 | await client.ai_module_tensorset(...);
54 | await client.ris_module_add(...);
55 | await client.disconnect();
56 | ```
57 | All modules are supported! :fire:
58 |
59 | # Sandbox Playground
60 | Want to play around with the code before installing it? Feel free to do so [here](https://playcode.io/974244)
61 |
62 | # Supported modules :dark_sunglasses:
63 | * [ReJSON](https://github.com/RedisJSON/RedisJSON)
64 | * [RedisTimeSeries](https://github.com/RedisTimeSeries/RedisTimeSeries) (RTS)
65 | * [RediSearch](https://github.com/RediSearch/RediSearch)
66 | * [RedisGraph](https://github.com/RedisGraph/RedisGraph)
67 | * [RedisGears](https://github.com/RedisGears/RedisGears)
68 | * [RedisBloom](https://github.com/RedisBloom/RedisBloom)
69 | * [RedisAI](https://github.com/RedisAI/RedisAI)
70 | * [RedisIntervalSets](https://github.com/danitseitlin/redis-interval-sets)
71 |
72 | # Contributing :raised_hands:
73 | Interested in contributing? awesome! start with reading our guidelines [here](https://github.com/danitseitlin/redis-modules-sdk-ts/blob/master/readme/contributing.md#contributing-guide)
74 |
75 | # Supporters :open_hands:
76 | [](https://github.com/danitseitlin/redis-modules-sdk-ts/stargazers)
77 | # Forkers :fork_and_knife:
78 | [](https://github.com/danitseitlin/redis-modules-sdk-ts/network/members)
79 |
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | export { RedisModules } from './modules/redis-modules';
2 | /* ** Redis JSON ***/
3 | export { ReJSON } from './modules/rejson/rejson';
4 | export { ReJSONGetParameters } from './modules/rejson/rejson.types'
5 | /* ** Redis Graph ***/
6 | export { RedisGraph } from './modules/redisgraph/redisgraph';
7 | export { GraphConfigInfo } from './modules/redisgraph/redisgraph.types';
8 | /* ** Redis Gears ***/
9 | export { RedisGears } from './modules/redisgears/redisgears';
10 | export { RGGetExecutionParameters, RGPyExecuteParameters } from './modules/redisgears/redisgears.types';
11 | /* ** Redis Bloom ***/
12 | export { RedisBloom } from './modules/bloom/redisbloom';
13 | export { BFInsertParameters, BFResponse, BFReserveParameter } from './modules/bloom/redisbloom.types';
14 | /* ** Redis Bloom TopK ***/
15 | export { RedisBloomTopK } from './modules/bloom-topk/redisbloom-topk';
16 | export { TOPKIncrbyItems, TOPKResponse } from './modules/bloom-topk/redisbloom-topk.types';
17 | /* ** Redis Bloom Cuckoo ***/
18 | export { RedisBloomCuckoo } from './modules/bloom-cuckoo/redisbloom-cuckoo';
19 | export { CFInsertParameters, CFResponse, CFReserveParameters } from './modules/bloom-cuckoo/redisbloom-cuckoo.types';
20 | /* ** Redis Bloom CMK ***/
21 | export { RedisBloomCMK } from './modules/bloom-cmk/redisbloom-cmk';
22 | export { CMKIncrbyItems } from './modules/bloom-cmk/redisbloom-cmk.types';
23 | /* ** RedisTimeSeries ***/
24 | export { RedisTimeSeries as RTS, RedisTimeSeries } from './modules/rts/rts';
25 | export {
26 | TSCreateOptions, TSLabel, TSAddOptions, TSKeySet, TSIncrbyDecrbyOptions, TSOptions, TSCreateRule, TSAggregationType,
27 | TSRangeOptions, TSMRangeOptions, TSInfo, TSAlignType
28 | } from './modules/rts/rts.types';
29 | /* ** Redis Search ***/
30 | export { Redisearch } from './modules/redisearch/redisearch';
31 | export {
32 | FTCreateParameters, FTFieldOptions, FTSchemaField, FTSearchParameters, FTAggregateParameters, FTSugAddParameters, FTSugGetParameters, FTSpellCheck,
33 | FTFieldType, FTConfig, FTInfo, FTIndexType, FTSort, FTExpression, FTSortByProperty, FTReduce, FTSearchArrayResponse, FTSearchResponse, FTSpellCheckResponse,
34 | FTAggregateResponse, FTAggregateResponseItem
35 | } from './modules/redisearch/redisearch.types';
36 | /* ** Redis AI ***/
37 | export { RedisAI } from './modules/redis-ai/redis-ai';
38 | export {
39 | AIBackend, AIDagExecuteParameters, AIDevice, AIModel, AIScript, AIScriptInfo, AIScriptSetParameters, AITensor, AITensorInfo, AIModelExecute,
40 | AIModelSetParameters, AIScriptExecuteParameters
41 | } from './modules/redis-ai/redis-ai.types';
42 | /* ** RedisIntervalSets ***/
43 | export { RedisIntervalSets } from './modules/ris/ris';
44 | export { RedisIntervalSet } from './modules/ris/ris.types';
--------------------------------------------------------------------------------
/modules/bloom-cmk/redisbloom-cmk.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import { CMKIncrbyItems } from "./redisbloom-cmk.types";
3 |
4 | export class BloomCmkCommander {
5 | /**
6 | * Initializes a Count-Min Sketch to dimensions specified by user.
7 | * @param key The name of the sketch.
8 | * @param width The number of counter in each array. Reduces the error size.
9 | * @param depth The number of counter-arrays. Reduces the probability for an error of a certain size (percentage of total count).
10 | */
11 | initbydim(key: string, width: number, depth: number): CommandData {
12 | return {
13 | command: 'CMS.INITBYDIM',
14 | args: [key, width, depth]
15 | }
16 | }
17 |
18 | /**
19 | * Initializes a Count-Min Sketch to accommodate requested capacity.
20 | * @param key The name of the sketch.
21 | * @param errorSize Estimate size of error. The error is a percent of total counted items. This effects the width of the sketch.
22 | * @param probability The desired probability for inflated count.
23 | */
24 | initbyprob(key: string, errorSize: number, probability: number): CommandData {
25 | return {
26 | command: 'CMS.INITBYPROB',
27 | args: [key, errorSize, probability]
28 | }
29 | }
30 |
31 | /**
32 | * Increases the count of item's by increment.
33 | * @param key The name of the sketch.
34 | * @param items A list of item and increment set's
35 | */
36 | incrby(key: string, items: CMKIncrbyItems[]): CommandData {
37 | let args = [key];
38 | for(const item of items){
39 | args = args.concat([item.name.toString(), item.increment.toString()])
40 | }
41 | return {
42 | command: 'CMS.INCRBY',
43 | args: args
44 | }
45 | }
46 |
47 | /**
48 | * Returns count for item's.
49 | * @param key The name of the sketch.
50 | * @param items A list of items.
51 | */
52 | query(key: string, items: string[]): CommandData {
53 | return {
54 | command: 'CMS.QUERY',
55 | args: [key].concat(items)
56 | }
57 | }
58 |
59 | /**
60 | * Merges several sketches into one sketch.
61 | * @param dest The name of destination sketch.
62 | * @param numKeys The number of sketches to be merged.
63 | * @param sources The names of source sketches to be merged.
64 | * @param weights A multiple of each sketch. Default =1.
65 | */
66 | merge(dest: string, numKeys: number, sources: string[], weights?: number[]): CommandData {
67 | let args = [dest, numKeys];
68 | args = args.concat(sources);
69 | if(weights !== undefined && weights.length > 0) {
70 | args.push('WEIGHTS');
71 | for(const weight of weights){
72 | args.push(`${weight}`);
73 | }
74 | }
75 | return {
76 | command: 'CMS.MERGE',
77 | args: args
78 | }
79 | }
80 |
81 | /**
82 | * Returning information about a key
83 | * @param key The key of the 'CMS.INFO' command
84 | */
85 | info(key: string): CommandData {
86 | return {
87 | command: 'CMS.INFO',
88 | args: [key]
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/modules/bloom-cmk/redisbloom-cmk.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { BloomCmkCommander } from './redisbloom-cmk.commander';
4 | import { CMKIncrbyItems } from './redisbloom-cmk.types';
5 |
6 | export class RedisBloomCMK extends Module {
7 |
8 | private bloomCmkCommander = new BloomCmkCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisBloomCMK.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Initializes a Count-Min Sketch to dimensions specified by user.
34 | * @param key The name of the sketch.
35 | * @param width The number of counter in each array. Reduces the error size.
36 | * @param depth The number of counter-arrays. Reduces the probability for an error of a certain size (percentage of total count).
37 | */
38 | async initbydim(key: string, width: number, depth: number): Promise<'OK'> {
39 | const command = this.bloomCmkCommander.initbydim(key, width, depth)
40 | return await this.sendCommand(command);
41 | }
42 |
43 | /**
44 | * Initializes a Count-Min Sketch to accommodate requested capacity.
45 | * @param key The name of the sketch.
46 | * @param errorSize Estimate size of error. The error is a percent of total counted items. This effects the width of the sketch.
47 | * @param probability The desired probability for inflated count.
48 | */
49 | async initbyprob(key: string, errorSize: number, probability: number): Promise<'OK'> {
50 | const command = this.bloomCmkCommander.initbyprob(key, errorSize, probability);
51 | return await this.sendCommand(command);
52 | }
53 |
54 | /**
55 | * Increases the count of item's by increment.
56 | * @param key The name of the sketch.
57 | * @param items A list of item and increment set's
58 | */
59 | async incrby(key: string, items: CMKIncrbyItems[]): Promise {
60 | const command = this.bloomCmkCommander.incrby(key, items);
61 | return await this.sendCommand(command);
62 | }
63 |
64 | /**
65 | * Returns count for item's.
66 | * @param key The name of the sketch.
67 | * @param items A list of items.
68 | */
69 | async query(key: string, items: string[]): Promise {
70 | const command = this.bloomCmkCommander.query(key, items);
71 | return await this.sendCommand(command);
72 | }
73 |
74 | /**
75 | * Merges several sketches into one sketch.
76 | * @param dest The name of destination sketch.
77 | * @param numKeys The number of sketches to be merged.
78 | * @param sources The names of source sketches to be merged.
79 | * @param weights A multiple of each sketch. Default =1.
80 | */
81 | async merge(dest: string, numKeys: number, sources: string[], weights?: number[]): Promise<'OK'> {
82 | const command = this.bloomCmkCommander.merge(dest, numKeys, sources, weights);
83 | return await this.sendCommand(command);
84 | }
85 |
86 | /**
87 | * Returning information about a key
88 | * @param key The key of the 'CMS.INFO' command
89 | */
90 | async info(key: string): Promise {
91 | const command = this.bloomCmkCommander.info(key);
92 | return await this.sendCommand(command);
93 | }
94 | }
--------------------------------------------------------------------------------
/modules/bloom-cmk/redisbloom-cmk.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The sets of the incrby items (and increments)
3 | * @param name The item name which counter to be increased.
4 | * @param increment The counter to be increased by this integer.
5 | */
6 | export type CMKIncrbyItems = {
7 | name: string,
8 | increment: number
9 | }
--------------------------------------------------------------------------------
/modules/bloom-cuckoo/redisbloom-cuckoo.commander.ts:
--------------------------------------------------------------------------------
1 | import { CFReserveParameters, CFInsertParameters } from "./redisbloom-cuckoo.types";
2 | import { CommandData } from "../module.base";
3 |
4 | export class BloomCuckooCommander {
5 |
6 | /**
7 | * Creating an empty Bloom Cuckoo filter with a given initial capacity.
8 | * @param key The key under which the filter is to be found
9 | * @param capacity The number of entries you intend to add to the filter. Performance will begin to degrade after adding more items than this number. The actual degradation will depend on how far the limit has been exceeded. Performance will degrade linearly as the number of entries grow exponentially.
10 | * @param options The additional optional parameters
11 | */
12 | reserve(key: string, capacity: number, options?: CFReserveParameters): CommandData {
13 | let args = [key, capacity];
14 | if(options && options.bucketSize)
15 | args = args.concat(['BUCKETSIZE', options.bucketSize])
16 | if(options && options.maxIteractions)
17 | args = args.concat(['MAXITERATIONS', options.maxIteractions])
18 | if(options && options.expansion)
19 | args = args.concat(['EXPANSION', options.expansion])
20 | return {
21 | command: 'CF.RESERVE',
22 | args: args
23 | }
24 | }
25 |
26 | /**
27 | * Adding an item to the cuckoo filter, creating the filter if it does not exist.
28 | * @param key The name of the filter
29 | * @param item The item to add
30 | */
31 | add(key: string, item: string): CommandData {
32 | return {
33 | command: 'CF.ADD',
34 | args: [key, item]
35 | }
36 | }
37 |
38 | /**
39 | * Adding an item to a cuckoo filter if the item did not exist previously.
40 | * @param key The name of the filter
41 | * @param item The item to add
42 | */
43 | addnx(key: string, item: string): CommandData {
44 | return {
45 | command: 'CF.ADDNX',
46 | args: [key, item]
47 | }
48 | }
49 |
50 | /**
51 | * Adding one or more items to a cuckoo filter, allowing the filter to be created with a custom capacity if it does not yet exist.
52 | * @param key The name of the filter
53 | * @param items Begin the list of items to add
54 | * @param options The additional optional parameters of the 'CF.INSERT' command
55 | */
56 | insert(key: string, items: string[], options?: CFInsertParameters): CommandData {
57 | let args = [key];
58 | if(options !== undefined && options.capacity !== undefined)
59 | args = args.concat(['CAPACITY', options.capacity.toString()]);
60 | if(options !== undefined && options.nocreate !== undefined)
61 | args.push('NOCREATE');
62 | args = args.concat(['ITEMS']).concat(items)
63 | return {
64 | command: 'CF.INSERT',
65 | args: args
66 | }
67 | }
68 |
69 | /**
70 | * Adding one or more items to a cuckoo filter, allowing the filter to be created with a custom capacity if it does not yet exist.
71 | * @param key The name of the filter
72 | * @param items The items of the 'CF.INSERT' command
73 | * @param options The additional optional parameters of the 'CF.INSERTNX' command
74 | */
75 | insertnx(key: string, items: string[], options?: CFInsertParameters): CommandData {
76 | let args = [key];
77 | if(options !== undefined && options.capacity !== undefined)
78 | args = args.concat(['CAPACITY', options.capacity.toString()]);
79 | if(options !== undefined && options.nocreate !== undefined)
80 | args.push('NOCREATE');
81 | args = args.concat(['ITEMS']).concat(items);
82 | return {
83 | command: 'CF.INSERTNX',
84 | args: args
85 | }
86 | }
87 |
88 | /**
89 | * Determining whether an item may exist in the Cuckoo Filter or not.
90 | * @param key The name of the filter
91 | * @param item The item to check for
92 | */
93 | exists(key: string, item: string): CommandData {
94 | return {
95 | command: 'CF.EXISTS',
96 | args: [key, item]
97 | }
98 | }
99 |
100 | /**
101 | * Deleting an item once from the filter. If the item exists only once, it will be removed from the filter.
102 | * @param key The name of the filter
103 | * @param item The item to delete from the filter
104 | */
105 | del(key: string, item: string): CommandData {
106 | return {
107 | command: 'CF.DEL',
108 | args: [key, item]
109 | }
110 | }
111 |
112 | /**
113 | * Returning the number of times an item may be in the filter.
114 | * @param key The name of the filter
115 | * @param item The item to count
116 | */
117 | count(key: string, item: string): CommandData {
118 | return {
119 | command: 'CF.COUNT',
120 | args: [key, item]
121 | }
122 | }
123 |
124 | /**
125 | * Begining an incremental save of the Cuckoo filter
126 | * @param key The name of the filter
127 | * @param iterator Iterator value. This is either 0, or the iterator from a previous invocation of this command
128 | */
129 | scandump(key: string, iterator: number): CommandData {
130 | return {
131 | command: 'CF.SCANDUMP',
132 | args: [key, iterator]
133 | }
134 | }
135 |
136 | /**
137 | * Restoring a filter previously saved using SCANDUMP.
138 | * @param key The name of the key to restore
139 | * @param iterator The iterator value associated with data (returned by SCANDUMP )
140 | * @param data The current data chunk (returned by SCANDUMP )
141 | */
142 | loadchunk(key: string, iterator: number, data: string): CommandData {
143 | return {
144 | command: 'CF.LOADCHUNK',
145 | args: [key, iterator, data]
146 | }
147 | }
148 |
149 | /**
150 | * Returning information about a key
151 | * @param key The name of the filter
152 | */
153 | info(key: string): CommandData {
154 | return {
155 | command: 'CF.INFO',
156 | args: [key]
157 | }
158 | }
159 | }
--------------------------------------------------------------------------------
/modules/bloom-cuckoo/redisbloom-cuckoo.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { BloomCuckooCommander } from './redisbloom-cuckoo.commander';
4 | import { CFInsertParameters, CFReserveParameters, CFResponse } from './redisbloom-cuckoo.types';
5 |
6 | export class RedisBloomCuckoo extends Module {
7 |
8 | private bloomCuckooCommander = new BloomCuckooCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisBloomCuckoo.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Creating an empty Bloom Cuckoo filter with a given initial capacity.
34 | * @param key The key under which the filter is to be found
35 | * @param capacity The number of entries you intend to add to the filter. Performance will begin to degrade after adding more items than this number. The actual degradation will depend on how far the limit has been exceeded. Performance will degrade linearly as the number of entries grow exponentially.
36 | * @param options The additional optional parameters
37 | */
38 | async reserve(key: string, capacity: number, options?: CFReserveParameters): Promise<'OK'> {
39 | const command = this.bloomCuckooCommander.reserve(key, capacity, options)
40 | return await this.sendCommand(command);
41 | }
42 |
43 | /**
44 | * Adding an item to the cuckoo filter, creating the filter if it does not exist.
45 | * @param key The name of the filter
46 | * @param item The item to add
47 | */
48 | async add(key: string, item: string): Promise {
49 | const command = this.bloomCuckooCommander.add(key, item);
50 | return await this.sendCommand(command);
51 | }
52 |
53 | /**
54 | * Adding an item to a cuckoo filter if the item did not exist previously.
55 | * @param key The name of the filter
56 | * @param item The item to add
57 | */
58 | async addnx(key: string, item: string): Promise {
59 | const command = this.bloomCuckooCommander.addnx(key, item);
60 | return await this.sendCommand(command);
61 | }
62 |
63 | /**
64 | * Adding one or more items to a cuckoo filter, allowing the filter to be created with a custom capacity if it does not yet exist.
65 | * @param key The name of the filter
66 | * @param items Begin the list of items to add
67 | * @param options The additional optional parameters of the 'CF.INSERT' command
68 | */
69 | async insert(key: string, items: string[], options?: CFInsertParameters): Promise {
70 | const command = this.bloomCuckooCommander.insert(key, items, options);
71 | return await this.sendCommand(command);
72 | }
73 |
74 | /**
75 | * Adding one or more items to a cuckoo filter, allowing the filter to be created with a custom capacity if it does not yet exist.
76 | * @param key The name of the filter
77 | * @param items The items of the 'CF.INSERT' command
78 | * @param options The additional optional parameters of the 'CF.INSERTNX' command
79 | */
80 | async insertnx(key: string, items: string[], options?: CFInsertParameters): Promise {
81 | const command = this.bloomCuckooCommander.insertnx(key, items, options);
82 | return await this.sendCommand(command);
83 | }
84 |
85 | /**
86 | * Determining whether an item may exist in the Cuckoo Filter or not.
87 | * @param key The name of the filter
88 | * @param item The item to check for
89 | */
90 | async exists(key: string, item: string): Promise {
91 | const command = this.bloomCuckooCommander.exists(key, item);
92 | return await this.sendCommand(command);
93 | }
94 |
95 | /**
96 | * Deleting an item once from the filter. If the item exists only once, it will be removed from the filter.
97 | * @param key The name of the filter
98 | * @param item The item to delete from the filter
99 | */
100 | async del(key: string, item: string): Promise {
101 | const command = this.bloomCuckooCommander.del(key, item);
102 | return await this.sendCommand(command);
103 | }
104 |
105 | /**
106 | * Returning the number of times an item may be in the filter.
107 | * @param key The name of the filter
108 | * @param item The item to count
109 | */
110 | async count(key: string, item: string): Promise {
111 | const command = this.bloomCuckooCommander.count(key, item);
112 | return await this.sendCommand(command);
113 | }
114 |
115 | /**
116 | * Begining an incremental save of the Cuckoo filter
117 | * @param key The name of the filter
118 | * @param iterator Iterator value. This is either 0, or the iterator from a previous invocation of this command
119 | */
120 | async scandump(key: string, iterator: number): Promise {
121 | const command = this.bloomCuckooCommander.scandump(key, iterator);
122 | return await this.sendCommand(command);
123 | }
124 |
125 | /**
126 | * Restoring a filter previously saved using SCANDUMP.
127 | * @param key The name of the key to restore
128 | * @param iterator The iterator value associated with data (returned by SCANDUMP )
129 | * @param data The current data chunk (returned by SCANDUMP )
130 | */
131 | async loadchunk(key: string, iterator: number, data: string): Promise<'OK'> {
132 | const command = this.bloomCuckooCommander.loadchunk(key, iterator, data);
133 | return await this.sendCommand(command);
134 | }
135 |
136 | /**
137 | * Returning information about a key
138 | * @param key The name of the filter
139 | */
140 | async info(key: string): Promise {
141 | const command = this.bloomCuckooCommander.info(key);
142 | return await this.sendCommand(command);
143 | }
144 | }
--------------------------------------------------------------------------------
/modules/bloom-cuckoo/redisbloom-cuckoo.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The additional optional parameter of the 'CF.INSERT' command
3 | * @param capacity The 'CAPACITY' argument. If specified, should be followed by the desired capacity for the filter to be created. This parameter is ignored if the filter already exists. If the filter is automatically created and this parameter is absent, then the default capacity (specified at the module-level) is used.
4 | * @param nocreate The 'NOCREATE' argument. If specified, indicates that the filter should not be created if it does not already exist. If the filter does not yet exist, an error is returned rather than creating it automatically. This may be used where a strict separation between filter creation and filter addition is desired.
5 | */
6 | export type CFInsertParameters = {
7 | capacity?: number,
8 | nocreate?: boolean,
9 | }
10 |
11 | /**
12 | * The response of the CF commands
13 | * @param 1 Stands for 'true'
14 | * @param 0 Stands for 'false'
15 | */
16 | export type CFResponse = '1' | '0';
17 |
18 | /**
19 | * The additional optional parameters of the 'CF.RESERVE' command
20 | * @param bucketSize Number of items in each bucket. A higher bucket size value improves the fill rate but also causes a higher error rate and slightly slower performance.
21 | * @param maxIteractions Number of attempts to swap items between buckets before declaring filter as full and creating an additional filter. A low value is better for performance and a higher number is better for filter fill rate.
22 | * @param expansion When a new filter is created, its size is the size of the current filter multiplied by expansion . Expansion is rounded to the next 2^n number.
23 | */
24 | export type CFReserveParameters = {
25 | bucketSize?: number,
26 | maxIteractions?: number,
27 | expansion?: number
28 | }
--------------------------------------------------------------------------------
/modules/bloom-tdigest/redisbloom-tdigest.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import { TDigestAddParameters, TDigestMergeOptions } from "./redisbloom-tdigest.types";
3 |
4 | export class BloomTdigestCommander {
5 | /**
6 | * Allocate the memory and initialize the t-digest
7 | * @param key The name of the sketch
8 | * @param compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. See the further notes bellow.
9 | * @returns OK on success, error otherwise
10 | */
11 | create(key: string, compression?: number): CommandData {
12 | return {
13 | command: 'TDIGEST.CREATE',
14 | args: [key, 'COMPRESSION', `${compression}`]
15 | }
16 | }
17 |
18 | /**
19 | * Reset the sketch to zero - empty out the sketch and re-initialize it
20 | * @param key The name of the sketch
21 | * @returns OK on success, error otherwise
22 | */
23 | reset(key: string): CommandData {
24 | return {
25 | command: 'TDIGEST.RESET',
26 | args: [key]
27 | }
28 | }
29 |
30 | /**
31 | * Adds one or more samples to a sketch
32 | * @param key The name of the sketch
33 | * @param parameters The parameters of the command
34 | * @returns OK on success, error otherwise
35 | */
36 | add(key: string, parameters: TDigestAddParameters[]): CommandData {
37 | let args = [key]
38 | for(const pair of parameters){
39 | args = args.concat([`${pair.value}`, `${pair.weight}`])
40 | }
41 | return {
42 | command: 'TDIGEST.ADD',
43 | args: args
44 | }
45 | }
46 |
47 | /**
48 | * Merges all of the values from 'from' keys to 'destination-key' sketch.
49 | * @param destinationKey Sketch to copy values to.
50 | * @param sourceKeys Sketch to copy values from, this can be a string for 1 key or an array of keys.
51 | * @param options.numberOfKeys Number of sketch(es) to copy observation values from. If not passed, it will set a default based on sourceKeys parameter.
52 | * @param options.compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. If no value is passed, the used compression will the maximal value among all inputs.
53 | * @param options.override If destination already exists, it is overwritten.
54 | * @returns OK on success, error otherwise
55 | */
56 | merge(destinationKey: string, sourceKeys: string | string[], options?: TDigestMergeOptions): CommandData {
57 | const numkeys = options?.numberOfKeys ? options?.numberOfKeys: Array.isArray(sourceKeys) ? sourceKeys.length: 1;
58 | let args = [destinationKey, `${numkeys}`]
59 | if(Array.isArray(sourceKeys)) {
60 | args = args.concat(sourceKeys)
61 | }
62 | else {
63 | args.push(sourceKeys)
64 | }
65 |
66 | if(options?.compression) {
67 | args = args.concat(['COMPRESSION', `${options?.compression}`])
68 | }
69 |
70 | if(options?.override === true) {
71 | args.push('OVERRIDE')
72 | }
73 | return {
74 | command: 'TDIGEST.MERGE',
75 | args: args
76 | }
77 | }
78 |
79 | /**
80 | * Get minimum value from the sketch. Will return DBL_MAX if the sketch is empty
81 | * @param key The name of the sketch
82 | * @returns DBL_MAX if the sketch is empty
83 | */
84 | min(key: string): CommandData {
85 | return {
86 | command: 'TDIGEST.MIN',
87 | args: [key]
88 | }
89 | }
90 |
91 | /**
92 | * Get maximum value from the sketch. Will return DBL_MIN if the sketch is empty
93 | * @param key The name of the sketch
94 | * @returns DBL_MIN if the sketch is empty
95 | */
96 | max(key: string): CommandData {
97 | return {
98 | command: 'TDIGEST.MAX',
99 | args: [key]
100 | }
101 | }
102 |
103 | /**
104 | * Returns an estimate of the cutoff such that a specified fraction of the data added to this TDigest would be less than or equal to the cutoff
105 | * @param key The name of the sketch
106 | * @param quantile The desired fraction ( between 0 and 1 inclusively )
107 | * @returns Double value estimate of the cutoff such that a specified fraction of the data added to this TDigest would be less than or equal to the cutoff
108 | */
109 | quantile(key: string, quantile: number): CommandData {
110 | return {
111 | command: 'TDIGEST.QUANTILE',
112 | args: [key, quantile]
113 | }
114 | }
115 |
116 | /**
117 | * Returns the fraction of all points added which are <= value
118 | * @param key The name of the sketch
119 | * @param value Upper limit for which the fraction of all points added which are <= value
120 | * @returns Returns compression, capacity, total merged and unmerged nodes, the total compressions made up to date on that key, and merged and unmerged weight
121 | */
122 | cdf(key: string, value: number): CommandData {
123 | return {
124 | command: 'TDIGEST.CDF',
125 | args: [key, value]
126 | }
127 | }
128 |
129 | /**
130 | * Returns compression, capacity, total merged and unmerged nodes, the total compressions made up to date on that key, and merged and unmerged weight.
131 | * @param key The name of the sketch
132 | */
133 | info(key: string): CommandData {
134 | return {
135 | command: 'TDIGEST.INFO',
136 | args: [key]
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/modules/bloom-tdigest/redisbloom-tdigest.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { BloomTdigestCommander } from './redisbloom-tdigest.commander';
4 | import { TDigestAddParameters, TDigestInfo, TDigestMergeOptions } from './redisbloom-tdigest.types';
5 |
6 | export class RedisBloomTDigest extends Module {
7 |
8 | private bloomTdigestCommander = new BloomTdigestCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisBloomTDigest.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Allocate the memory and initialize the t-digest
34 | * @param key The name of the sketch
35 | * @param compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. If no value is passed by default the compression will be 100.
36 | * @returns OK on success, error otherwise
37 | */
38 | async create(key: string, compression?: number): Promise<'OK'> {
39 | const command = this.bloomTdigestCommander.create(key, compression);
40 | return await this.sendCommand(command);
41 | }
42 |
43 | /**
44 | * Reset the sketch to zero - empty out the sketch and re-initialize it
45 | * @param key The name of the sketch
46 | * @returns OK on success, error otherwise
47 | */
48 | async reset(key: string): Promise<'OK'> {
49 | const command = this.bloomTdigestCommander.reset(key);
50 | return await this.sendCommand(command);
51 | }
52 |
53 | /**
54 | * Adds one or more samples to a sketch
55 | * @param key The name of the sketch
56 | * @param parameters The parameters of the command
57 | * @returns OK on success, error otherwise
58 | */
59 | async add(key: string, parameters: TDigestAddParameters[]): Promise<'OK'> {
60 | const command = this.bloomTdigestCommander.add(key, parameters);
61 | return await this.sendCommand(command);
62 | }
63 |
64 | /**
65 | * Merges all of the values from 'from' keys to 'destination-key' sketch.
66 | * @param destinationKey Sketch to copy values to.
67 | * @param sourceKeys Sketch to copy values from, this can be a string for 1 key or an array of keys.
68 | * @param options.numberOfKeys Number of sketch(es) to copy observation values from. If not passed, it will set a default based on sourceKeys parameter.
69 | * @param options.compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. If no value is passed, the used compression will the maximal value among all inputs.
70 | * @param options.override If destination already exists, it is overwritten.
71 | */
72 | async merge(destinationKey: string, sourceKeys: string | string[], options?: TDigestMergeOptions): Promise<'OK'> {
73 | const command = this.bloomTdigestCommander.merge(destinationKey, sourceKeys, options);
74 | return await this.sendCommand(command);
75 | }
76 |
77 | /**
78 | * Get minimum value from the sketch. Will return DBL_MAX if the sketch is empty
79 | * @param key The name of the sketch
80 | * @returns DBL_MAX if the sketch is empty
81 | */
82 | async min(key: string): Promise {
83 | const command = this.bloomTdigestCommander.min(key);
84 | return await this.sendCommand(command);
85 | }
86 |
87 | /**
88 | * Get maximum value from the sketch. Will return DBL_MIN if the sketch is empty
89 | * @param key The name of the sketch
90 | * @returns DBL_MIN if the sketch is empty
91 | */
92 | async max(key: string): Promise {
93 | const command = this.bloomTdigestCommander.max(key);
94 | return await this.sendCommand(command);
95 | }
96 |
97 | /**
98 | * Returns an estimate of the cutoff such that a specified fraction of the data added to this TDigest would be less than or equal to the cutoff
99 | * @param key The name of the sketch
100 | * @param quantile The desired fraction ( between 0 and 1 inclusively )
101 | * @returns Double value estimate of the cutoff such that a specified fraction of the data added to this TDigest would be less than or equal to the cutoff
102 | */
103 | async quantile(key: string, quantile: number): Promise {
104 | const command = this.bloomTdigestCommander.quantile(key, quantile);
105 | return await this.sendCommand(command);
106 | }
107 |
108 | /**
109 | * Returns the fraction of all points added which are <= value
110 | * @param key The name of the sketch
111 | * @param value Upper limit for which the fraction of all points added which are <= value
112 | * @returns Returns compression, capacity, total merged and unmerged nodes, the total compressions made up to date on that key, and merged and unmerged weight
113 | */
114 | async cdf(key: string, value: number): Promise {
115 | const command = this.bloomTdigestCommander.cdf(key, value);
116 | return await this.sendCommand(command);
117 | }
118 |
119 | /**
120 | * Returns compression, capacity, total merged and unmerged nodes, the total compressions made up to date on that key, and merged and unmerged weight.
121 | * @param key The name of the sketch
122 | */
123 | async info(key: string): Promise {
124 | const command = this.bloomTdigestCommander.info(key);
125 | const response = await this.sendCommand(command);
126 | return this.handleResponse(response)
127 | }
128 | }
--------------------------------------------------------------------------------
/modules/bloom-tdigest/redisbloom-tdigest.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The parameters of the 'TDIGEST.ADD' command
3 | * @param value The value to add
4 | * @param weight The weight of this point
5 | */
6 | export type TDigestAddParameters = {
7 | value: number,
8 | weight: number
9 | }
10 |
11 | /**
12 | * The response of the 'TDIGEST.INFO' command
13 | * @param compression The compression
14 | * @param capacity The capacity
15 | * @param 'Merged nodes' The merged nodes
16 | * @param 'Unmerged nodes' The unmerged nodes
17 | * @param 'Merged weight' The merged weight
18 | * @param 'Unmerged weight' The unmerged weight
19 | * @param 'Total compressions' The total compressions
20 | */
21 | export type TDigestInfo = {
22 | Compression: number,
23 | Capacity: number,
24 | 'Merged nodes': number,
25 | 'Unmerged nodes': number,
26 | 'Merged weight': string,
27 | 'Unmerged weight': string,
28 | 'Total compressions': number
29 | }
30 |
31 | /**
32 | * The optional parameters of the 'TDIGEST.MERGE' command
33 | * @param numberOfKeys Number of sketch(es) to copy observation values from. If not passed, it will set a default based on sourceKeys parameter.
34 | * @param compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. If no value is passed, the used compression will the maximal value among all inputs.
35 | * @param override If destination already exists, it is overwritten.
36 | */
37 | export type TDigestMergeOptions = {
38 | numberOfKeys?: number,
39 | compression?: number,
40 | override?: boolean
41 | }
--------------------------------------------------------------------------------
/modules/bloom-topk/redisbloom-topk.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import { TOPKIncrbyItems } from "./redisbloom-topk.types";
3 |
4 | export class BloomTopkCommander {
5 |
6 | /**
7 | * Initializing a TopK with specified parameters
8 | * @param key The key under which the sketch is to be found.
9 | * @param topk The number of top occurring items to keep.
10 | * @param width The number of counters kept in each array.
11 | * @param depth The number of arrays.
12 | * @param decay The probability of reducing a counter in an occupied bucket. It is raised to power of it's counter (decay ^ bucket[i].counter). Therefore, as the counter gets higher, the chance of a reduction is being reduced.
13 | */
14 | reserve(key: string, topk: number, width: number, depth: number, decay: number): CommandData {
15 | return {
16 | command: 'TOPK.RESERVE',
17 | args: [key, topk, width, depth, decay]
18 | }
19 | }
20 |
21 | /**
22 | * Adding an item to the data structure.
23 | * @param key Name of sketch where item is added.
24 | * @param items Item/s to be added.
25 | */
26 | add(key: string, items: (number | string)[]): CommandData {
27 | const args: (number | string)[] = [key].concat(items as string[]);
28 | return {
29 | command: 'TOPK.ADD',
30 | args: args
31 | }
32 | }
33 |
34 | /**
35 | * Increases the count of item's by increment.
36 | * @param key The name of the sketch.
37 | * @param items A list of item and increment set's
38 | */
39 | incrby(key: string, items: TOPKIncrbyItems[]): CommandData {
40 | let args = [key];
41 | for(const item of items) {
42 | args = args.concat([item.name.toString(), item.increment.toString()])
43 | }
44 | return {
45 | command: 'TOPK.INCRBY',
46 | args: args
47 | }
48 | }
49 |
50 | /**
51 | * Checking whether an item is one of Top-K items.
52 | * @param key Name of sketch where item is queried.
53 | * @param items Item/s to be queried.
54 | */
55 | query(key: string, items: (string | number)[]): CommandData {
56 | const args = [key].concat(items as string[]);
57 | return {
58 | command: 'TOPK.QUERY',
59 | args: args
60 | }
61 | }
62 |
63 | /**
64 | * Returning count for an item.
65 | * @param key Name of sketch where item is counted.
66 | * @param items Item/s to be counted.
67 | */
68 | count(key: string, items: (string | number)[]): CommandData {
69 | const args = [key].concat(items as string[]);
70 | return {
71 | command: 'TOPK.COUNT',
72 | args: args
73 | }
74 | }
75 |
76 | /**
77 | * Returning full list of items in Top K list.
78 | * @param key Name of sketch where item is counted.
79 | */
80 | list(key: string): CommandData {
81 | return {
82 | command: 'TOPK.LIST',
83 | args: [key]
84 | }
85 | }
86 |
87 | /**
88 | * Returning information about a key
89 | * @param key Name of sketch.
90 | */
91 | info(key: string): CommandData {
92 | return {
93 | command: 'TOPK.INFO',
94 | args: [key]
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/modules/bloom-topk/redisbloom-topk.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { BloomTopkCommander } from './redisbloom-topk.commander';
4 | import { TOPKIncrbyItems, TOPKResponse } from './redisbloom-topk.types';
5 |
6 | export class RedisBloomTopK extends Module {
7 |
8 | private bloomTopkCommander = new BloomTopkCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisBloomTopK.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Initializing a TopK with specified parameters
34 | * @param key The key under which the sketch is to be found.
35 | * @param topk The number of top occurring items to keep.
36 | * @param width The number of counters kept in each array.
37 | * @param depth The number of arrays.
38 | * @param decay The probability of reducing a counter in an occupied bucket. It is raised to power of it's counter (decay ^ bucket[i].counter). Therefore, as the counter gets higher, the chance of a reduction is being reduced.
39 | */
40 | async reserve(key: string, topk: number, width: number, depth: number, decay: number): Promise<'OK'> {
41 | const command = this.bloomTopkCommander.reserve(key, topk, width, depth, decay);
42 | return await this.sendCommand(command);
43 | }
44 |
45 | /**
46 | * Adding an item to the data structure.
47 | * @param key Name of sketch where item is added.
48 | * @param items Item/s to be added.
49 | */
50 | async add(key: string, items: (number | string)[]): Promise {
51 | const command = this.bloomTopkCommander.add(key, items);
52 | return await this.sendCommand(command);
53 | }
54 |
55 | /**
56 | * Increases the count of item's by increment.
57 | * @param key The name of the sketch.
58 | * @param items A list of item and increment set's
59 | */
60 | async incrby(key: string, items: TOPKIncrbyItems[]): Promise {
61 | const command = this.bloomTopkCommander.incrby(key, items);
62 | return await this.sendCommand(command);
63 |
64 | }
65 |
66 | /**
67 | * Checking whether an item is one of Top-K items.
68 | * @param key Name of sketch where item is queried.
69 | * @param items Item/s to be queried.
70 | */
71 | async query(key: string, items: (string | number)[]): Promise {
72 | const command = this.bloomTopkCommander.query(key, items);
73 | return await this.sendCommand(command);
74 | }
75 |
76 | /**
77 | * Returning count for an item.
78 | * @param key Name of sketch where item is counted.
79 | * @param items Item/s to be counted.
80 | */
81 | async count(key: string, items: (string | number)[]): Promise {
82 | const command = this.bloomTopkCommander.count(key, items);
83 | return await this.sendCommand(command);
84 | }
85 |
86 | /**
87 | * Returning full list of items in Top K list.
88 | * @param key Name of sketch where item is counted.
89 | */
90 | async list(key: string): Promise<(string | number)[]> {
91 | const command = this.bloomTopkCommander.list(key);
92 | return await this.sendCommand(command);
93 | }
94 |
95 | /**
96 | * Returning information about a key
97 | * @param key Name of sketch.
98 | */
99 | async info(key: string): Promise<(string | number)[]> {
100 | const command = this.bloomTopkCommander.info(key);
101 | return await this.sendCommand(command);
102 | }
103 | }
--------------------------------------------------------------------------------
/modules/bloom-topk/redisbloom-topk.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The response of the TOPK commands
3 | * @param 1 Stands for 'true'
4 | * @param 0 Stands for 'false'
5 | */
6 | export type TOPKResponse = '1' | '0';
7 |
8 | /**
9 | * The sets of the incrby items (and increments)
10 | * @param item The item name which counter to be increased.
11 | * @param increment The counter to be increased by this integer.
12 | */
13 | export type TOPKIncrbyItems = {
14 | name: string | number,
15 | increment: number
16 | }
--------------------------------------------------------------------------------
/modules/bloom/redisbloom.commander.ts:
--------------------------------------------------------------------------------
1 | import { BFReserveParameter, BFInsertParameters } from "./redisbloom.types";
2 | import { CommandData } from "../module.base";
3 |
4 | export class BloomCommander {
5 | /**
6 | * Creating an empty Bloom filter with a given desired error ratio and initial capacity.
7 | * @param key The key under which the filter is to be found
8 | * @param errorRate The desired probability for false positives. This should be a decimal value between 0 and 1. For example, for a desired false positive rate of 0.1% (1 in 1000), error_rate should be set to 0.001. The closer this number is to zero, the greater the memory consumption per item and the more CPU usage per operation.
9 | * @param capacity The number of entries you intend to add to the filter. Performance will begin to degrade after adding more items than this number. The actual degradation will depend on how far the limit has been exceeded. Performance will degrade linearly as the number of entries grow exponentially.
10 | * @param options The additional optional parameters
11 | */
12 | reserve(key: string, errorRate: number, capacity: number, options?: BFReserveParameter): CommandData {
13 | const args = [key, errorRate, capacity];
14 | if(options !== undefined && options.expansion !== undefined)
15 | args.push(options.expansion);
16 | if(options !== undefined && options.nonscaling === true)
17 | args.push('NONSCALING');
18 | return {
19 | command: 'BF.RESERVE',
20 | args: args
21 | }
22 | }
23 |
24 | /**
25 | * Adding an item to the Bloom Filter, creating the filter if it does not yet exist.
26 | * @param key The key of the 'BF.ADD' command
27 | * @param item The item of the 'BF.ADD' command
28 | */
29 | add(key: string, item: string): CommandData {
30 | return {
31 | command: 'BF.ADD',
32 | args: [key, item]
33 | }
34 | }
35 |
36 | /**
37 | * Adding one or more items to the Bloom Filter, creating the filter if it does not yet exist. This command operates identically to BF.ADD except it allows multiple inputs and returns multiple values * @param key
38 | * @param items The items of the 'BF.MADD' command
39 | */
40 | madd(key: string, items: string[]): CommandData {
41 | return {
42 | command: 'BF.MADD',
43 | args: [key].concat(items)
44 | }
45 | }
46 |
47 | /**
48 | * Adding one or more items to the bloom filter, by default creating it if it does not yet exist. There are several arguments which may be used to modify this behavior.
49 | * @param key The key of the 'BF.INSERT' command
50 | * @param items The items of the 'BF.INSERT' command
51 | * @param options The additional optional parameters of the 'BF.INSERT' command
52 | */
53 | insert(key: string, items: string[], options?: BFInsertParameters): CommandData {
54 | let args = [key];
55 | if(options !== undefined && options.capacity !== undefined)
56 | args = args.concat(['CAPACITY', options.capacity.toString()]);
57 | if(options !== undefined && options.error !== undefined)
58 | args = args.concat(['ERROR', options.error]);
59 | if(options !== undefined && options.expansion !== undefined)
60 | args = args.concat(['EXPANSION', options.expansion]);
61 | if(options !== undefined && options.nocreate !== undefined)
62 | args.push('NOCREATE');
63 | if(options !== undefined && options.noscaling !== undefined)
64 | args.push('NOSCALING');
65 | args.push('ITEMS')
66 | args = args.concat(items)
67 | return {
68 | command: 'BF.INSERT',
69 | args: args
70 | }
71 | }
72 |
73 | /**
74 | * Determining whether an item may exist in the Bloom Filter or not.
75 | * @param key The key of the 'BF.EXISTS' command
76 | * @param item The key of the 'BF.EXISTS' command
77 | */
78 | exists(key: string, item: string): CommandData {
79 | return {
80 | command: 'BF.EXISTS',
81 | args: [key, item]
82 | }
83 | }
84 |
85 | /**
86 | * Determining if one or more items may exist in the filter or not.
87 | * @param key The key of the 'BF.MEXISTS' command
88 | * @param items The items of the 'BF.MEXISTS' command
89 | */
90 | mexists(key: string, items: string[]): CommandData {
91 | return {
92 | command: 'BF.MEXISTS',
93 | args: [key].concat(items)
94 | }
95 | }
96 |
97 | /**
98 | * Begining an incremental save of the bloom filter
99 | * @param key The key of the 'BF.SCANDUMP' command
100 | * @param iterator The iterator of the 'BF.SCANDUMP' command
101 | */
102 | scandump(key: string, iterator: number): CommandData {
103 | return {
104 | command: 'BF.SCANDUMP',
105 | args: [key, iterator]
106 | }
107 | }
108 |
109 | /**
110 | * Restoring a filter previously saved using SCANDUMP.
111 | * @param key The key of the 'BF.LOADCHUNK' command
112 | * @param iterator The iterator of the 'BF.LOADCHUNK' command
113 | * @param data The data of the 'BF.LOADCHUNK' command
114 | */
115 | loadchunk(key: string, iterator: number, data: string): CommandData {
116 | return {
117 | command: 'BF.LOADCHUNK',
118 | args: [key, iterator, data]
119 | }
120 | }
121 |
122 | /**
123 | * Returning information about a key
124 | * @param key The key of the 'BF.INFO' command
125 | */
126 | info(key: string): CommandData {
127 | return {
128 | command: 'BF.INFO',
129 | args: [key]
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/modules/bloom/redisbloom.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { BFReserveParameter, BFResponse, BFInsertParameters } from './redisbloom.types';
3 | import { Module, RedisModuleOptions } from '../module.base';
4 | import { BloomCommander } from './redisbloom.commander';
5 |
6 | export class RedisBloom extends Module {
7 |
8 | private bloomCommander = new BloomCommander();
9 | /**
10 | * Initializing the module object
11 | * @param clusterNodes The nodes of the cluster
12 | * @param moduleOptions The additional module options
13 | * @param moduleOptions.isHandleError If to throw error on error
14 | * @param moduleOptions.showDebugLogs If to print debug logs
15 | * @param clusterOptions The options of the clusters
16 | */
17 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
18 | /**
19 | * Initializing the module object
20 | * @param redisOptions The options of the redis database
21 | * @param moduleOptions The additional module options
22 | * @param moduleOptions.isHandleError If to throw error on error
23 | * @param moduleOptions.showDebugLogs If to print debug logs
24 | */
25 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
26 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
27 | super(RedisBloom.name, options, moduleOptions, clusterOptions)
28 | }
29 |
30 | /**
31 | * Creating an empty Bloom filter with a given desired error ratio and initial capacity.
32 | * @param key The key under which the filter is to be found
33 | * @param errorRate The desired probability for false positives. This should be a decimal value between 0 and 1. For example, for a desired false positive rate of 0.1% (1 in 1000), error_rate should be set to 0.001. The closer this number is to zero, the greater the memory consumption per item and the more CPU usage per operation.
34 | * @param capacity The number of entries you intend to add to the filter. Performance will begin to degrade after adding more items than this number. The actual degradation will depend on how far the limit has been exceeded. Performance will degrade linearly as the number of entries grow exponentially.
35 | * @param options The additional optional parameters
36 | */
37 | async reserve(key: string, errorRate: number, capacity: number, options?: BFReserveParameter): Promise<'OK'> {
38 | const command = this.bloomCommander.reserve(key, errorRate, capacity, options);
39 | return await this.sendCommand(command);
40 | }
41 |
42 | /**
43 | * Adding an item to the Bloom Filter, creating the filter if it does not yet exist.
44 | * @param key The key of the 'BF.ADD' command
45 | * @param item The item of the 'BF.ADD' command
46 | */
47 | async add(key: string, item: string): Promise {
48 | const command = this.bloomCommander.add(key, item);
49 | return await this.sendCommand(command);
50 | }
51 |
52 | /**
53 | * Adding one or more items to the Bloom Filter, creating the filter if it does not yet exist. This command operates identically to BF.ADD except it allows multiple inputs and returns multiple values * @param key
54 | * @param items The items of the 'BF.MADD' command
55 | */
56 | async madd(key: string, items: string[]): Promise {
57 | const command = this.bloomCommander.madd(key, items);
58 | return await this.sendCommand(command);
59 | }
60 |
61 | /**
62 | * Adding one or more items to the bloom filter, by default creating it if it does not yet exist. There are several arguments which may be used to modify this behavior.
63 | * @param key The key of the 'BF.INSERT' command
64 | * @param items The items of the 'BF.INSERT' command
65 | * @param options The additional optional parameters of the 'BF.INSERT' command
66 | */
67 | async insert(key: string, items: string[], options?: BFInsertParameters): Promise {
68 | const command = this.bloomCommander.insert(key, items, options);
69 | return await this.sendCommand(command);
70 | }
71 |
72 | /**
73 | * Determining whether an item may exist in the Bloom Filter or not.
74 | * @param key The key of the 'BF.EXISTS' command
75 | * @param item The key of the 'BF.EXISTS' command
76 | */
77 | async exists(key: string, item: string): Promise {
78 | const command = this.bloomCommander.exists(key, item);
79 | return await this.sendCommand(command);
80 | }
81 |
82 | /**
83 | * Determining if one or more items may exist in the filter or not.
84 | * @param key The key of the 'BF.MEXISTS' command
85 | * @param items The items of the 'BF.MEXISTS' command
86 | */
87 | async mexists(key: string, items: string[]): Promise {
88 | const command = this.bloomCommander.mexists(key, items);
89 | return await this.sendCommand(command);
90 | }
91 |
92 | /**
93 | * Begining an incremental save of the bloom filter
94 | * @param key The key of the 'BF.SCANDUMP' command
95 | * @param iterator The iterator of the 'BF.SCANDUMP' command
96 | */
97 | async scandump(key: string, iterator: number): Promise {
98 | const command = this.bloomCommander.scandump(key, iterator);
99 | return await this.sendCommand(command);
100 | }
101 |
102 | /**
103 | * Restoring a filter previously saved using SCANDUMP.
104 | * @param key The key of the 'BF.LOADCHUNK' command
105 | * @param iterator The iterator of the 'BF.LOADCHUNK' command
106 | * @param data The data of the 'BF.LOADCHUNK' command
107 | */
108 | async loadchunk(key: string, iterator: number, data: string): Promise<'OK'> {
109 | const command = this.bloomCommander.loadchunk(key, iterator, data);
110 | return await this.sendCommand(command);
111 | }
112 |
113 | /**
114 | * Returning information about a key
115 | * @param key The key of the 'BF.INFO' command
116 | */
117 | async info(key: string): Promise {
118 | const command = this.bloomCommander.info(key);
119 | return await this.sendCommand(command);
120 | }
121 | }
--------------------------------------------------------------------------------
/modules/bloom/redisbloom.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The additional optional parameter of the 'BF.INSERT' command
3 | * @param capacity The 'CAPACITY' argument. If specified, should be followed by the desired capacity for the filter to be created. This parameter is ignored if the filter already exists. If the filter is automatically created and this parameter is absent, then the default capacity (specified at the module-level) is used.
4 | * @param error The 'ERROR' argument. If specified, should be followed by the the error ratio of the newly created filter if it does not yet exist. If the filter is automatically created and ERROR is not specified then the default module-level error rate is used.
5 | * @param expansion The 'EXPANSION' argument. If a new sub-filter is created, its size will be the size of the current filter multiplied by expansion . Default expansion value is 2. This means each subsequent sub-filter will be twice as large as the previous one.
6 | * @param nocreate The 'NOCREATE' argument. If specified, indicates that the filter should not be created if it does not already exist. If the filter does not yet exist, an error is returned rather than creating it automatically. This may be used where a strict separation between filter creation and filter addition is desired.
7 | * @param noscaling The 'NOSCALING' argument. Prevents the filter from creating additional sub-filters if initial capacity is reached. Non-scaling filters requires slightly less memory than their scaling counterparts.
8 | */
9 | export type BFInsertParameters = {
10 | capacity?: number,
11 | error?: string,
12 | expansion?: string,
13 | nocreate?: boolean,
14 | noscaling?: boolean
15 | }
16 |
17 | /**
18 | * The additional optional parameters of the 'BF.RESERVE' command
19 | * @param expansion If a new sub-filter is created, its size will be the size of the current filter multiplied by expansion.
20 | * @param nonscaling Prevents the filter from creating additional sub-filters if initial capacity is reached.
21 | */
22 | export type BFReserveParameter = {
23 | expansion?: number,
24 | nonscaling?: boolean
25 | }
26 |
27 | /**
28 | * The response of the BF commands
29 | * @param 1 Stands for 'true'
30 | * @param 0 Stands for 'false'
31 | */
32 | export type BFResponse = '1' | '0';
--------------------------------------------------------------------------------
/modules/module.base.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import * as IORedis from 'ioredis';
3 |
4 | export class Module {
5 | public name: string
6 | public redis: IORedis.Redis;
7 | public redisOptions: IORedis.RedisOptions
8 | public cluster: IORedis.Cluster;
9 | public clusterNodes: IORedis.ClusterNode[]
10 | public clusterOptions: IORedis.ClusterOptions
11 | public isHandleError: boolean;
12 | public showDebugLogs: boolean;
13 | public returnRawResponse: boolean;
14 |
15 | /**
16 | * Initializing the module object
17 | * @param name The name of the module
18 | * @param clusterNodes The nodes of the cluster
19 | * @param moduleOptions The additional module options
20 | * @param moduleOptions.isHandleError If to throw error on error
21 | * @param moduleOptions.showDebugLogs If to print debug logs
22 | * @param clusterOptions The options of the clusters
23 | */
24 | constructor(name: string, clusterNodes: IORedis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: IORedis.ClusterOptions, )
25 | /**
26 | * Initializing the module object
27 | * @param name The name of the module
28 | * @param redisOptions The options of the redis database
29 | * @param moduleOptions The additional module options
30 | * @param moduleOptions.isHandleError If to throw error on error
31 | * @param moduleOptions.showDebugLogs If to print debug logs
32 | */
33 | constructor(name: string, redisOptions: IORedis.RedisOptions, moduleOptions?: RedisModuleOptions)
34 | constructor(name: string, options: IORedis.RedisOptions | IORedis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: IORedis.ClusterOptions) {
35 | this.name = name;
36 | //If it's a list of cluster nodes
37 | if(Array.isArray(options)){
38 | this.clusterNodes = options as IORedis.ClusterNode[];
39 | }
40 | else {
41 | this.redisOptions = options as IORedis.RedisOptions;
42 | }
43 | this.isHandleError = moduleOptions && moduleOptions.isHandleError ? moduleOptions.isHandleError: true;
44 | this.showDebugLogs = moduleOptions && moduleOptions.showDebugLogs ? moduleOptions.showDebugLogs: false;
45 | this.returnRawResponse = moduleOptions?.returnRawResponse ? moduleOptions.returnRawResponse: false;
46 | this.clusterOptions = clusterOptions ? clusterOptions: undefined;
47 | }
48 |
49 | /**
50 | * Connecting to the Redis database with the module
51 | */
52 | async connect(): Promise {
53 | if(this.clusterNodes){
54 | this.cluster = new IORedis.Cluster(this.clusterNodes, this.clusterOptions);
55 | }
56 | else {
57 | this.redis = new IORedis.default(this.redisOptions);
58 | }
59 | }
60 |
61 | /**
62 | * Disconnecting from the Redis database with the module
63 | */
64 | async disconnect(): Promise {
65 | if(this.clusterNodes){
66 | await this.cluster.quit();
67 | }
68 | else {
69 | await this.redis.quit();
70 | }
71 | }
72 |
73 | /**
74 | * Running a Redis command
75 | * @param data The command data of a command to send. Consists of command and args.
76 | */
77 | async sendCommand(data: CommandData): Promise {
78 | try {
79 | if(this.showDebugLogs){
80 | console.log(`${this.name}: Running command ${data.command} with arguments: ${data.args}`);
81 | }
82 | const response = this.clusterNodes ?
83 | await this.cluster.cluster.call(data.command, data.args)
84 | : await this.redis.call(data.command, data.args);
85 |
86 | if(this.showDebugLogs){
87 | console.log(`${this.name}: command ${data.command} responded with ${response}`);
88 | }
89 | return response;
90 | } catch(error) {
91 | return this.handleError(`${this.name} class (${data.command.split(' ')[0]}): ${error}`)
92 | }
93 | }
94 |
95 | /**
96 | * Handling a error
97 | * @param error The message of the error
98 | */
99 | handleError(error: string): any {
100 | if(this.isHandleError)
101 | throw new Error(error);
102 | return error;
103 | }
104 |
105 | /**
106 | * Simpilizing the response of the Module command
107 | * @param response The array response from the module
108 | */
109 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
110 | handleResponse(response: any): any {
111 | if(this.returnRawResponse === true) {
112 | return response
113 | }
114 | //If not an array/object
115 | if(
116 | (typeof response === 'string' ||
117 | typeof response === 'number' ||
118 | (Array.isArray(response) && response.length % 2 === 1 && response.length > 1 && !this.isOnlyTwoDimensionalArray(response)) ||
119 | (Array.isArray(response) && response.length === 0))
120 | ) {
121 | return response;
122 | }
123 | else if(Array.isArray(response) && response.length === 1) {
124 | return this.handleResponse(response[0])
125 | }
126 | else if(Array.isArray(response) && response.length > 1 && this.isOnlyTwoDimensionalArray(response)) {
127 | return this.handleResponse(this.reduceArrayDimension(response))
128 | }
129 | const obj = {}
130 | //If is an array/obj we will build it
131 | for(let i = 0; i < response.length; i += 2) {
132 | if(response[i + 1] !== '' && response[i + 1] !== undefined) {
133 | if(Array.isArray(response[i + 1]) && this.isOnlyTwoDimensionalArray(response[i + 1])) {
134 | obj[response[i]] = this.reduceArrayDimension(response[i + 1]);
135 | continue;
136 | }
137 | const value = (Array.isArray(response[i + 1]) ? this.handleResponse(response[i + 1]) : response[i + 1])
138 | obj[response[i]] = value;
139 | }
140 | }
141 | return obj
142 | }
143 |
144 | /**
145 | * Check if array is fully two dimensional. Only items in the array are arrays.
146 | * @param array The potential two dimensional array
147 | */
148 | isOnlyTwoDimensionalArray(array: any[]): boolean {
149 | return array.filter(item => Array.isArray(item)).length === array.length;
150 | }
151 |
152 | /**
153 | * Reducing an array by one level. i.e. from two dimensional to 1 dimensional.
154 | * @param array The potentional two dimensional array
155 | */
156 | reduceArrayDimension(array: any[][]): any[] {
157 | let newArray = [];
158 | array.forEach(singleArr => {
159 | newArray = newArray.concat(singleArr)
160 | })
161 | return newArray;
162 | }
163 |
164 | /**
165 | * Formatting given param value to string
166 | * @param paramValue The given param value
167 | * @returns A param value converted to string
168 | */
169 | paramToString(paramValue: string): string {
170 | if(paramValue == null) return 'null';
171 | const paramType = typeof paramValue;
172 | if(paramType == 'string') {
173 | let strValue = "";
174 | paramValue = paramValue.replace(/[\\"']/g, '\\$&');
175 | if(paramValue[0] != '"') strValue += "'";
176 | strValue += paramValue;
177 | if(!paramValue.endsWith('"') || paramValue.endsWith("\\\"")) strValue += "'";
178 | return strValue;
179 | }
180 |
181 | if(Array.isArray(paramValue)) {
182 | const stringsArr = new Array(paramValue.length);
183 | for(let i = 0; i < paramValue.length; i++) {
184 | stringsArr[i] = this.paramToString(paramValue[i]);
185 | }
186 | return ["[", stringsArr.join(", "), "]"].join("");
187 | }
188 | return paramValue;
189 | }
190 | }
191 |
192 | /**
193 | * Logging a message
194 | * @param level The level of the log
195 | * @param msg The log message
196 | */
197 | export function log(level: LogLevel, msg: string): void {
198 | if(level === LogLevel.DEBUG && this.showDebugLogs === true) {
199 | console.debug(msg)
200 | }
201 | else if(level === LogLevel.Error) {
202 | throw new Error(msg)
203 | }
204 | else {
205 | console.log(msg);
206 | }
207 | }
208 |
209 | /**
210 | * Enum representing the log levels
211 | */
212 | export enum LogLevel {
213 | INFO,
214 | DEBUG,
215 | Error
216 | }
217 |
218 | /**
219 | * The Redis module class options
220 | */
221 | export type RedisModuleOptions = {
222 | /**
223 | * If to throw exception in case of error
224 | */
225 | isHandleError?: boolean,
226 | /**
227 | * If to print debug logs
228 | */
229 | showDebugLogs?: boolean,
230 | /**
231 | * In some cases functions are parsing responses into a JS object, setting true here will disable that ability.
232 | */
233 | returnRawResponse?: boolean
234 | }
235 |
236 | /**
237 | * The command object send to the sendCommand function
238 | */
239 | export type CommandData = {
240 | /**
241 | * The full Redis command
242 | */
243 | command: string,
244 | /**
245 | * A list of arguments passed to the Redis command
246 | */
247 | args?: any[]
248 | }
--------------------------------------------------------------------------------
/modules/redis-ai/redis-ai.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import {
3 | AIBackend, AIDagExecuteParameters, AIDevice, AIModelExecute, AIModelSetParameters, AIScriptExecuteParameters, AIScriptSetParameters, TensorType
4 | } from "./redis-ai.types";
5 |
6 | export class RedisAICommander {
7 |
8 | /**
9 | * Setting a tensor
10 | * @param key The tensor's key name
11 | * @param type The tensor's data type can be one of: FLOAT , DOUBLE , INT8 , INT16 , INT32 , INT64 , UINT8 or UINT16
12 | * @param data The tensor's data (binary/numberic)
13 | * @param shapes One or more dimensions, or the number of elements per axis, for the tensor
14 | */
15 | tensorset(key: string, type: TensorType, shapes: number[], data?: number[] | Buffer[]): CommandData {
16 | const args: (number | string | Buffer)[] = [key, type];
17 | shapes.forEach(shape => {args.push(shape.toString())});
18 | if(data !== undefined) {
19 | args.push(data instanceof Buffer ? 'BLOB': 'VALUES');
20 | data.forEach((value: (number | string | Buffer)) => {args.push(value.toString())});
21 | }
22 | return {
23 | command: 'AI.TENSORSET',
24 | args: args
25 | }
26 | }
27 |
28 | /**
29 | * Retrieving a tensor
30 | * @param key The tensor's key name
31 | * @param meta Returns the tensor's metadata
32 | * @param format The tensor's reply format can be one of the following (BLOB/VALUES)
33 | */
34 | tensorget(key: string, format?: 'BLOB' | 'VALUES', meta?: boolean): CommandData {
35 | const args = [key];
36 | if(meta === true){
37 | args.push('META');
38 | }
39 | if(format !== undefined){
40 | args.push(format);
41 | }
42 | return {
43 | command: 'AI.TENSORGET',
44 | args: args
45 | }
46 | }
47 |
48 | /**
49 | * Setting a model
50 | * @param key The model's key name
51 | * @param backend The backend of the model
52 | * @param device The devide of the model
53 | * @param model The Protobuf-serialized model. Since Redis supports strings up to 512MB, blobs for very large
54 | * @param options Additional optional parameters
55 | */
56 | modelstore(key: string, backend: AIBackend, device: AIDevice, model: Buffer, options?: AIModelSetParameters): CommandData {
57 | let args: (string | Buffer | number)[] = [key, backend, device];
58 | if(options !== undefined && options.tag !== undefined){
59 | args = args.concat(['TAG', options.tag]);
60 | }
61 | if(options !== undefined && options.batch !== undefined) {
62 | args = args.concat(['BATCHSIZE', options.batch.size]);
63 | if(options.batch.minSize !== undefined){
64 | args = args.concat(['MINBATCHSIZE', options.batch.minSize]);
65 | }
66 | }
67 | if(options !== undefined && options.inputs !== undefined && options.inputs.length > 0){
68 | args = args.concat(['INPUTS', options.inputsCount].concat(options.inputs));
69 | }
70 | if(options !== undefined && options.outputs !== undefined && options.outputs.length > 0){
71 | args = args.concat(['OUTPUTS', options.outputsCount].concat(options.outputs));
72 | }
73 | args = args.concat(['BLOB', model]);
74 | return {
75 | command: 'AI.MODELSTORE',
76 | args: args
77 | }
78 | }
79 |
80 | /**
81 | * Retrieving a model
82 | * @param key The model's key name
83 | * @param meta Will return the model's meta information on backend, device and tag
84 | * @param blob Will return the model's blob containing the serialized model
85 | */
86 | modelget(key: string, meta?: boolean, blob?: boolean): CommandData {
87 | const args = [key];
88 | if(meta === true){
89 | args.push('META');
90 | }
91 | if(blob === true){
92 | args.push('BLOB');
93 | }
94 | return {
95 | command: 'AI.MODELGET',
96 | args: args
97 | }
98 | }
99 |
100 | /**
101 | * Deleting a model
102 | * @param key The model's key name
103 | */
104 | modeldel(key: string): CommandData {
105 | return {
106 | command: 'AI.MODELDEL',
107 | args: [key]
108 | }
109 | }
110 |
111 | /**
112 | * Running a model
113 | * @param key The model's key name
114 | * @param parameters The parameters of 'AI.MODELEXECUTE'
115 | */
116 | modelexecute(key: string, parameters: AIModelExecute): CommandData {
117 | let args = [key, 'INPUTS', parameters.inputsCount];
118 | args = args.concat(parameters.inputs);
119 | args = args.concat(['OUTPUTS', parameters.outputsCount]);
120 | args = args.concat(parameters.outputs);
121 | if(parameters.timeout){
122 | args = args.concat(['TIMEOUT', parameters.timeout]);
123 | }
124 | return {
125 | command: 'AI.MODELEXECUTE',
126 | args: args
127 | }
128 | }
129 |
130 | /**
131 | * Scanning a model
132 | */
133 | modelscan(): CommandData {
134 | return {
135 | command: 'AI._MODELSCAN',
136 | args: []
137 | }
138 | }
139 |
140 | /**
141 | * Setting a script
142 | * @param key The script's key name
143 | * @param parameters Additional optional parameters
144 | */
145 | scriptset(key: string, parameters: AIScriptSetParameters): CommandData {
146 | let args = [key, parameters.device];
147 | if(parameters.tag !== undefined){
148 | args = args.concat(['TAG', parameters.tag]);
149 | }
150 | args = args.concat(['SOURCE', parameters.script]);
151 | return {
152 | command: 'AI.SCRIPTSET',
153 | args: args
154 | }
155 | }
156 |
157 | /**
158 | * Retrieving a script
159 | * @param key The script's key name
160 | * @param meta The script's device as a String
161 | * @param source The script's source code as a String
162 | */
163 | scriptget(key: string, meta?: boolean, source?: boolean): CommandData {
164 | const args = [key];
165 | if(meta === true){
166 | args.push('META');
167 | }
168 | if(source === true){
169 | args.push('SOURCE');
170 | }
171 | return {
172 | command: 'AI.SCRIPTGET',
173 | args: args
174 | }
175 | }
176 |
177 | /**
178 | * Deleting a script
179 | * @param key The script's key name
180 | */
181 | scriptdel(key: string): CommandData {
182 | return {
183 | command: 'AI.SCRIPTDEL',
184 | args: [key]
185 | }
186 | }
187 |
188 | /**
189 | * Running a script
190 | * @param key The script's key nameb
191 | * @param functionName The name of the function to run
192 | * @param parameters The parameters of the 'AI.SCRIPTEXECUTE' command
193 | */
194 | scriptexecute(key: string, functionName: string, parameters: AIScriptExecuteParameters): CommandData {
195 | let args = [key, functionName, 'KEYS', parameters.numberOfKeys].concat(parameters.keys)
196 | if(parameters.inputs && parameters.numberOfInputs && parameters.inputs.length > 0){
197 | args = args.concat(['INPUTS', parameters.numberOfInputs]).concat(parameters.inputs)
198 | }
199 | else if(parameters.listInputs && parameters.numberOfListInputs && parameters.listInputs.length > 0){
200 | args = args.concat('LIST_INPUTS', parameters.numberOfListInputs).concat(parameters.listInputs)
201 | }
202 | args = args.concat('OUTPUTS', parameters.numberOfOutputs).concat(parameters.outputs)
203 | if(parameters.timeout){
204 | args.concat('TIMEOUT', parameters.timeout)
205 | }
206 | return {
207 | command: 'AI.SCRIPTEXECUTE',
208 | args: args
209 | }
210 | }
211 |
212 | /**
213 | * Scanning a script
214 | */
215 | scriptscan(): CommandData {
216 | return {
217 | command: 'AI._SCRIPTSCAN',
218 | args: []
219 | }
220 | }
221 |
222 | /**
223 | * Running a DAG
224 | * @param parameters Additional parameters required for the 'AI.DAGEXECUTE' command
225 | * @param commands The commands sent to the 'AI.DAGEXECUTE' command
226 | */
227 | dagexecute(parameters: AIDagExecuteParameters, commands: string[]): CommandData {
228 | return {
229 | command: 'AI.DAGEXECUTE',
230 | args: this.generateDagRunArguments(parameters, commands)
231 | }
232 | }
233 |
234 | /**
235 | * Running a readonly DAG
236 | * @param parameters Additional parameters required for the 'AI.DAGEXECUTE_RO' command
237 | * @param commands The commands sent to the 'AI.DAGEXECUTE_RO' command
238 | */
239 | dagexecuteRO(parameters: AIDagExecuteParameters, commands: string[]): CommandData {
240 | return {
241 | command: 'AI.DAGEXECUTE_RO',
242 | args: this.generateDagRunArguments(parameters, commands)
243 | }
244 | }
245 |
246 | /**
247 | * Generating the dagexecute CLI arguments
248 | * @param parameters Additional parameters required for the DAG command
249 | * @param commands The given commands
250 | */
251 | private generateDagRunArguments(parameters: AIDagExecuteParameters, commands: string[]): string[] {
252 | let args: string[] = [];
253 | args = args.concat([parameters.type.toUpperCase(), `${parameters.numberOfKeys}`].concat(parameters.keys));
254 | if(parameters.timeout) {
255 | args = args.concat(['TIMEOUT', `${parameters.timeout}`])
256 | }
257 | commands.forEach(command => {
258 | args = args.concat(['|>'].concat(command.split(' ')))
259 | });
260 | return args;
261 | }
262 |
263 | /**
264 | * Retrieving script/model info
265 | * @param key The key name of a model or script
266 | * @param RESETSTAT Resets all statistics associated with the key
267 | */
268 | info(key: string, RESETSTAT?: boolean): CommandData {
269 | const args = [key]
270 | if(RESETSTAT === true) args.push('RESETSTAT')
271 | return {
272 | command: 'AI.INFO',
273 | args: args
274 | }
275 | }
276 |
277 | /**
278 | * Restrieving configuration
279 | * @param path Specifies the default base backends path to path . The backends path is used when dynamically loading a backend (default: '{module_path}/backends', where module_path is the module's path).
280 | * @param backend Loads the DL/ML backend specified by the backend identifier from path . If path is relative, it is resolved by prefixing the BACKENDSPATH to it. If path is absolute then it is used as is.
281 | */
282 | config(path: string, backend?: AIBackend): CommandData {
283 | let args: string[] = []
284 | if(backend !== undefined)
285 | args = args.concat(['LOADBACKEND', backend, path])
286 | else
287 | args = args.concat(['BACKENDSPATH', path])
288 | return {
289 | command: 'AI.CONFIG',
290 | args: args
291 | }
292 | }
293 | }
--------------------------------------------------------------------------------
/modules/redis-ai/redis-ai.ts:
--------------------------------------------------------------------------------
1 | import { Module, RedisModuleOptions } from '../module.base'
2 | import * as Redis from 'ioredis';
3 | import { RedisAICommander } from './redis-ai.commander';
4 | import {
5 | AIBackend, AIDagExecuteParameters, AIDevice, AIModel, AIModelExecute, AIModelSetParameters, AIScript, AIScriptExecuteParameters, AIScriptInfo,
6 | AIScriptSetParameters, AITensorInfo, TensorType
7 | } from './redis-ai.types';
8 |
9 | export class RedisAI extends Module {
10 | private aiCommander = new RedisAICommander()
11 | /**
12 | * Initializing the module object
13 | * @param name The name of the module
14 | * @param clusterNodes The nodes of the cluster
15 | * @param moduleOptions The additional module options
16 | * @param moduleOptions.isHandleError If to throw error on error
17 | * @param moduleOptions.showDebugLogs If to print debug logs
18 | * @param clusterOptions The options of the clusters
19 | */
20 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
21 | /**
22 | * Initializing the module object
23 | * @param name The name of the module
24 | * @param redisOptions The options of the redis database
25 | * @param moduleOptions The additional module options
26 | * @param moduleOptions.isHandleError If to throw error on error
27 | * @param moduleOptions.showDebugLogs If to print debug logs
28 | */
29 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
30 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
31 | super(RedisAI.name, options, moduleOptions, clusterOptions);
32 | }
33 |
34 | /**
35 | * Setting a tensor
36 | * @param key The tensor's key name
37 | * @param type The tensor's data type can be one of: FLOAT , DOUBLE , INT8 , INT16 , INT32 , INT64 , UINT8 or UINT16
38 | * @param data The tensor's data (binary/numberic)
39 | * @param shape One or more dimensions, or the number of elements per axis, for the tensor
40 | */
41 | async tensorset(key: string, type: TensorType, shapes: number[], data?: number[] | Buffer[]): Promise<'OK'> {
42 | const command = this.aiCommander.tensorset(key, type, shapes, data);
43 | return await this.sendCommand(command);
44 | }
45 |
46 | /**
47 | * Retrieving a tensor
48 | * @param key The tensor's key name
49 | * @param meta Returns the tensor's metadata
50 | * @param format The tensor's reply format can be one of the following (BLOB/VALUES)
51 | */
52 | async tensorget(key: string, format?: 'BLOB' | 'VALUES', meta?: boolean): Promise {
53 | const command = this.aiCommander.tensorget(key, format, meta);
54 | const response = await this.sendCommand(command);
55 | return this.handleResponse(response);
56 | }
57 |
58 | /**
59 | * Setting a model
60 | * @param key The model's key name
61 | * @param backend The backend of the model
62 | * @param device The devide of the model
63 | * @param model The Protobuf-serialized model. Since Redis supports strings up to 512MB, blobs for very large
64 | * @param options Additional optional parameters
65 | */
66 | async modelstore(key: string, backend: AIBackend, device: AIDevice, model: Buffer, options?: AIModelSetParameters): Promise<'OK'> {
67 | const command = this.aiCommander.modelstore(key, backend, device, model, options)
68 | return await this.sendCommand(command);
69 | }
70 |
71 | /**
72 | * Retrieving a model
73 | * @param key The model's key name
74 | * @param meta Will return the model's meta information on backend, device and tag
75 | * @param blob Will return the model's blob containing the serialized model
76 | */
77 | async modelget(key: string, meta?: boolean, blob?: boolean): Promise {
78 | const command = this.aiCommander.modelget(key, meta, blob);
79 | const response = await this.sendCommand(command);
80 | return this.handleResponse(response)
81 | }
82 |
83 | /**
84 | * Deleting a model
85 | * @param key The model's key name
86 | */
87 | async modeldel(key: string): Promise<'OK'> {
88 | const command = this.aiCommander.modeldel(key);
89 | return await this.sendCommand(command);
90 | }
91 |
92 | /**
93 | * Running a model
94 | * @param key The model's key name
95 | * @param parameters The parameters of 'AI.MODELEXECUTE'
96 | */
97 | async modelexecute(key: string, parameters: AIModelExecute): Promise<'OK'> {
98 | const command = this.aiCommander.modelexecute(key, parameters);
99 | return await this.sendCommand(command);
100 | }
101 |
102 | /**
103 | * Scanning a model
104 | */
105 | async modelscan(): Promise {
106 | const command = this.aiCommander.modelscan();
107 | return await this.sendCommand(command);
108 | }
109 |
110 | /**
111 | * Setting a script
112 | * @param key The script's key name
113 | * @param parameters Additional optional parameters
114 | */
115 | async scriptset(key: string, parameters: AIScriptSetParameters): Promise<'OK'> {
116 | const command = this.aiCommander.scriptset(key, parameters);
117 | return await this.sendCommand(command);
118 | }
119 |
120 | /**
121 | * Retrieving a script
122 | * @param key The script's key name
123 | * @param meta The script's device as a String
124 | * @param source The script's source code as a String
125 | */
126 | async scriptget(key: string, meta?: boolean, source?: boolean): Promise {
127 | const command = this.aiCommander.scriptget(key, meta, source);
128 | const response: string[] = await this.sendCommand(command);
129 | return this.handleResponse(response);
130 | }
131 |
132 | /**
133 | * Deleting a script
134 | * @param key The script's key name
135 | */
136 | async scriptdel(key: string): Promise<'OK'> {
137 | const command = this.aiCommander.scriptdel(key);
138 | return await this.sendCommand(command);
139 | }
140 |
141 | /**
142 | * Running a script
143 | * @param key The script's key nameb
144 | * @param functionName The name of the function to run
145 | * @param parameters The parameters of the 'AI.SCRIPTEXECUTE' command
146 | */
147 | async scriptexecute(key: string, functionName: string, parameters: AIScriptExecuteParameters): Promise<'OK'> {
148 | const command = this.aiCommander.scriptexecute(key, functionName, parameters);
149 | return await this.sendCommand(command);
150 | }
151 |
152 | /**
153 | * Scanning a script
154 | */
155 | async scriptscan(): Promise {
156 | const command = this.aiCommander.scriptscan();
157 | return await this.sendCommand(command);
158 | }
159 |
160 | /**
161 | * Running a DAG
162 | * @param parameters Additional parameters required for the 'AI.DAGEXECUTE' command
163 | * @param commands The commands sent to the 'AI.DAGEXECUTE' command
164 | */
165 | async dagexecute(parameters: AIDagExecuteParameters, commands: string[]): Promise {
166 | const command = this.aiCommander.dagexecute(parameters, commands);
167 | return await this.sendCommand(command)
168 | }
169 |
170 | /**
171 | * Running a readonly DAG
172 | * @param parameters Additional parameters required for the 'AI.DAGEXECUTE_RO' command
173 | * @param commands The commands sent to the 'AI.DAGEXECUTE_RO' command
174 | */
175 | async dagexecuteRO(parameters: AIDagExecuteParameters, commands: string[]): Promise {
176 | const command = this.aiCommander.dagexecuteRO(parameters, commands);
177 | return await this.sendCommand(command);
178 | }
179 |
180 | /**
181 | * Retrieving script/model info
182 | * @param key The key name of a model or script
183 | * @param RESETSTAT Resets all statistics associated with the key
184 | */
185 | async info(key: string, RESETSTAT?: boolean): Promise {
186 | const command = this.aiCommander.info(key, RESETSTAT);
187 | const response: string[] = await this.sendCommand(command);
188 | return this.handleResponse(response);
189 | }
190 |
191 | /**
192 | * Restrieving configuration
193 | * @param path Specifies the default base backends path to path . The backends path is used when dynamically loading a backend (default: '{module_path}/backends', where module_path is the module's path).
194 | * @param backend Loads the DL/ML backend specified by the backend identifier from path . If path is relative, it is resolved by prefixing the BACKENDSPATH to it. If path is absolute then it is used as is.
195 | */
196 | async config(path: string, backend?: AIBackend): Promise<'OK'> {
197 | const command = this.aiCommander.config(path, backend);
198 | return await this.sendCommand(command);
199 | }
200 | }
--------------------------------------------------------------------------------
/modules/redis-ai/redis-ai.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The available tensor types
3 | */
4 | export type TensorType = 'FLOAT' | 'DOUBLE' | 'INT8' | 'INT16' | 'INT32' | 'INT64' | 'UINT8' | 'UINT16';
5 |
6 | /**
7 | * The model set parameters
8 | * @param tag The tag of the model
9 | * @param batch The batch of the model
10 | * @param size The size of the model
11 | * @param minSize The min size of the model
12 | * @param inputs The inputs of the model
13 | * @param inputsCount The inputs count of model
14 | * @param outputs The outputs of the model
15 | * @param outputsCount The outputs count of the model
16 | */
17 | export type AIModelSetParameters = {
18 | tag?: string,
19 | batch?: {
20 | size: string,
21 | minSize?: string
22 | },
23 | inputsCount?: number,
24 | inputs?: string[],
25 | outputsCount?: number,
26 | outputs?: string[],
27 | }
28 |
29 | /**
30 | * The available backend types
31 | * @param TF The TensorFlow backend
32 | * @param TFLITE The TensorFlow Lite backend
33 | * @param TORCH The PyTorch backend
34 | * @param ONNX The ONNX backend
35 | */
36 | export type AIBackend = 'TF' | 'TFLITE' | 'TORCH' | 'ONNX';
37 |
38 | /**
39 | * The available device types
40 | * @param CPU The CPU device
41 | * @param GPU The GPU device
42 | */
43 | export type AIDevice = 'CPU' | 'GPU' | string
44 |
45 | /**
46 | * The script set parameters
47 | * @param device The device of the script
48 | * @param tag The tag of the script
49 | * @param script The script name of the script
50 | */
51 | export type AIScriptSetParameters = {
52 | device: string,
53 | tag?: string,
54 | script: string
55 | }
56 |
57 | /**
58 | * The dagexecute parameters
59 | * @param type The keyword of the command
60 | * @param keys The keys of the dagexecute
61 | * @param numberOfKeys The key count of the dagexecute
62 | * @param timeout Optional. The time (in ms) after which the client is unblocked and a TIMEDOUT string is returned
63 | */
64 | export type AIDagExecuteParameters = {
65 | type: 'load' | 'persist' | 'keys',
66 | keys: string[],
67 | numberOfKeys: number,
68 | timeout?: number
69 | }
70 |
71 | /**
72 | * The model object
73 | * @param backend The backend of the model
74 | * @param device The device of the model
75 | * @param tag The tag of the model
76 | * @param batchsize The batch size of the model
77 | * @param minbatchsize The min batch size of the model
78 | * @param inputs The inputs of the model
79 | * @param outputs The outputs of the model
80 | */
81 | export type AIModel = {
82 | backend?: AIBackend,
83 | device?: AIDevice,
84 | tag?: string,
85 | batchsize?: number,
86 | minbatchsize?: number,
87 | inputs?: string[],
88 | outputs?: string[]
89 | }
90 |
91 | /**
92 | * The tensor object
93 | * @param blob The blob of the tensor
94 | */
95 | export interface AITensor extends AIModel {
96 | blob?: string
97 | }
98 |
99 | /**
100 | * The script object
101 | * @param device The device of the script
102 | * @param tag The tag of the script
103 | * @param source The source of the script
104 | */
105 | export type AIScript = {
106 | device?: AIDevice,
107 | tag?: string,
108 | source?: string
109 | }
110 |
111 | /**
112 | * The script information object
113 | * @param key The key of the script
114 | * @param type The type of the script
115 | * @param backend The backend of the script
116 | * @param duration The duration of the script
117 | * @param samples The samples of the script
118 | * @param calls The calls of the script
119 | * @param errors The errors of the script
120 | */
121 | export interface AIScriptInfo extends AIScript {
122 | key?: string,
123 | type?: string,
124 | backend?: string,
125 | duration?: number,
126 | samples?: number,
127 | calls?: number,
128 | errors?: number
129 | }
130 |
131 | /**
132 | * The tensor information object
133 | * @param dtype The tensor's data type can be one of: FLOAT , DOUBLE , INT8 , INT16 , INT32 , INT64 , UINT8 or UINT16
134 | * @param shape One or more dimensions, or the number of elements per axis, for the tensor
135 | * @param values Indicates that data is numeric and is provided by one or more subsequent val arguments
136 | * @param blob Indicates that data is in binary format and is provided via the subsequent data argument
137 | */
138 | export type AITensorInfo = {
139 | dtype?: TensorType,
140 | shape?: string[],
141 | values?: string[],
142 | blob?: string
143 | }
144 |
145 | /**
146 | * The AI.MODELEXECUTE additional parameters
147 | * @param inputsCount A positive number that indicates the number of following input keys
148 | * @param inputs The given inputs
149 | * @param outputsCount A positive number that indicates the number of output keys to follow
150 | * @param outputs The given outputs
151 | * @param timeout The time (in ms) after which the client is unblocked and a TIMEDOUT string is returned
152 | */
153 | export type AIModelExecute = {
154 | inputsCount: number,
155 | inputs: string[],
156 | outputsCount: number,
157 | outputs: string[],
158 | timeout?: number
159 | }
160 |
161 | /**
162 | * Additional parameters of the 'AI.SCRIPTEXECUTE' command
163 | * @param numberOfKeys The number of tensor keys
164 | * @param keys The tensor keys
165 | * @param numberOfInputs The number of inputs
166 | * @param inputs The inputs
167 | * @param numberOfListInputs Optional. The number of list inputs
168 | * @param listInputs Optional. The list inputs
169 | * @param numberOfOutputs The number of outputs
170 | * @param outputs The outputs
171 | * @param timeout The time (in ms) after which the client is unblocked and a TIMEDOUT string is returned
172 | */
173 | export type AIScriptExecuteParameters = {
174 | numberOfKeys: number,
175 | keys: string[],
176 | numberOfInputs: number,
177 | inputs: string[],
178 | listInputs?: string[],
179 | numberOfListInputs?: number,
180 | numberOfOutputs: number,
181 | outputs: string[],
182 | timeout?: number
183 | }
--------------------------------------------------------------------------------
/modules/redisearch/redisearch.helpers.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import { log, LogLevel } from "../module.base";
4 | import { FTAggregateResponse, FTAggregateResponseItem, FTParsedSearchResponse, FTSpellCheckResponse } from "./redisearch.types";
5 |
6 | export class RedisearchHelpers {
7 | /**
8 | * Parses `spellcheck` response into a list of objects.
9 | * @param response The response array from the spellcheck command
10 | */
11 | handleSpellcheckResponse(response: any): FTSpellCheckResponse[] {
12 | const output = [];
13 | for(const term of response) {
14 | output.push({
15 | term: term[1],
16 | suggestions: (term[2] as string[]).map(
17 | function (suggestionArrayElem) {
18 | return {
19 | score: suggestionArrayElem[0],
20 | suggestion: suggestionArrayElem[1]
21 | }
22 | }
23 | )
24 | });
25 | }
26 | return output;
27 | }
28 |
29 | /**
30 | * Handling the response of the aggregate function
31 | * @param response The raw response from the command execution
32 | * @returns A parsed response of the raw response
33 | */
34 | handleAggregateResponse(response: any): FTAggregateResponse {
35 | const numberOfItems = response[0];
36 | const items: FTAggregateResponseItem[] = [];
37 | for(let i = 1; i < response.length; i++) {
38 | items.push({
39 | name: response[i][0],
40 | value: response[i][1]
41 | })
42 | }
43 | return {
44 | numberOfItems: numberOfItems,
45 | items: items
46 | };
47 | }
48 |
49 | /**
50 | * Parsing the response of Search QUERY
51 | * @param response The raw response from Redis
52 | * @returns A parsed or processed response
53 | */
54 | handleQueryResponse(response: any) {
55 | log(LogLevel.DEBUG, `****** Function handleQueryResponse ******`);
56 | //Search queries should be parsed into objects, if possible.
57 | let responseObjects = response;
58 | //If the response is an array with 1 item, we will return it as the value.
59 | if(Array.isArray(response) && response.length === 1 && !Array.isArray(response[0])) {
60 | log(LogLevel.DEBUG, `The response is ${response[0]}`);
61 | return response[0];
62 | }
63 | //In case we have an array with a odd number of items, we will parse it as required.
64 | else if(Array.isArray(response) && response.length % 2 === 1) {
65 | // Put index as 0th element
66 | responseObjects = [response[0]];
67 | // Go through returned keys (doc:1, doc:2, ...)
68 | for(let i = 1; i < response.length; i += 2) {
69 | // propertyArray is the key-value pairs eg: ['name', 'John']
70 | const propertyArray = response[i + 1];
71 | responseObjects.push({
72 | key: response[i] //This is the key, 'eg doc:1'
73 | });
74 | if(Array.isArray(propertyArray) && propertyArray.length % 2 === 0) {
75 | for(let j = 0; j < propertyArray.length; j += 2) {
76 | // Add keys to last responseObjects item
77 | // propertyArray[j] = key name
78 | // propertyArray[j+1] = value
79 | responseObjects[responseObjects.length - 1][propertyArray[j]] = propertyArray[j + 1];
80 | }
81 | }
82 | }
83 | }
84 | //Check for a single dimensional array, these should only be keys, if im right
85 | else if(response.every((entry: any) => !Array.isArray(entry))) {
86 | responseObjects = [response[0]];
87 | for(let i = 1; i < response.length; i++) {
88 | responseObjects.push({
89 | key: response[i],
90 | });
91 | }
92 | }
93 | else {
94 | log(LogLevel.DEBUG, 'Parsing response to JSON:')
95 | const responses = response
96 | const resultCounts = responses[0];
97 | responseObjects = {}
98 | responseObjects.resultsCount = resultCounts;
99 | responseObjects.documentIds = []
100 | responseObjects.data = []
101 | for(let i = 1; i < responses.length; i ++) {
102 | if(Array.isArray(responses[i])) {
103 | responseObjects.data = responseObjects.data.concat(responses[i])
104 | }
105 | else {
106 | responseObjects.documentIds.push(responses[i])
107 | }
108 | }
109 | }
110 | return responseObjects as FTParsedSearchResponse;
111 | }
112 | }
--------------------------------------------------------------------------------
/modules/redisgears/redisgears.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import { RGGetExecutionParameters, RGPyExecuteParameters } from "./redisgears.types";
3 |
4 | export class GearsCommander {
5 | /**
6 | * Aborting an existing execution
7 | * @param id The id of the execution
8 | */
9 | abortExecution(id: string): CommandData {
10 | return {
11 | command: 'RG.ABORTEXECUTION',
12 | args: [id]
13 | }
14 | }
15 |
16 | /**
17 | * Retrieving key's configuration
18 | * @param key A list of keys
19 | */
20 | configGet(key: string[]): CommandData {
21 | return {
22 | command: 'RG.CONFIGGET',
23 | args: [key]
24 | }
25 | }
26 |
27 | /**
28 | * Setting key's configuration
29 | * @param keyvalue A key value array, i.e. [['key', 'value]]
30 | */
31 | configSet(keyvalues: string[][]): CommandData {
32 | const args = [];
33 | for(const keyvalue of keyvalues){
34 | args.concat(keyvalue)
35 | }
36 | return {
37 | command: 'RG.CONFIGSET',
38 | args: args
39 | }
40 | }
41 |
42 | /**
43 | * Dropping an existing execution
44 | * @param id The id of the execution
45 | */
46 | dropExecution(id: string): CommandData {
47 | return {
48 | command: 'RG.DROPEXECUTION',
49 | args: [id]
50 | }
51 | }
52 |
53 | /**
54 | * Dumping all of the executions
55 | */
56 | dumpExecutions(): CommandData {
57 | return {
58 | command: 'RG.DUMPEXECUTIONS'
59 | }
60 | }
61 |
62 | /**
63 | * Dumping all of the registrations
64 | */
65 | dumpRegistrations(): CommandData {
66 | return {
67 | command: 'RG.DUMPREGISTRATIONS'
68 | }
69 | }
70 |
71 | /**
72 | * Retrieving an execution
73 | * @param id The id of the execution
74 | * @param options The additional optional parameters
75 | */
76 | getExecution(id: string, options?: RGGetExecutionParameters): CommandData {
77 | const args = [id.toString()];
78 | if(options !== undefined && options.shard === true) args.push('SHARD');
79 | if(options !== undefined && options.cluster === true) args.push('CLUSTER');
80 | return {
81 | command: 'RG.GETEXECUTION',
82 | args: args
83 | }
84 | }
85 |
86 | /**
87 | * Retrieving the results
88 | * @param id The id of the execution
89 | */
90 | getResults(id: string): CommandData {
91 | return {
92 | command: 'RG.GETRESULTS',
93 | args: [id]
94 | }
95 | }
96 |
97 | /**
98 | * Retrieving the results that have 'UNBLOCKING' argument (And removing it)
99 | * @param id The id of the execution
100 | */
101 | getResultsBlocking(id: string): CommandData {
102 | return {
103 | command: 'RG.GETRESULTSBLOCKING',
104 | args: [id]
105 | }
106 | }
107 |
108 | /**
109 | * Retrieving information about the cluster
110 | */
111 | infocluster(): CommandData {
112 | return {
113 | command: 'RG.INFOCLUSTER'
114 | }
115 | }
116 |
117 | /**
118 | * Executing a python function
119 | * @param func The function
120 | * @param options The additional optional arguments
121 | */
122 | pyexecute(func: string, options?: RGPyExecuteParameters): CommandData {
123 | const args = [func];
124 | if(options !== undefined && options.unblocking === true) args.push('UNBLOCKING');
125 | if(options !== undefined && options.requirements !== undefined) args.concat(['REQUIREMENTS'].concat(options.requirements));
126 | return {
127 | command: 'RG.PYEXECUTE',
128 | args: args
129 | }
130 | }
131 |
132 | /**
133 | * Retrieving memory usage statistics from the 'Python interpreter'
134 | */
135 | pystats(): CommandData {
136 | return {
137 | command: 'RG.PYSTATS'
138 | }
139 | }
140 |
141 | /**
142 | * Retrieving a list of all the python requirements available
143 | */
144 | pydumpreqs(): CommandData {
145 | return {
146 | command: 'RG.PYDUMPREQS'
147 | }
148 | }
149 |
150 | /**
151 | * Refreshing the node's view of the cluster's topology
152 | */
153 | refreshCluster(): CommandData {
154 | return {
155 | command: 'RG.REFRESHCLUSTER'
156 | }
157 | }
158 |
159 | /**
160 | * Triggering the execution of a registered 'CommandReader' function
161 | * @param trigger The trigger's name
162 | * @param args The additional arguments
163 | */
164 | trigger(trigger: string, args: string[]): CommandData {
165 | return {
166 | command: 'RG.TRIGGER',
167 | args: [trigger].concat(args)
168 | }
169 | }
170 |
171 | /**
172 | * Removing the registration of a function
173 | * @param id The id of the execution
174 | */
175 | unregister(id: string): CommandData {
176 | return {
177 | command: 'RG.UNREGISTER',
178 | args: [id]
179 | }
180 | }
181 | }
--------------------------------------------------------------------------------
/modules/redisgears/redisgears.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { GearsCommander } from './redisgears.commander';
4 | import { RGGetExecutionParameters, RGPyExecuteParameters } from './redisgears.types';
5 |
6 | export class RedisGears extends Module {
7 |
8 | private gearsCommander = new GearsCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisGears.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Aborting an existing execution
34 | * @param id The id of the execution
35 | */
36 | async abortExecution(id: string): Promise<'OK'> {
37 | const commander = this.gearsCommander.abortExecution(id);
38 | return await this.sendCommand(commander);
39 | }
40 |
41 | /**
42 | * Retrieving key's configuration
43 | * @param key A list of keys
44 | */
45 | async configGet(key: string[]): Promise {
46 | const command = this.gearsCommander.configGet(key);
47 | return await this.sendCommand(command);
48 | }
49 |
50 | /**
51 | * Setting key's configuration
52 | * @param keyvalue A key value array, i.e. [['key', 'value]]
53 | */
54 | async configSet(keyvalues: string[][]): Promise<'OK'[]> {
55 | const command = this.gearsCommander.configSet(keyvalues);
56 | return await this.sendCommand(command);
57 | }
58 |
59 | /**
60 | * Dropping an existing execution
61 | * @param id The id of the execution
62 | */
63 | async dropExecution(id: string): Promise<'OK'> {
64 | const command = this.gearsCommander.dropExecution(id);
65 | return await this.sendCommand(command);
66 | }
67 |
68 | /**
69 | * Dumping all of the executions
70 | */
71 | async dumpExecutions(): Promise {
72 | const command = this.gearsCommander.dumpExecutions();
73 | return await this.sendCommand(command);
74 | }
75 |
76 | /**
77 | * Dumping all of the registrations
78 | */
79 | async dumpRegistrations(): Promise {
80 | const command = this.gearsCommander.dumpRegistrations();
81 | return await this.sendCommand(command);
82 | }
83 |
84 | /**
85 | * Retrieving an execution
86 | * @param id The id of the execution
87 | * @param options The additional optional parameters
88 | */
89 | async getExecution(id: string, options?: RGGetExecutionParameters): Promise {
90 | const command = this.gearsCommander.getExecution(id, options);
91 | return await this.sendCommand(command);
92 | }
93 |
94 | /**
95 | * Retrieving the results
96 | * @param id The id of the execution
97 | */
98 | async getResults(id: string): Promise {
99 | const command = this.gearsCommander.getResults(id);
100 | return await this.sendCommand(command);
101 | }
102 |
103 | /**
104 | * Retrieving the results that have 'UNBLOCKING' argument (And removing it)
105 | * @param id The id of the execution
106 | */
107 | async getResultsBlocking(id: string): Promise {
108 | const command = this.gearsCommander.getResultsBlocking(id);
109 | return await this.sendCommand(command);
110 | }
111 |
112 | /**
113 | * Retrieving information about the cluster
114 | */
115 | async infocluster(): Promise {
116 | const command = this.gearsCommander.infocluster();
117 | return await this.sendCommand(command);
118 | }
119 |
120 | /**
121 | * Executing a python function
122 | * @param func The function
123 | * @param options The additional optional arguments
124 | */
125 | async pyexecute(func: string, options?: RGPyExecuteParameters): Promise {
126 | const command = this.gearsCommander.pyexecute(func, options);
127 | return await this.sendCommand(command);
128 | }
129 |
130 | /**
131 | * Retrieving memory usage statistics from the 'Python interpreter'
132 | */
133 | async pystats(): Promise {
134 | const command = this.gearsCommander.pystats();
135 | return await this.sendCommand(command);
136 | }
137 |
138 | /**
139 | * Retrieving a list of all the python requirements available
140 | */
141 | async pydumpreqs(): Promise {
142 | const command = this.gearsCommander.pydumpreqs();
143 | return await this.sendCommand(command);
144 | }
145 |
146 | /**
147 | * Refreshing the node's view of the cluster's topology
148 | */
149 | async refreshCluster(): Promise<'OK'> {
150 | const command = this.gearsCommander.refreshCluster();
151 | return await this.sendCommand(command);
152 | }
153 |
154 | /**
155 | * Triggering the execution of a registered 'CommandReader' function
156 | * @param trigger The trigger's name
157 | * @param args The additional arguments
158 | */
159 | async trigger(trigger: string, args: string[]): Promise {
160 | const command = this.gearsCommander.trigger(trigger, args);
161 | return await this.sendCommand(command);
162 | }
163 |
164 | /**
165 | * Removing the registration of a function
166 | * @param id The id of the execution
167 | */
168 | async unregister(id: string): Promise<'OK'> {
169 | const command = this.gearsCommander.unregister(id);
170 | return await this.sendCommand(command);
171 | }
172 | }
--------------------------------------------------------------------------------
/modules/redisgears/redisgears.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The additional optional parameters of the 'RG.DROPEXECUTION' command
3 | * @param shard The 'SHARD' parameter. only gets the local execution (default in stand-alone mode)
4 | * @param cluster The 'CLUSTER' parameter. collects all executions from shards (default in cluster mode)
5 | */
6 | export type RGGetExecutionParameters = {
7 | shard?: boolean,
8 | cluster?: boolean
9 | }
10 |
11 | /**
12 | * The additional optional parameters of the 'RG.PYEXECUTE' command
13 | * @param unblocking The 'UNBLOCKING' parameter. doesn't block the client during execution
14 | * @param requirements The 'REQUIREMENTS' parameter. this argument ensures that list of dependencies it is given as an argument is installed on each shard before execution
15 | */
16 | export type RGPyExecuteParameters = {
17 | unblocking?: boolean,
18 | requirements?: string[]
19 | }
--------------------------------------------------------------------------------
/modules/redisgraph/redisgraph.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 |
3 | export class GraphCommander {
4 |
5 | /**
6 | * Executing the given query against a specific graph
7 | * @param name The name of the graph
8 | * @param query The query to execute
9 | * @param params The params of the query
10 | * @returns Result set
11 | */
12 | query(name: string, query: string, params?: {[key: string]: string}): CommandData {
13 | let args = [name]
14 | args = args.concat(this.buildQueryCommand(query, params));
15 | return {
16 | command: 'GRAPH.QUERY',
17 | args: args
18 | }
19 | }
20 |
21 | /**
22 | * Executing the given readonly query against a specific graph
23 | * @param name The name of the graph
24 | * @param query The query to execute
25 | * @param params The params of the query
26 | * @returns Result set
27 | */
28 | readonlyQuery(name: string, query: string, params?: {[key: string]: string}): CommandData {
29 | let args = [name]
30 | args = args.concat(this.buildQueryCommand(query, params));
31 | const queryList: string[] = []
32 | if(params !== undefined){
33 | queryList.push('CYPHER')
34 | for(const key in params) {
35 | const value = this.paramToString(params[key])
36 | queryList.push(`${key}=${value}`)
37 | }
38 | args.push(`${queryList.join(' ')} ${query}`)
39 | }
40 | else args.push(query)
41 | return {
42 | command: 'GRAPH.RO_QUERY',
43 | args: args
44 | }
45 | }
46 |
47 | /**
48 | * Building the cypher params of a query
49 | * @param query The query
50 | * @param params The params of the query
51 | * @returns Returning an array of arguments
52 | */
53 | private buildQueryCommand(query: string, params?: {[key: string]: string}): string[] {
54 | const args: string[] = [];
55 | const queryList: string[] = []
56 | if(params !== undefined){
57 | queryList.push('CYPHER')
58 | for(const key in params) {
59 | const value = this.paramToString(params[key])
60 | queryList.push(`${key}=${value}`)
61 | }
62 | args.push(`${queryList.join(' ')} ${query}`)
63 | }
64 | else args.push(query)
65 | return args;
66 | }
67 |
68 | /**
69 | * Executing a query and produces an execution plan augmented with metrics for each operation's execution
70 | * @param name The name of the graph
71 | * @param query The query to execute
72 | * @returns String representation of a query execution plan, with details on results produced by and time spent in each operation.
73 | */
74 | profile(name: string, query: string): CommandData {
75 | return {
76 | command: 'GRAPH.PROFILE',
77 | args: [name, query]
78 | };
79 | }
80 |
81 | /**
82 | * Completely removing the graph and all of its entities
83 | * @param name The name of the graph
84 | * @returns String indicating if operation succeeded or failed.
85 | */
86 | delete(name: string): CommandData {
87 | return {
88 | command: 'GRAPH.DELETE',
89 | args: [name]
90 | }
91 | }
92 |
93 | /**
94 | * Constructing a query execution plan but does not run it. Inspect this execution plan to better understand how your query will get executed
95 | * @param name The name of the graph
96 | * @param query The query to execute
97 | * @returns String representation of a query execution plan
98 | */
99 | explain(name: string, query: string): CommandData {
100 | return {
101 | command: 'GRAPH.EXPLAIN',
102 | args: [name, query]
103 | }
104 | }
105 |
106 | /**
107 | * Retrieving a list containing up to 10 of the slowest queries
108 | * @param id The id of the graph
109 | * @returns A list containing up to 10 of the slowest queries issued against the given graph ID.
110 | */
111 | slowlog(id: number): CommandData {
112 | return {
113 | command: 'GRAPH.SLOWLOG',
114 | args: [id]
115 | }
116 | }
117 |
118 | /**
119 | * Retrieves, describes and sets runtime configuration options
120 | * @param command The command type
121 | * @param option The option
122 | * @param value In case of 'SET' command, a valid value to set
123 | * @returns If 'SET' command, returns 'OK' for valid runtime-settable option names and values. If 'GET' command, returns a string with the current option's value.
124 | */
125 | config(command: 'GET' | 'SET' | 'HELP', option: string, value?: string): CommandData {
126 | const args = [command, option];
127 | if(command === 'SET'){
128 | args.push(value);
129 | }
130 | return {
131 | command: 'GRAPH.CONFIG',
132 | args: args
133 | };
134 | }
135 |
136 | /**
137 | * Formatting given param value to string
138 | * @param paramValue The given param value
139 | * @returns A param value converted to string
140 | */
141 | paramToString(paramValue: string): string {
142 | if(paramValue == null) return 'null';
143 | const paramType = typeof paramValue;
144 | if(paramType == 'string') {
145 | let strValue = "";
146 | paramValue = paramValue.replace(/[\\"']/g, '\\$&');
147 | if(paramValue[0] != '"') strValue += "'";
148 | strValue += paramValue;
149 | if(!paramValue.endsWith('"') || paramValue.endsWith("\\\"")) strValue += "'";
150 | return strValue;
151 | }
152 |
153 | if(Array.isArray(paramValue)) {
154 | const stringsArr = new Array(paramValue.length);
155 | for(let i = 0; i < paramValue.length; i++) {
156 | stringsArr[i] = this.paramToString(paramValue[i]);
157 | }
158 | return ["[", stringsArr.join(", "), "]"].join("");
159 | }
160 | return paramValue;
161 | }
162 | }
--------------------------------------------------------------------------------
/modules/redisgraph/redisgraph.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { GraphCommander } from './redisgraph.commander';
4 | import { GraphConfigInfo } from './redisgraph.types';
5 |
6 | export class RedisGraph extends Module {
7 |
8 | private graphCommander = new GraphCommander();
9 | /**
10 | * Initializing the module object
11 | * @param name The name of the module
12 | * @param clusterNodes The nodes of the cluster
13 | * @param moduleOptions The additional module options
14 | * @param moduleOptions.isHandleError If to throw error on error
15 | * @param moduleOptions.showDebugLogs If to print debug logs
16 | * @param clusterOptions The options of the clusters
17 | */
18 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
19 | /**
20 | * Initializing the module object
21 | * @param name The name of the module
22 | * @param redisOptions The options of the redis database
23 | * @param moduleOptions The additional module options
24 | * @param moduleOptions.isHandleError If to throw error on error
25 | * @param moduleOptions.showDebugLogs If to print debug logs
26 | */
27 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
28 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
29 | super(RedisGraph.name, options, moduleOptions, clusterOptions)
30 | }
31 |
32 | /**
33 | * Executing the given query against a specific graph
34 | * @param name The name of the graph
35 | * @param query The query to execute
36 | * @param params The params of the query
37 | * @returns Result set
38 | */
39 | async query(name: string, query: string, params?: {[key: string]: string}): Promise {
40 | const command = this.graphCommander.query(name, query, params);
41 | return await this.sendCommand(command);
42 | }
43 |
44 | /**
45 | * Executing the given readonly query against a specific graph
46 | * @param name The name of the graph
47 | * @param query The query to execute
48 | * @param params The params of the query
49 | * @returns Result set
50 | */
51 | async readonlyQuery(name: string, query: string, params?: {[key: string]: string}): Promise {
52 | const command = this.graphCommander.readonlyQuery(name, query, params);
53 | return await this.sendCommand(command);
54 | }
55 |
56 | /**
57 | * Executing a query and produces an execution plan augmented with metrics for each operation's execution
58 | * @param name The name of the graph
59 | * @param query The query to execute
60 | * @returns String representation of a query execution plan, with details on results produced by and time spent in each operation.
61 | */
62 | async profile(name: string, query: string): Promise {
63 | const command = this.graphCommander.profile(name, query);
64 | return await this.sendCommand(command);
65 | }
66 |
67 | /**
68 | * Completely removing the graph and all of its entities
69 | * @param name The name of the graph
70 | * @returns String indicating if operation succeeded or failed.
71 | */
72 | async delete(name: string): Promise {
73 | const command = this.graphCommander.delete(name);
74 | return await this.sendCommand(command);
75 | }
76 |
77 | /**
78 | * Constructing a query execution plan but does not run it. Inspect this execution plan to better understand how your query will get executed
79 | * @param name The name of the graph
80 | * @param query The query to execute
81 | * @returns String representation of a query execution plan
82 | */
83 | async explain(name: string, query: string): Promise {
84 | const command = this.graphCommander.explain(name, query);
85 | return await this.sendCommand(command);
86 | }
87 |
88 | /**
89 | * Retrieving a list containing up to 10 of the slowest queries
90 | * @param id The id of the graph
91 | * @returns A list containing up to 10 of the slowest queries issued against the given graph ID.
92 | */
93 | async slowlog(id: number): Promise {
94 | const command = this.graphCommander.slowlog(id);
95 | return await this.sendCommand(command);
96 | }
97 |
98 | /**
99 | * Retrieves, describes and sets runtime configuration options
100 | * @param command The command type
101 | * @param option The option
102 | * @param value In case of 'SET' command, a valid value to set
103 | * @returns If 'SET' command, returns 'OK' for valid runtime-settable option names and values. If 'GET' command, returns a string with the current option's value.
104 | */
105 | async config(commandType: 'GET' | 'SET' | 'HELP', option: string, value?: string): Promise {
106 | const command = this.graphCommander.config(commandType, option, value);
107 | const response = await this.sendCommand(command);
108 | return this.handleResponse(response);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/modules/redisgraph/redisgraph.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The config information
3 | * @param CACHE_SIZE The cache size of the module
4 | * @param ASYNC_DELETE The async delete of the module
5 | * @param OMP_THREAD_COUNT The omp thread count of the module
6 | * @param THREAD_COUNT The thread count of the module
7 | * @param RESULTSET_SIZE The resultset size of the module
8 | * @param MAINTAIN_TRANSPOSED_MATRICES The maintain transposed matrices of the module
9 | * @param VKEY_MAX_ENTITY_COUNT The vkey max entity count of the module
10 | */
11 | export type GraphConfigInfo = {
12 | CACHE_SIZE: number,
13 | ASYNC_DELETE: number,
14 | OMP_THREAD_COUNT: number,
15 | THREAD_COUNT: number,
16 | RESULTSET_SIZE: number,
17 | MAINTAIN_TRANSPOSED_MATRICES: number,
18 | VKEY_MAX_ENTITY_COUNT: number,
19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
20 | [key: string]: any
21 | }
--------------------------------------------------------------------------------
/modules/rejson/rejson.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The get command additional parameters
3 | * @param indent Sets the indentation string for nested levels
4 | * @param newline Sets the string that's printed at the end of each line
5 | * @param space Sets the string that's put between a key and a value
6 | * @param noescape Will disable the sending of \uXXXX escapes for non-ascii characters
7 | */
8 | export type ReJSONGetParameters = {
9 | indent?: string,
10 | newline?: string,
11 | space?: string,
12 | noescape?: boolean,
13 | }
--------------------------------------------------------------------------------
/modules/ris/ris.commander.ts:
--------------------------------------------------------------------------------
1 | import { CommandData } from "../module.base";
2 | import { RedisIntervalSet } from "./ris.types";
3 |
4 | export class RedisIntervalSetsCommander {
5 |
6 | /**
7 | * Adding an interval set
8 | * @param key The name of the key
9 | * @param sets A list of sets to create. At least 1 set is required.
10 | */
11 | add(key: string, sets: RedisIntervalSet[]): CommandData {
12 | let args: (number | string)[] = [key];
13 | for(const set of sets){
14 | args = args.concat([set.name, set.minimum, set.maximum])
15 | }
16 | return {
17 | command: 'iset.add',
18 | args: args
19 | };
20 | }
21 |
22 | /**
23 | * Retrieving all of key interval sets/a single set.
24 | * @param key The name of the key
25 | * @param setName Optional. The name of specific set. If not passed all interval sets under key will be retrieved.
26 | */
27 | get(key: string, setName?: string): CommandData {
28 | const args = [key];
29 | if(setName){
30 | args.push(setName)
31 | }
32 | return {
33 | command: 'iset.get',
34 | args: args
35 | };
36 | }
37 |
38 | /**
39 | * Deleting a all interval sets under a key, or a single/list of specific set/s.
40 | * @param key The name of the key
41 | * @param setNames Optional. A list of set names to delete. If not passed all interval sets under key will be removed.
42 | */
43 | del(key: string, setNames?: string[]): CommandData {
44 | return {
45 | command: 'iset.del',
46 | args: [key].concat(setNames)
47 | }
48 | }
49 |
50 | /**
51 | * Retrieving all sets under a key that have a specific score in their range.
52 | * @param key The name of the key
53 | * @param score The score of the set
54 | */
55 | score(key: string, score: number): CommandData {
56 | return {
57 | command: 'iset.score',
58 | args: [key, score]
59 | }
60 | }
61 |
62 | /**
63 | * Retrieving all sets under a key that don't have a specific score in their range.
64 | * @param key The name of the key
65 | * @param score The score of the set
66 | */
67 | notScore(key: string, score: number): CommandData {
68 | return {
69 | command: 'iset.not_score',
70 | args: [key, score]
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/modules/ris/ris.helpers.ts:
--------------------------------------------------------------------------------
1 | import { RedisIntervalSet } from "./ris.types";
2 |
3 | export class RedisIntervalSetsHelpers {
4 | /**
5 | * Parsing the iset.get command response
6 | * @param sets The list of sets
7 | */
8 | parseGet(sets: string[][]): RedisIntervalSet[] {
9 | const parsedSets: RedisIntervalSet[] = [];
10 | for(const set of sets) {
11 | if(set.length > 2){
12 | parsedSets.push({name: set[0], minimum: parseInt(set[1]), maximum: parseInt(set[2])})
13 | }
14 | else {
15 | return [{minimum: parseInt(set[0]), maximum: parseInt(set[1])}]
16 | }
17 | }
18 | return parsedSets;
19 | }
20 | }
--------------------------------------------------------------------------------
/modules/ris/ris.ts:
--------------------------------------------------------------------------------
1 | import * as Redis from 'ioredis';
2 | import { Module, RedisModuleOptions } from '../module.base';
3 | import { RedisIntervalSetsCommander } from './ris.commander';
4 | import { RedisIntervalSetsHelpers } from './ris.helpers';
5 | import { RedisIntervalSet } from './ris.types';
6 |
7 | export class RedisIntervalSets extends Module {
8 | private risHelpers = new RedisIntervalSetsHelpers();
9 | private risCommander = new RedisIntervalSetsCommander();
10 | /**
11 | * Initializing the module object
12 | * @param name The name of the module
13 | * @param clusterNodes The nodes of the cluster
14 | * @param moduleOptions The additional module options
15 | * @param moduleOptions.isHandleError If to throw error on error
16 | * @param moduleOptions.showDebugLogs If to print debug logs
17 | * @param clusterOptions The options of the clusters
18 | */
19 | constructor(clusterNodes: Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions)
20 | /**
21 | * Initializing the module object
22 | * @param name The name of the module
23 | * @param redisOptions The options of the redis database
24 | * @param moduleOptions The additional module options
25 | * @param moduleOptions.isHandleError If to throw error on error
26 | * @param moduleOptions.showDebugLogs If to print debug logs
27 | */
28 | constructor(redisOptions: Redis.RedisOptions, moduleOptions?: RedisModuleOptions)
29 | constructor(options: Redis.RedisOptions & Redis.ClusterNode[], moduleOptions?: RedisModuleOptions, clusterOptions?: Redis.ClusterOptions) {
30 | super(RedisIntervalSets.name, options, moduleOptions, clusterOptions)
31 | }
32 |
33 | /**
34 | * Adding an interval set
35 | * @param key The name of the key
36 | * @param sets A list of sets to create. At least 1 set is required.
37 | */
38 | async add(key: string, sets: RedisIntervalSet[]): Promise<'OK'> {
39 | const command = this.risCommander.add(key, sets);
40 | return await this.sendCommand(command);
41 | }
42 |
43 | /**
44 | * Retrieving all of key interval sets/a single set.
45 | * @param key The name of the key
46 | * @param setName Optional. The name of specific set. If not passed all interval sets under key will be retrieved.
47 | */
48 | async get(key: string, setName?: string): Promise {
49 | const command = this.risCommander.get(key, setName);
50 | const response = await this.sendCommand(command);
51 | return this.risHelpers.parseGet(response);
52 | }
53 |
54 | /**
55 | * Deleting a all interval sets under a key, or a single/list of specific set/s.
56 | * @param key The name of the key
57 | * @param setNames Optional. A list of set names to delete. If not passed all interval sets under key will be removed.
58 | */
59 | async del(key: string, setNames?: string[]): Promise<'OK'> {
60 | const command = this.risCommander.del(key, setNames);
61 | return await this.sendCommand(command);
62 | }
63 |
64 | /**
65 | * Retrieving all sets under a key that have a specific score in their range.
66 | * @param key The name of the key
67 | * @param score The score of the set
68 | */
69 | async score(key: string, score: number): Promise {
70 | const command = this.risCommander.score(key, score);
71 | return await this.sendCommand(command);
72 | }
73 |
74 | /**
75 | * Retrieving all sets under a key that don't have a specific score in their range.
76 | * @param key The name of the key
77 | * @param score The score of the set
78 | */
79 | async notScore(key: string, score: number): Promise {
80 | const command = this.risCommander.notScore(key, score);
81 | return await this.sendCommand(command);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/modules/ris/ris.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The Interval Set object
3 | * @param name The name of the interval set
4 | * @param minimum The minimum score of the interval set
5 | * @param maximum The maximum score of the interval set
6 | */
7 | export type RedisIntervalSet = {
8 | name?: string,
9 | minimum: number,
10 | maximum: number
11 | }
--------------------------------------------------------------------------------
/modules/rts/rts.types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The info JS object
3 | * @param totalSamples The count of total samples
4 | * @param memoryUsage The memory usage
5 | * @param firstTimestamp The first timestamp
6 | * @param lastTimestamp The last timestamp
7 | * @param retentionTime The retention time
8 | * @param chunkCount The cound of chunks
9 | * @param chunkSize The chunk size
10 | * @param duplicatePolicy If duplicate policy is set
11 | * @param labels A list of labels
12 | * @param sourceKey If source key is set
13 | * @param rules A list of rules
14 | */
15 | export type TSInfo = {
16 | totalSamples?: string,
17 | memoryUsage?: number,
18 | firstTimestamp?: number,
19 | lastTimestamp?: number,
20 | retentionTime?: number,
21 | chunkCount?: number,
22 | chunkSize?: number,
23 | duplicatePolicy?: TSDuplicatePolicyType,
24 | labels?: Array,
25 | sourceKey?: string | null,
26 | rules?: Array,
27 | }
28 |
29 | /**
30 | * The 'TS.CREATE' optional parameter
31 | * @param retention The 'RETENTION' optional parameter
32 | * @param uncompressed The 'UNCOMPRESSED' optional parameter
33 | * @param chunkSize The 'CHUNK_SIZE' optional parameter
34 | * @param labels A list of 'LABELS' optional parameter
35 | * @param duplicatePolicy The 'DUPLICATE_POLICY' optional parameter
36 | */
37 | export interface TSCreateOptions extends TSOptions {
38 | duplicatePolicy?: TSDuplicatePolicyType
39 | }
40 | /**
41 | * The label object
42 | * @param name The name of the label
43 | * @param value The value of the label
44 | */
45 | export type TSLabel = {
46 | name: string,
47 | value: string
48 | }
49 |
50 | /**
51 | * The 'TS.ADD' command optional parameters
52 | * @param onDuplicate The 'ON_DUPLICATE' optional parameter
53 | * @param retention The 'RETENTION' optional parameter
54 | * @param uncompressed The 'UNCOMPRESSED' optional parameter
55 | * @param chunkSize The 'CHUNK_SIZE' optional parameter
56 | * @param labels A list of 'LABELS' optional parameter
57 | */
58 | export interface TSAddOptions extends TSOptions {
59 | onDuplicate?: TSDuplicatePolicyType
60 | }
61 |
62 | /**
63 | * The 'TS.ALTER' command optional parameters
64 | * @param retention The 'RETENTION' optional parameter
65 | * @param chunkSize The 'CHUNK_SIZE' optional parameter
66 | * @param duplicatePolicy The DUPLICATE_POLICY optional parameter
67 | * @param labels A list of 'LABELS' optional parameter
68 | */
69 | export type TSAlterOptions = {
70 | retention?: number,
71 | chunkSize?: number,
72 | duplicatePolicy?: TSDuplicatePolicyType,
73 | labels?: TSLabel[]
74 | }
75 |
76 | /**
77 | * The available Duplicate policy types. Policy that will define handling of duplicate samples.
78 | * @param BLOCK an error will occur for any out of order sample
79 | * @param FIRST ignore the new value
80 | * @param LAST override with latest value
81 | * @param MIN only override if the value is lower than the existing value
82 | * @param MAX only override if the value is higher than the existing value
83 | * @param SUM If a previous sample exists, add the new sample to it so that the updated value is equal to (previous + new). If no previous sample exists, set the updated value equal to the new value.
84 | */
85 | export type TSDuplicatePolicyType = 'BLOCK' | 'FIRST' | 'LAST' | 'MIN' | 'MAX' | 'SUM';
86 |
87 | /**
88 | * The 'TS.KEYSET' command optional parameters
89 | * @param key The key
90 | * @param timestamp The timestamp
91 | * @param value The value
92 | */
93 | export type TSKeySet = {
94 | key: string,
95 | timestamp: string,
96 | value: string
97 | }
98 |
99 | /**
100 | * The 'TS.INCRBY/TS.DECRBY' command optional parameters
101 | * @param timestamp The 'TIMESTAMP' optional parameter
102 | * @param retention The 'RETENTION' optional parameter
103 | * @param uncompressed The 'UNCOMPRESSED' optional parameter
104 | * @param chunkSize The 'CHUNK_SIZE' optional parameter
105 | * @param labels A list of 'LABELS' optional parameter
106 | */
107 | export interface TSIncrbyDecrbyOptions extends TSOptions {
108 | timestamp?: number
109 | }
110 |
111 | /**
112 | * The TS optional parameters
113 | * @param retention The 'RETENTION' optional parameter
114 | * @param uncompressed The 'UNCOMPRESSED' optional parameter
115 | * @param chunkSize The 'CHUNK_SIZE' optional parameter
116 | * @param labels A list of 'LABELS' optional parameter
117 | */
118 |
119 | export type TSOptions = {
120 | retention?: number,
121 | uncompressed?: boolean,
122 | chunkSize?: number,
123 | labels?: TSLabel[]
124 | }
125 |
126 | /**
127 | * The 'TS.CREATERULE' command optional parameters
128 | * @param sourceKey The source key
129 | * @param destKey The dest key
130 | * @param aggregation The aggregation type
131 | * @param timeBucket The time bucket
132 | */
133 | export type TSCreateRule = {
134 | sourceKey: string,
135 | destKey: string,
136 | aggregation: TSAggregationType,
137 | timeBucket: number
138 | }
139 |
140 | /**
141 | * The available types of aggregation
142 | */
143 | export type TSAggregationType = 'avg' | 'sum' | 'min' | 'max' | 'range' | 'range' | 'count' | 'first' | 'last' | 'std.p' | 'std.s' | 'var.p' | 'var.s';
144 |
145 | /**
146 | * The 'TS.Range' command optional parameters
147 | * @param align The 'ALIGN' optional parameter
148 | * @param count The 'COUNT' optional parameter
149 | * @param filterByValue The 'FILTER_BY_VALUE' optional parameter.
150 | * @param filterByValue.min The min value to filter by
151 | * @param filterByValue.max The max value to filter by`
152 | * @param filterByTS The 'FILTER_BY_TS' optional parameter. A list of TS values.
153 | * @param aggregation The 'AGGREGATION' optional parameter
154 | * @param aggregation.type The type of the 'AGGREGATION' command
155 | * @param aggregation.timeBucket The time bucket of the 'AGGREGATION' command
156 | */
157 | export type TSRangeOptions = {
158 | count?: number,
159 | align?: TSAlignType,
160 | filterByValue?: {
161 | min: number,
162 | max: number
163 | },
164 | filterByTS?: string[],
165 | aggregation?: {
166 | type: TSAggregationType,
167 | timeBucket: number
168 | }
169 | }
170 |
171 | /**
172 | * The 'TS.MRange' command optional parameters
173 | * @param count The 'COUNT' optional parameter
174 | * @param aggregation The 'AGGREGATION' optional parameter
175 | * @param aggregation.type The type of the 'AGGREGATION' command
176 | * @param aggregation.timeBucket The time bucket of the 'AGGREGATION' command
177 | * @param withLabels The 'WITHLABELS' optional parameter
178 | * @param groupBy The 'GROUPBY' optional parameters
179 | * @param groupBy.label The label of the 'GROUPBY' parameters
180 | * @param groupBy.reducer The reducer of the 'GROUPBY' parameters
181 | */
182 | export interface TSMRangeOptions extends TSRangeOptions {
183 | withLabels?: boolean,
184 | groupBy?: {
185 | label: string,
186 | reducer: 'SUM' | 'MIN' | 'MAX'
187 | }
188 | }
189 |
190 | /**
191 | * The available values of Align aggregation
192 | * @param start The reference timestamp will be the query start interval time (fromTimestamp).
193 | * @param + The reference timestamp will be the query start interval time (fromTimestamp).
194 | * @param end The reference timestamp will be the signed remainder of query end interval time by the AGGREGATION time bucket (toTimestamp % timeBucket).
195 | * @param - The reference timestamp will be the signed remainder of query end interval time by the AGGREGATION time bucket (toTimestamp % timeBucket).
196 | */
197 | export type TSAlignType = 'start' | '+' | 'end' | '-';
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redis-modules-sdk",
3 | "version": "0.0.1",
4 | "description": "A Software development kit for easier connection and execution of Redis Modules commands. ",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "scripts": {
8 | "build": "tsc",
9 | "test": "mocha -r ts-node/register",
10 | "run-redis": "docker run -p 6379:6379 -d --rm redis && docker ps -a",
11 | "run-redis-with-rejson-module": "docker run -p 6379:6379 -d --rm redislabs/rejson:edge && docker ps -a",
12 | "run-redis-with-rts-module": "docker run -p 6379:6379 -d --rm redislabs/redistimeseries:edge && docker ps -a",
13 | "run-redis-with-redisearch-module": "docker run -p 6379:6379 -d --rm redislabs/redisearch:edge && docker ps -a",
14 | "run-redis-with-redisgraph-module": "docker run -p 6379:6379 -d --rm redislabs/redisgraph:edge && docker ps -a",
15 | "run-redis-with-redisgears-module": "docker run -p 6379:6379 -d --rm redislabs/redisgears:latest && docker ps -a",
16 | "run-redis-with-bloom-module": "docker run -p 6379:6379 -d --rm redislabs/rebloom:latest && docker ps -a",
17 | "run-redis-with-redisai-module": "docker run -p 6379:6379 -d --rm redislabs/redisai:edge-cpu-bionic && docker ps -a",
18 | "run-redis-with-ris-module": "docker run -p 6379:6379 -d --rm danitseitlin/redis-interval-sets && docker ps -a",
19 | "rejson-module-tests": "npm run test tests/rejson.ts -- -- --host=127.0.0.1 --port=6379",
20 | "rts-module-tests": "npm run test tests/rts.ts -- -- --host=127.0.0.1 --port=6379",
21 | "redisearch-module-tests": "npm run test tests/redisearch.ts -- -- --host=127.0.0.1 --port=6379",
22 | "redisgraph-module-tests": "npm run test tests/redisgraph.ts -- -- --host=127.0.0.1 --port=6379",
23 | "redisgears-module-tests": "npm run test tests/redisgears.ts -- -- --host=127.0.0.1 --port=6379",
24 | "redisbloom-module-tests": "npm run redisbloom-filter-tests && npm run redisbloom-topk-filter-tests && redisbloom-cuckoo-filter-tests && npm run redisbloom-cmk-filter-tests",
25 | "redisbloom-filter-tests": "npm run test tests/redisbloom.ts -- -- --host=127.0.0.1 --port=6379",
26 | "redisbloom-topk-filter-tests": "npm run test tests/redisbloom-topk.ts -- -- --host=127.0.0.1 --port=6379",
27 | "redisbloom-cuckoo-filter-tests": "npm run test tests/redisbloom-cuckoo.ts -- -- --host=127.0.0.1 --port=6379",
28 | "redisbloom-cmk-filter-tests": "npm run test tests/redisbloom-cmk.ts -- -- --host=127.0.0.1 --port=6379",
29 | "redisbloom-tdigest-filter-tests": "npm run test tests/redisbloom-tdigest.ts -- -- --host=127.0.0.1 --port=6379",
30 | "redis-ai-module-tests": "npm run test tests/redis-ai.ts -- -- --host=127.0.0.1 --port=6379",
31 | "ris-module-tests": "npm run test tests/ris.ts -- -- --host=127.0.0.1 --port=6379",
32 | "redis-module-base-tests": "npm run test tests/module-base.ts -- -- --host=127.0.0.1 --port=6379",
33 | "start-redis-server-with-modules": "docker run --rm --name redis-mod-sdk -p 6379:6379 redislabs/redismod --port 6379 --loadmodule /usr/lib/redis/modules/redisai.so --loadmodule /usr/lib/redis/modules/redisbloom.so --loadmodule /usr/lib/redis/modules/redistimeseries.so --loadmodule /usr/lib/redis/modules/redisearch.so --loadmodule /usr/lib/redis/modules/redisgraph.so --loadmodule /usr/lib/redis/modules/rejson.so",
34 | "tests": "npm run redisbloom-tdigest-filter-tests && npm run rejson-module-tests && npm run rts-module-tests && npm run redisearch-module-tests && npm run redisgraph-module-tests && npm run redisgears-module-tests && npm run redisbloom-module-tests && npm run redis-ai-tests",
35 | "pre-deploy": "npm run build",
36 | "deploy": "npm-deploy redis-modules-sdk",
37 | "generate-docs": "typedoc --out docs ."
38 | },
39 | "repository": {
40 | "type": "git",
41 | "url": "git+https://github.com/danitseitlin/redis-modules-sdk.git"
42 | },
43 | "keywords": [
44 | "redis-modules-sdk",
45 | "development-kit",
46 | "redis",
47 | "redisbloom",
48 | "redisgraph",
49 | "redisjson",
50 | "rejson",
51 | "redisgears",
52 | "rts",
53 | "redistimeseries",
54 | "redisearch",
55 | "typescript",
56 | "nodejs"
57 | ],
58 | "author": "Dani Tseitlin",
59 | "license": "Apache 2.0",
60 | "bugs": {
61 | "url": "https://github.com/danitseitlin/redis-modules-sdk/issues"
62 | },
63 | "homepage": "https://github.com/danitseitlin/redis-modules-sdk#readme",
64 | "devDependencies": {
65 | "@microsoft/tsdoc": "0.13.2",
66 | "@types/chai": "4.2.14",
67 | "@types/eslint": "7.2.5",
68 | "@types/ioredis": "4.28.10",
69 | "@types/mocha": "8.0.3",
70 | "@types/node": "14.14.1",
71 | "@typescript-eslint/eslint-plugin": "4.8.2",
72 | "@typescript-eslint/parser": "4.8.2",
73 | "chai": "4.2.0",
74 | "cli-argument-parser": "0.3.4",
75 | "eslint": "7.14.0",
76 | "fs": "0.0.1-security",
77 | "mocha": "9.2.0",
78 | "npm-package-deployer": "0.2.9",
79 | "ts-node": "9.0.0",
80 | "typedoc": "0.22.11",
81 | "typescript": "4.2.3"
82 | },
83 | "dependencies": {
84 | "ioredis": "5.2.3"
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/readme/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/readme/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing Guide
2 |
3 | Hi! I'm happy that you are interested in contributing to Redis Modules SDK. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
4 |
5 | - [Pull Request Guidelines](#pull-request-guidelines)
6 | - [Development](#development)
7 | - [Continuous integration](#continuous-integration)
8 | - [Deploy Bot](#deploy-bot)
9 |
10 | ## Pull Request Guidelines
11 |
12 | - All relevant tests in the CI must pass
13 | - For every newly added exported entity, please add it to the index.ts file
14 | - For every newly added function, add tests coverage
15 | - Make sure the merged commit and PR title are identical
16 |
17 | ## Development
18 |
19 | ### Setup
20 | - Install [Node.js](http://nodejs.org) **version 8+**
21 | - Clone the repo
22 | - Inside the repo and run
23 | ```bash
24 | npm i #Install dependencies of the project
25 | ```
26 |
27 | ### Committing Changes
28 | Each merged commit needs to be in the following format: `[Subject][Action] Description`
29 | - Subject: Something that links the commit to a specific part of the project, i.e. RedisGears, README, ESLint .etc.
30 | - Action: What was done, i.e. Bug Fix, Feature .etc.
31 | - Description: A short description as a title of what was actually done via code
32 |
33 | ### Commonly used NPM scripts
34 | ``` bash
35 | # Building the project using tsc
36 | $ npm run build
37 |
38 | # Running all existing tests
39 | $ npm run tests
40 |
41 | # Deploying a new NPM version
42 | $ npm run deploy
43 |
44 | # Running a specific test by path
45 | $ npm run test
46 | ```
47 |
48 | ### Project structure
49 | - **modules** folder - All Redis Modules SDK classes are located there
50 | - **tests** folder - All Redis Modules SDK tests are located here
51 | - **index.ts** - All exported classes/types/enums/interfaces are exported here
52 | - **.github/docs** folder - All documentations are located here
53 | - **.github/images** folder - All documentation related images
54 | - **.github/workflows** folders - All GitHub workflows
55 |
56 | ## Continuous integration
57 | - The CI auto triggers on pull request changes
58 | - The CI triggers a 'Setup' job that verified linting and build and gathers edited files
59 | - The CI selectively chooses which tests to run according to the changes done
60 | - The CI has a workflow_dispatch where you can select all tests or specific ones, 'Setup' build is run by default.
61 | - It is recommended you will open a PR only when most of the development is done, in order to prevent from running a lot of GitHub Action triggers.
62 |
63 | 
64 |
65 | ## Deploy Bot
66 | - The Bot auto triggers on push to default (master) branch
67 | - The Bot verified the package is buildable
68 | - The Bot is deploying a new version using 'npm-package-deployer'. The versions are updated in a patch level. i.e. 0.1.9 -> 0.2.0 -> 0.2.1
69 |
--------------------------------------------------------------------------------
/tests/data/models/graph.pb:
--------------------------------------------------------------------------------
1 |
2 | ,
3 | aPlaceholder*
4 | dtype0*
5 | shape:
6 | ,
7 | bPlaceholder*
8 | dtype0*
9 | shape:
10 |
11 | mulMulab*
12 | T0
13 |
14 | cIdentitymul*
15 | T0
--------------------------------------------------------------------------------
/tests/data/models/model1.onnx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danitseitlin/redis-modules-sdk-ts/bd3dc8f381a9f16cda1ba09d00710e27f67664e7/tests/data/models/model1.onnx
--------------------------------------------------------------------------------
/tests/data/models/sample1.json:
--------------------------------------------------------------------------------
1 | [
2 | {"id":500,"title":"KAS","description":"edge case description"}
3 | ]
--------------------------------------------------------------------------------
/tests/data/scripts/script.txt:
--------------------------------------------------------------------------------
1 | def bar(a, b):
2 | return a + b
3 |
--------------------------------------------------------------------------------
/tests/module-base.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import { cliArguments } from 'cli-argument-parser';
3 | import { expect } from 'chai'
4 | import { Module } from '../modules/module.base';
5 | const clients: Module[] = []
6 | describe('Module base testing', async function() {
7 | before(async () => {
8 | clients.push(new Module('Module', {
9 | host: cliArguments.host,
10 | port: parseInt(cliArguments.port),
11 | }, { isHandleError: false }));
12 | /*
13 | Commenting this out until we find a solution for the mock server.
14 | clients.push(new Module('Module', [{
15 | host: cliArguments.host,
16 | port: parseInt(cliArguments.port),
17 | }], { isHandleError: false }));*/
18 | for(const client of clients)
19 | await client.connect()
20 | })
21 | after(async() => {
22 | for(const client of clients)
23 | await client.disconnect()
24 | })
25 |
26 | it('sendCommand function', async() => {
27 | for(const client of clients) {
28 | let response = await client.sendCommand({command: 'set', args: ['foo', 'bar']})
29 | expect(response).to.equal('OK', 'The response of the SET command')
30 | response = await client.sendCommand({command: 'get', args: ['foo']})
31 | expect(response).to.equal('bar', 'The response of the GET command')
32 | response = await client.sendCommand({command: 'del', args: ['foo']})
33 | expect(response).to.equal(1, 'The response of the DEL command')
34 | }
35 | })
36 |
37 | it('handleResponse function', async () => {
38 | let response: any = 'OK';
39 | let parsed = clients[0].handleResponse(response)
40 | expect(parsed).to.equal(response, 'The parsed response')
41 | response = ['key', 'value', 'key2', 'value2'];
42 | parsed = clients[0].handleResponse(response)
43 | expect(parsed.key).to.equal(response[1], 'The parsed response')
44 | expect(parsed.key2).to.equal(response[3], 'The parsed response')
45 | response = [
46 | 'numbers', ['num1', 2]
47 | ];
48 | parsed = clients[0].handleResponse(response)
49 | expect(parsed.numbers.num1).to.equal(response[1][1], 'The parsed response')
50 | });
51 |
52 | it('isOnlyTwoDimensionalArray function', async () => {
53 | let response = [
54 | [1, 2, 3],
55 | 1
56 | ]
57 | expect(clients[0].isOnlyTwoDimensionalArray(response)).to.equal(false, 'If array is two dimensional')
58 | response = [
59 | [1, 2, 3],
60 | [6]
61 | ]
62 | expect(clients[0].isOnlyTwoDimensionalArray(response)).to.equal(true, 'If array is two dimensional')
63 | })
64 | })
--------------------------------------------------------------------------------
/tests/redis-ai.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import * as fs from 'fs';
4 | import { RedisModules } from '../';
5 | import { AIModel, AIScript, AIScriptInfo, AITensorInfo } from '../modules/redis-ai/redis-ai.types';
6 | let redis: RedisModules;
7 | describe('AI testing', async function() {
8 | before(async () => {
9 | const clientOptions = {
10 | host: cliArguments.host,
11 | port: parseInt(cliArguments.port)
12 | };
13 | redis = new RedisModules(clientOptions);
14 | await redis.connect();
15 | })
16 | after(async () => {
17 | await redis.disconnect();
18 | })
19 | it('tensorset function', async () => {
20 | let response = await redis.ai_module_tensorset('values-key', 'FLOAT', [2, 2], [1, 2 ,3, 4])
21 | expect(response).to.eql('OK', 'The response of tensorset')
22 | response = await redis.ai_module_tensorset('blob-key', 'FLOAT', [1], [Buffer.from('1.11111')])
23 | expect(response).to.eql('OK', 'The response of tensorset')
24 | });
25 | it('tensorget function', async () => {
26 | let response = await redis.ai_module_tensorget('values-key', 'VALUES', true) as AITensorInfo;
27 | expect(response.dtype).to.eql('FLOAT', 'The dtype of tensor')
28 | response = await redis.ai_module_tensorget('blob-key', 'BLOB', true) as AITensorInfo
29 | expect(response.dtype).to.eql('FLOAT', 'The dtype of tensor')
30 | response = await redis.ai_module_tensorget('blob-key', 'BLOB', true) as AITensorInfo
31 | expect(response.dtype).to.eql('FLOAT', 'The dtype of tensor')
32 | });
33 | it('modelstore function', async () => {
34 | const file = fs.readFileSync('./tests/data/models/model1.onnx')
35 | const response = await redis.ai_module_modelstore('blob-model', 'ONNX', 'CPU', file)
36 | expect(response).to.eql('OK', 'The response of modelset')
37 | });
38 | it('modelget function', async () => {
39 | const modelName = 'blob-model';
40 | const response = await redis.ai_module_modelget('blob-model', true, true) as AIModel;
41 | expect(response.device).to.eql('CPU', `The device of key ${modelName}`)
42 | });
43 | it('modelexecute function', async () => {
44 | let response = await redis.ai_module_tensorset('tensorA', 'FLOAT', [1, 2], [2, 3])
45 | response = await redis.ai_module_tensorset('tensorB', 'FLOAT', [1, 2], [3, 5])
46 | const blob = fs.readFileSync('./tests/data/models/graph.pb');
47 | response = await redis.ai_module_modelstore('mymodel', 'TF', 'CPU', blob, {
48 | inputs: ['a', 'b'],
49 | inputsCount: 2,
50 | outputs: ['c'],
51 | outputsCount: 1
52 | })
53 | expect(response).to.eql('OK', 'The response of modelstore')
54 | response = await redis.ai_module_modelexecute('mymodel', {
55 | inputs: ['tensorA', 'tensorB'],
56 | outputs: ['tensorC'],
57 | inputsCount: 2,
58 | outputsCount: 1
59 | })
60 | expect(response).to.eql('OK', 'The response of modelexecute')
61 | });
62 | it('modelscan function', async () => {
63 | const response = await redis.ai_module_modelscan();
64 | expect(response[0][0]).to.eql('mymodel', 'The response of mymodel')
65 | });
66 | it('modeldel function', async () => {
67 | const response = await redis.ai_module_modeldel('blob-model');
68 | expect(response).to.eql('OK', 'The response of modeldel')
69 | });
70 | it('scriptset function', async () => {
71 | const scriptFileStr = fs.readFileSync('./tests/data/scripts/script.txt').toString();
72 | const response = await redis.ai_module_scriptset('myscript', {
73 | device: 'CPU',
74 | script: scriptFileStr
75 | });
76 | expect(response).to.eql('OK', 'The response of scriptset')
77 | });
78 | it('scriptget function', async () => {
79 | const scriptName = 'myscript'
80 | const response = await redis.ai_module_scriptget(scriptName, true, true) as AIScript;
81 | expect(response.device).to.eql('CPU', `The device of script ${scriptName}`)
82 | });
83 |
84 | it('scriptexecute function', async () => {
85 | await redis.ai_module_tensorset('tensorA', 'FLOAT', [1, 2], [2, 3]);
86 | await redis.ai_module_tensorset('tensorB', 'FLOAT', [1, 2], [3, 5]);
87 | const response = await redis.ai_module_scriptexecute('myscript', 'bar', {
88 | numberOfKeys: 3,
89 | keys: ['tensorA', 'tensorB', 'tensorC'],
90 | numberOfInputs: 2,
91 | inputs: ['tensorA', 'tensorB'],
92 | numberOfOutputs: 1,
93 | outputs: ['tensorC']
94 | })
95 | expect(response).to.eql('OK', 'The response of scriptexecute')
96 | });
97 | it('scriptscan function', async () => {
98 | const response = await redis.ai_module_scriptscan();
99 | expect(response[0][0]).to.eql('myscript', 'The response of scriptscan')
100 | });
101 | it('info function', async () => {
102 | const response: AIScriptInfo = await redis.ai_module_info('myscript') as AIScriptInfo;
103 | expect(response.key).to.eql('myscript', 'The response of info')
104 | });
105 | it('scriptdel function', async () => {
106 | const response = await redis.ai_module_scriptdel('myscript');
107 | expect(response).to.eql('OK', 'The response of scriptdel')
108 | });
109 | it('config function', async () => {
110 | const response = await redis.ai_module_config('/usr/lib/redis/modules/backends/')
111 | expect(response).to.eql('OK', 'The response of config')
112 | });
113 | it('dagexecute function', async () => {
114 | await redis.ai_module_tensorset('tensorA', 'FLOAT', [1, 2], [2, 3]);
115 | const response = await redis.ai_module_dagexecute({
116 | type: 'load',
117 | numberOfKeys: 1,
118 | keys: ['tensorA']
119 | }, [
120 | 'AI.TENSORGET tensorA VALUES'
121 | ])
122 | expect(response).to.eql([
123 | [
124 | "2",
125 | "3"
126 | ]
127 | ], 'The response of dagexecute')
128 | });
129 | it('dagexecuteRO function', async () => {
130 | await redis.ai_module_tensorset('tensorA', 'FLOAT', [1, 2], [2, 3]);
131 | const response = await redis.ai_module_dagexecuteRO({
132 | type: 'load',
133 | numberOfKeys: 1,
134 | keys: ['tensorA']
135 | }, [
136 | 'AI.TENSORGET tensorA VALUES'
137 | ])
138 | expect(response).to.eql([
139 | [
140 | "2",
141 | "3"
142 | ]
143 | ], 'The response of dagexecute_RO')
144 | });
145 | })
--------------------------------------------------------------------------------
/tests/redisbloom-cmk.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const key1 = 'key1cmk'
6 | const key2 = 'key1cmk2';
7 |
8 | describe('RedisBloom Count-Min-Sketch filter testing', async function() {
9 | before(async () => {
10 |
11 | redis = new RedisModules({
12 | host: cliArguments.host,
13 | port: parseInt(cliArguments.port),
14 | });
15 | await redis.connect();
16 | })
17 | after(async () => {
18 | await redis.disconnect();
19 | })
20 |
21 | it('initbydim function', async () => {
22 | let response = await redis.bloom_cmk_module_initbydim('dest', 1, 2);
23 | expect(response).to.equal('OK', 'The response of CMS.INITBYDIM command');
24 | response = await redis.bloom_cmk_module_initbydim(key1, 1, 2);
25 | expect(response).to.equal('OK', 'The response of CMS.INITBYDIM command');
26 | });
27 | it('initbyprob function', async () => {
28 | const response = await redis.bloom_cmk_module_initbyprob(key2, 0.001, 0.01);
29 | expect(response).to.equal('OK', 'The response of CMS.INITBYPROB command');
30 | });
31 | it('incrby function', async () => {
32 | const response = await redis.bloom_cmk_module_incrby(key1, [{
33 | name: 'foo',
34 | increment: 10
35 | }]);
36 | expect(response[0]).to.equal(10, 'The response of CMS.INCRBY command');
37 | });
38 | it('query function', async () => {
39 | const response = await redis.bloom_cmk_module_query(key1, ['foo']);
40 | expect(response[0]).to.equal(10, 'The response of CMS.QUERY command');
41 | });
42 | it('merge function', async () => {
43 | const response = await redis.bloom_cmk_module_merge('dest', 1, [key1]);
44 | expect(response).to.equal('OK', 'The response of CMS.MERGE command');
45 | });
46 | it('info function', async () => {
47 | const response = await redis.bloom_cmk_module_info(key1);
48 | expect(response[1]).to.equal(1, 'The width of the key');
49 | expect(response[3]).to.equal(2, 'The depth of the key');
50 | expect(response[5]).to.equal(10, 'The count of the key');
51 | });
52 | });
--------------------------------------------------------------------------------
/tests/redisbloom-cuckoo.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | const key1 = 'key1cuckoo'
5 | const key2 = '1'
6 | const key3 = 'cuckoo'
7 | const chunks: {iterator: number, data: string}[] = [];
8 | let redis: RedisModules;
9 |
10 | describe('RedisBloom Cuckoo filter testing', async function() {
11 | before(async () => {
12 | redis = new RedisModules({
13 | host: cliArguments.host,
14 | port: parseInt(cliArguments.port),
15 | });
16 | await redis.connect();
17 | })
18 | after(async () => {
19 | await redis.disconnect();
20 | })
21 |
22 | it('reserve function', async () => {
23 | const response = await redis.bloom_cuckoo_module_reserve(key2, 100, {
24 | bucketSize: 1
25 | });
26 | expect(response).to.equal('OK', 'The response of the \'CF.RESERVE\' command');
27 | })
28 | it('add function', async () => {
29 | let response = await redis.bloom_cuckoo_module_add(key1, 'item');
30 | expect(response).to.equal(1, 'The response of the CF.ADD command');
31 | response = await redis.bloom_cuckoo_module_add(key2, 'X');
32 | expect(response).to.equal(1, 'The response of the CF.ADD command');
33 | });
34 | it('addnx function', async () => {
35 | const response = await redis.bloom_cuckoo_module_addnx(key1, 'item1');
36 | expect(response).to.equal(1, 'The response of the CF.ADDNX command');
37 | });
38 | it('insert function', async () => {
39 | const response = await redis.bloom_cuckoo_module_insert(key1, ['item4', 'item5'])
40 | expect(response[0]).to.equal(1, 'The response of the CF.INSERT command');
41 | });
42 | it('insertnx function', async () => {
43 | const response = await redis.bloom_cuckoo_module_insertnx(key3, ['item'])
44 | expect(response[0]).to.equal(1, 'The response of the CF.INSERTNX command');
45 | });
46 | it('exists function', async () => {
47 | const response = await redis.bloom_cuckoo_module_exists(key1, 'item1');
48 | expect(response).to.equal(1, 'The response of the CF.EXISTS command');
49 | });
50 | it('count function', async () => {
51 | const response = await redis.bloom_cuckoo_module_count(key1, 'item1');
52 | expect(response).to.equal(1, 'The response of the CF.COUNT command');
53 | });
54 | it('scandump function', async () => {
55 | let iter = 0;
56 | let response = await redis.bloom_cuckoo_module_scandump(key2, iter)
57 | let data = response[1]
58 | chunks.push({iterator: iter, data: data})
59 | iter = parseInt(response[0])
60 | while(iter != 0){
61 | response = await redis.bloom_cuckoo_module_scandump(key2, iter)
62 | iter = parseInt(response[0])
63 | data = response[1]
64 | chunks.push({iterator: iter, data: data})
65 | }
66 | expect(chunks.length).gt(0, `The count of chunks of key ${key2}`)
67 | });
68 | it.skip('loadchunk function', async () => {
69 | const chunk = chunks[1];
70 | const res = await redis.bloom_cuckoo_module_loadchunk(key2, chunk.iterator, chunk.data.replace(/�/g, 'fffd'));
71 | expect(res).to.equal('OK', `The response of load chunk with iterator ${chunk.iterator}`)
72 | });
73 | it('info function', async () => {
74 | const response = await redis.bloom_cuckoo_module_info(key1);
75 | expect(response[1]).to.equal(1080, 'The size of the key');
76 | expect(response[3]).to.equal(512, 'The number of buckets of the key');
77 | });
78 | it('del function', async () => {
79 | const response = await redis.bloom_cuckoo_module_del(key1, 'item1');
80 | expect(response).to.equal(1, 'The response of the CF.DEL command');
81 | });
82 | });
--------------------------------------------------------------------------------
/tests/redisbloom-tdigest.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const key1 = 'mykey1'
6 | const key2 = 'mykey2';
7 |
8 | describe('RedisBloom TDigest filter testing', async function() {
9 | before(async () => {
10 | redis = new RedisModules({
11 | host: cliArguments.host,
12 | port: parseInt(cliArguments.port),
13 | });
14 | await redis.connect();
15 | })
16 | after(async () => {
17 | await redis.disconnect();
18 | })
19 |
20 | it('create function', async () => {
21 | let response = await redis.bloom_tdigest_module_create(key1, 100);
22 | expect(response).to.equal('OK', 'The response of \'TDIGEST.CREATE\' command');
23 | response = await redis.bloom_tdigest_module_create(key2, 100);
24 | expect(response).to.equal('OK', 'The response of \'TDIGEST.CREATE\' command');
25 | });
26 | it('reset function', async () => {
27 | const response = await redis.bloom_tdigest_module_reset(key1);
28 | expect(response).to.equal('OK', 'The response of \'TDIGEST.RESET\' command');
29 | });
30 | it('add function', async () => {
31 | const response = await redis.bloom_tdigest_module_add(key1, [{
32 | value: 1500.0,
33 | weight: 1.0
34 | }])
35 | expect(response).to.equal('OK', 'The response of \'TDIGEST.ADD\' command');
36 | });
37 | it('merge function', async () => {
38 | const response = await redis.bloom_tdigest_module_merge(key1, key2);
39 | expect(response).to.equal('OK', 'The response of \'TDIGEST.MERGE\' command');
40 | });
41 | it('max function', async () => {
42 | const response = await redis.bloom_tdigest_module_max(key1);
43 | expect(response).to.eql('1500', 'The response of \'TDIGEST.MAX\' command')
44 | });
45 | it('min function', async () => {
46 | const response = await redis.bloom_tdigest_module_min(key1);
47 | expect(response).to.eql('1500', 'The response of \'TDIGEST.MIN\' command')
48 | });
49 | it('quantile function', async () => {
50 | const response = await redis.bloom_tdigest_module_quantile(key1, 0.5);
51 | expect(response[0]).to.eql('1500', 'The response of \'TDIGEST.QUANTILE\' command')
52 | });
53 | it('cdf function', async () => {
54 | const response = await redis.bloom_tdigest_module_cdf(key1, 10);
55 | expect(response[0]).to.eql('0', 'The response of \'TDIGEST.CDF\' command')
56 | });
57 | it('info function', async () => {
58 | const response = await redis.bloom_tdigest_module_info(key1);
59 | expect(response.Compression).to.eql(100, 'The compression')
60 | expect(response.Capacity).to.eql(610, 'The capacity')
61 | expect(response['Merged nodes']).to.eql(1, 'The merged nodes')
62 | expect(response['Unmerged nodes']).to.eql(0, 'The unmerged nodes')
63 | expect(response['Merged weight']).to.eql('1', 'The merged weight')
64 | expect(response['Unmerged weight']).to.eql('0', 'The unmerged weight')
65 | expect(response['Total compressions']).to.eql(1, 'The total compressions')
66 | });
67 | });
--------------------------------------------------------------------------------
/tests/redisbloom-topk.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const key1 = 'key1topk';
6 |
7 | describe('RedisBloom Top-K filter testing', async function() {
8 | before(async () => {
9 | redis = new RedisModules({
10 | host: cliArguments.host,
11 | port: parseInt(cliArguments.port),
12 | });
13 | await redis.connect();
14 | })
15 | after(async () => {
16 | await redis.disconnect();
17 | })
18 |
19 | it('reserve function', async() => {
20 | const response = await redis.bloom_topk_module_reserve(key1, 10, 2, 3, 0.1);
21 | expect(response).to.equal('OK', 'The response of the TOPK.RESERVE command');
22 | })
23 | it('add function', async () => {
24 | const response = await redis.bloom_topk_module_add(key1, ['bar', 42])
25 | expect(response[0]).to.equal(null, 'The response of the TOPK.ADD command')
26 | });
27 | it('incrby function', async () => {
28 | const response = await redis.bloom_topk_module_incrby(key1, [{
29 | name: 42,
30 | increment: 1
31 | }])
32 | expect(response[0]).to.equal(null, 'The response of the TOPK.INCRBY command');
33 | });
34 | it('query function', async () => {
35 | const response = await redis.bloom_topk_module_query(key1, [42, 'nonexist'])
36 | expect(response[0]).to.equal(1, 'The query response of key 42');
37 | expect(response[1]).to.equal(0, 'The query response of key nonexist');
38 | });
39 | it('count function', async () => {
40 | const response = await redis.bloom_topk_module_count(key1, ['foo', 42, 'nonexist'])
41 | expect(response[0]).to.equal(0, 'The response of the TOPK.COUNT command');
42 | expect(response[1]).to.equal(2, 'The response of the TOPK.COUNT command');
43 | expect(response[2]).to.equal(0, 'The response of the TOPK.COUNT command');
44 | });
45 | it('list function', async () => {
46 | const response = await redis.bloom_topk_module_list(key1);
47 | expect(response[0]).to.equal('42', 'The response of the TOPK.LIST command');
48 | });
49 | it('info function', async () => {
50 | const response = await redis.bloom_topk_module_info(key1);
51 | expect(response.length).to.equal(8, 'The length of items in the response')
52 | });
53 | });
--------------------------------------------------------------------------------
/tests/redisbloom.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const key1 = 'key1bloom';
6 | const key2 = '1';
7 | const item1 = 'item1';
8 | const chunks: {iterator: number, data: string}[] = [];
9 |
10 | describe('RedisBloom Module testing', async function() {
11 | before(async () => {
12 | redis = new RedisModules({
13 | host: cliArguments.host,
14 | port: parseInt(cliArguments.port),
15 | });
16 | await redis.connect();
17 | })
18 | after(async () => {
19 | await redis.disconnect();
20 | })
21 |
22 | it('reserve function', async () => {
23 | const response = await redis.bloom_module_reserve(key2, 0.01, 100);
24 | expect(response).to.equal('OK', 'The response of the \'BF.RESERVE\' command');
25 | })
26 | it('add function', async () => {
27 | const response = await redis.bloom_module_add(key1, item1)
28 | expect(response).to.equal(1, 'The response of the \'BF.ADD\' command')
29 | });
30 | it('madd function', async () => {
31 | const response = await redis.bloom_module_madd(key1, [item1])
32 | expect(response[0]).to.equal(0, 'The response of the \'BF.MADD\' command')
33 | });
34 | it('insert function', async () => {
35 | const response = await redis.bloom_module_insert(key1, [item1])
36 | expect(response[0]).to.equal(0, 'The response of the \'BF.INSERT\' command')
37 | });
38 | it('exists function', async () => {
39 | const response = await redis.bloom_module_exists(key1, item1)
40 | expect(response).to.equal(1, 'The response of the \'BF.EXISTS\' command')
41 | });
42 | it('mexists function', async () => {
43 | const response = await redis.bloom_module_mexists(key1, [item1])
44 | expect(response[0]).to.equal(1, 'The response of the \'BF.MEXISTS\' command')
45 | });
46 | it('info function', async () => {
47 | const response = await redis.bloom_module_info(key1)
48 | expect(response[0]).to.equal('Capacity', 'The first item of the information')
49 | expect(response[1]).to.equal(100, 'The value of the \'Capacity\' item')
50 | });
51 | it('scandump function', async () => {
52 | let iter = 0;
53 | let response = await redis.bloom_module_scandump(key2, iter)
54 | let data = response[1]
55 | chunks.push({iterator: iter, data: data})
56 | iter = parseInt(response[0])
57 | while(iter != 0){
58 | response = await redis.bloom_module_scandump(key2, iter)
59 | iter = parseInt(response[0])
60 | data = response[1]
61 | chunks.push({iterator: iter, data: data})
62 | }
63 | expect(chunks.length).gt(0, `The count of chunks of key ${key2}`)
64 | });
65 | it('loadchunk function', async () => {
66 | const chunk = chunks[1];
67 | const res = await redis.bloom_module_loadchunk(key2, chunk.iterator, chunk.data);
68 | expect(res).to.equal('OK', `The response of load chunk with iterator ${chunk.iterator}`)
69 | });
70 | });
--------------------------------------------------------------------------------
/tests/redisgears.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | let executionId1: string;
6 | let executionId2: string;
7 | let executionId3: string;
8 | describe('RedisGears Module testing', async function() {
9 | before(async () => {
10 | redis = new RedisModules({
11 | host: cliArguments.host,
12 | port: parseInt(cliArguments.port),
13 | });
14 | await redis.connect();
15 | })
16 | after(async () => {
17 | await redis.disconnect();
18 | })
19 |
20 | it('pyexecute function', async () => {
21 | executionId1 = await redis.gears_module_pyexecute('GB().run()', {
22 | unblocking: true
23 | })
24 | expect(executionId1).to.equal('0000000000000000000000000000000000000000-0', 'The execution id')
25 | console.log(`Execution ID1: ${executionId1}`)
26 | executionId2 = await redis.gears_module_pyexecute('GB().run()', {
27 | unblocking: true
28 | })
29 | console.log(`Execution ID2: ${executionId2}`)
30 | expect(executionId2).to.equal('0000000000000000000000000000000000000000-1', 'The execution id')
31 | executionId3 = await redis.gears_module_pyexecute('GB().run()', {
32 | unblocking: true
33 | })
34 | console.log(`Execution ID3: ${executionId3}`)
35 | expect(executionId3).to.equal('0000000000000000000000000000000000000000-2', 'The execution id')
36 | });
37 | it('configSet function', async () => {
38 | const response = await redis.gears_module_configSet([['ProfileExecutions', '1']])
39 | expect(response.length).to.equal(0, 'The response count of the \'RG.CONFIGSET\' Command');
40 | });
41 | it('configGet function', async () => {
42 | const response = await redis.gears_module_configGet(['ProfileExecutions'])
43 | expect(response[0]).to.equal(0, 'The response count of the \'RG.CONFIGGET\' Command');
44 | });
45 | it('getExecution function', async () => {
46 | const response = await redis.gears_module_getExecution(executionId1)
47 | expect(response[0][3][1]).to.equal('done', 'The response count of the \'RG.GETEXECUTION\' Command')
48 | });
49 | it('dumpExecutions function', async () => {
50 | const response = await redis.gears_module_dumpExecutions()
51 | expect(response[1][1]).to.equal(executionId1, 'The execution id')
52 | expect(response[0][1]).to.equal(executionId2, 'The execution id')
53 | });
54 | it('dumpRegistrations function', async () => {
55 | const response = await redis.gears_module_dumpRegistrations()
56 | expect(response.length).to.equal(0, 'The response count of the \'RG.DUMPREGISTRATIONS\' Command')
57 | });
58 | it('getResults function', async () => {
59 | const response = await redis.gears_module_getResults(executionId1)
60 | expect(response.length).to.equal(2, 'The response count of the \'RG.GETRESULTS\' Command')
61 | });
62 | it('getResultsBlocking function', async () => {
63 | const response = await redis.gears_module_getResultsBlocking(executionId1)
64 | expect(response.length).to.equal(2, 'The response count of the \'RG.GETRESULTSBLOCKING\' Command')
65 | });
66 | it('infocluster function', async () => {
67 | const response = await redis.gears_module_infocluster()
68 | expect(response).to.equal('no cluster mode', 'The response of the \'RG.INFOCLUSTER\' Command')
69 | });
70 | it('pystats function', async () => {
71 | const response = await redis.gears_module_pystats()
72 | expect(response[0]).to.equal('TotalAllocated', 'The response of the \'RG.PYSTATS\' Command')
73 | });
74 | it('pydumpreqs function', async () => {
75 | const response = await redis.gears_module_pydumpreqs()
76 | expect(response.length).to.equal(0, 'The response of the \'RG.PYDUMPREQS\' Command')
77 | });
78 | it('refreshCluster function', async () => {
79 | const response = await redis.gears_module_refreshCluster()
80 | expect(response).to.equal('OK', 'The response of the \'RG.REFRESHCLUSTER\' Command')
81 | });
82 | it('trigger function', async () => {
83 | await redis.gears_module_pyexecute("GB('CommandReader').register(trigger='mytrigger')", {
84 | unblocking: true
85 | })
86 | const response = await redis.gears_module_trigger('mytrigger', ['foo', 'bar'])
87 | expect(response[0]).to.equal('[\'mytrigger\', \'foo\', \'bar\']', 'The response of the \'RG.TRIGGER\' Command')
88 | });
89 | it('dropExecution function', async () => {
90 | const response = await redis.gears_module_dropExecution(executionId1)
91 | expect(response).to.equal('OK', 'The response of the \'RG.DROPEXECUTION\' Command')
92 | });
93 | it('abortExecution function', async () => {
94 | const response = await redis.gears_module_abortExecution(executionId2)
95 | expect(response).to.equal('OK', 'The response of the \'RG.ABORTEXECUTION\' Command')
96 | });
97 | it.skip('unregister function', async () => {
98 | const registrationId = `${executionId3.split('-')[0]}-${parseInt(executionId3.split('-')[1])}`
99 | const response = await redis.gears_module_unregister(registrationId)
100 | expect(response).to.equal('OK', 'The response of the \'RG.UNREGISTER\' command')
101 | });
102 | });
--------------------------------------------------------------------------------
/tests/redisgraph.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | import { GraphConfigInfo } from '../modules/redisgraph/redisgraph.types';
5 | let redis: RedisModules;
6 | const graphName = 'Test'
7 |
8 | describe('RedisGraph Module testing', async function() {
9 | this.timeout(10 * 60 * 60);
10 | before(async () => {
11 | redis = new RedisModules({
12 | host: cliArguments.host,
13 | port: parseInt(cliArguments.port),
14 | });
15 | await redis.connect();
16 | })
17 | after(async () => {
18 | await redis.disconnect();
19 | })
20 |
21 | it('query function', async () => {
22 | let response = await redis.graph_module_query(graphName, 'CREATE (p:Person {name: \'Kurt\', age: 27}) RETURN p');
23 | expect(response[2].find(item => item === 'Labels added: 1')).to.not.equal(undefined, 'The value of Labels added');
24 | expect(response[2].find(item => item === 'Nodes created: 1')).to.not.equal(undefined, 'The value of Nodes created');
25 | expect(response[2].find(item => item === 'Properties set: 2')).to.not.equal(undefined, 'The value of Properties set');
26 | expect(response[2].find(item => item === 'Cached execution: 0')).to.not.equal(undefined, 'The value of Cached execution');
27 | response = await redis.graph_module_query(graphName, `MATCH (p:Person) WHERE p.name=$name RETURN count(p) as count`, { name: 'Kurt'});
28 | expect(response[2].find(item => item === 'Cached execution: 0')).to.not.equal(undefined, 'The response of the GRAPH.QUERY command');
29 | });
30 | it('readonlyQuery function', async () => {
31 | let response = await redis.graph_module_readonlyQuery(graphName, 'MATCH (p:Person) WHERE p.age > 80 RETURN p');
32 | expect(response[2][0]).to.equal('Cached execution: 0', 'The response of the GRAPH.RO_QUERY command');
33 | response = await redis.graph_module_readonlyQuery(graphName, 'MATCH (p:Person) WHERE p.age > $age RETURN p', { age: '80' });
34 | expect(response[2][0]).to.equal('Cached execution: 0', 'The response of the GRAPH.RO_QUERY command');
35 | });
36 | it('profile function', async () => {
37 | const response = await redis.graph_module_profile(graphName, 'MATCH (p:Person) WHERE p.age > 80 RETURN p');
38 | expect(response[0]).to.contain('Results | Records produced: 0', 'The response of the GRAPH.QUERY command');
39 | });
40 | it('explain function', async () => {
41 | const response = await redis.graph_module_explain(graphName, 'MATCH (p:Person) WHERE p.age > 80 RETURN p');
42 | expect(response[0]).to.equal('Results', 'The response of the GRAPH.EXPLAIN command');
43 | expect(response[1]).to.contain('Project', 'The response of the GRAPH.EXPLAIN command');
44 | expect(response[2]).to.contain('Filter', 'The response of the GRAPH.EXPLAIN command');
45 | expect(response[3]).to.contain('Node By Label Scan | (p:Person)', 'The response of the GRAPH.EXPLAIN command');
46 | });
47 | it.skip('slowlog function', async () => {
48 | const response = await redis.graph_module_slowlog(1)
49 | expect(response.length).to.equal(0, 'The response of the GRAPH.SLOWLOG command');
50 | });
51 | it('delete function', async () => {
52 | const response = await redis.graph_module_delete(graphName)
53 | expect(response).to.contain('Graph removed', 'The response of the GRAPH.DELETE command');
54 | });
55 | it('config function', async () => {
56 | const response = await redis.graph_module_config('SET', 'RESULTSET_SIZE', '1000');
57 | expect(response).to.eql('OK', 'The RESULT SET SIZE');
58 | let response2 = await redis.graph_module_config('GET', 'RESULTSET_SIZE') as GraphConfigInfo;
59 | expect(response2.RESULTSET_SIZE).to.eql(1000, 'The RESULT SET SIZE');
60 | response2 = await redis.graph_module_config('GET', '*') as GraphConfigInfo;
61 | expect(response2.CACHE_SIZE).to.eql(25, 'The CACHE_SIZE of the module');
62 | });
63 | });
--------------------------------------------------------------------------------
/tests/rejson.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const key1 = 'key1';
6 | const key2 = 'key2';
7 | const key3 = 'arrkey';
8 | const path = '.';
9 |
10 | describe('ReJSON Module testing', async function() {
11 | before(async () => {
12 | redis = new RedisModules({
13 | host: cliArguments.host,
14 | port: parseInt(cliArguments.port)
15 | }, { showDebugLogs: true });
16 | await redis.connect();
17 | })
18 | after(async () => {
19 | await redis.disconnect();
20 | })
21 |
22 | it('set function', async () => {
23 | let response = await redis.rejson_module_set(key1, path, '{"x": 1, "str": "yy"}');
24 | expect(response).to.equal('OK', 'The response of the set command');
25 | response = await redis.rejson_module_set(key2, path, '{"x": 3}');
26 | expect(response).to.equal('OK', 'The response of the set command');
27 | response = await redis.rejson_module_set(key3, path, '{"items": [1]}');
28 | expect(response).to.equal('OK', 'The response of the set command');
29 | });
30 |
31 | it('get function', async () => {
32 | let response = await redis.rejson_module_get(key1, path);
33 | expect(response).to.equal('{"x":1,"str":"yy"}', 'The response of the get command');
34 | response = await redis.rejson_module_get(key1, '$..x');
35 | expect(response).to.equal('[1]', 'The value of the X key')
36 | });
37 |
38 | it('mget function', async () => {
39 | const response = await redis.rejson_module_mget([key1, key2], path);
40 | expect(response).to.contain('{"x":1,"str":"yy"}', 'The response of the mget command');
41 | expect(response).to.contain('{"x":3}', 'The response of the mget command');
42 | });
43 |
44 | it('type function', async () => {
45 | const response = await redis.rejson_module_type(key1, path);
46 | expect(response).to.equal('object', 'The response of the type command')
47 | });
48 |
49 | it('numincrby function', async () => {
50 | const response = await redis.rejson_module_numincrby(key1, 2, '.x');
51 | expect(response).to.equal('3', 'The response of the numincrby command')
52 | });
53 |
54 | it('nummultby function', async () => {
55 | const response = await redis.rejson_module_nummultby(key1, 3, '.x');
56 | expect(response).to.equal('9', 'The response of the nummultby command')
57 | });
58 |
59 | it('strappend function', async () => {
60 | const response = await redis.rejson_module_strappend(key1, '"rrr"', '.str');
61 | expect(response).to.equal(5, 'The response of the strappend command');
62 | const string = await redis.rejson_module_get(key1, '.str');
63 | expect(string).to.equal('"yyrrr"', 'The response of the get command');
64 | });
65 |
66 | it('strlen function', async () => {
67 | const response = await redis.rejson_module_strlen(key1, '.str')
68 | expect(response).to.equal(5, 'The response of the strlen command');
69 | });
70 |
71 | it('arrappend function', async () => {
72 | const response = await redis.rejson_module_arrappend(key3, ['3','5','4','2'], '.items');
73 | expect(response).to.equal(5, 'The response of the arrappend command');
74 | });
75 |
76 | it('arrindex function', async () => {
77 | const response = await redis.rejson_module_arrindex(key3, '1', '.items');
78 | expect(response).to.equal(0, 'The response of the arrindex command');
79 | });
80 |
81 | it('arrinsert function', async () => {
82 | const response = await redis.rejson_module_arrinsert(key3, 1, '{"z": 5}', '.items');
83 | expect(response).to.equal(6, 'The response of the arrinsert command');
84 | });
85 |
86 | it('arrlen function', async () => {
87 | const response = await redis.rejson_module_arrlen(key3, '.items');
88 | expect(response).to.equal(6, 'The response of the arrlen command');
89 | });
90 |
91 | it('arrpop function', async () => {
92 | const response = await redis.rejson_module_arrpop(key3, 0, '.items');
93 | expect(response).to.equal('1', 'The response of the arrpop command');
94 | });
95 |
96 | it('arrtrim function', async () => {
97 | const response = await redis.rejson_module_arrtrim(key3, 0, 1, '.items');
98 | expect(response).to.equal(2, 'The response of the arrtrim command');
99 | });
100 |
101 | it('objkeys function', async () => {
102 | const response = await redis.rejson_module_objkeys(key1, path);
103 | expect(response.toString()).to.equal('x,str', 'The response of the objkeys command');
104 | });
105 |
106 | it('objlen function', async () => {
107 | const response = await redis.rejson_module_objlen(key1, path);
108 | expect(response).to.equal(2, 'The response of the objlen command');
109 | });
110 |
111 | it('debug function', async () => {
112 | const response = await redis.rejson_module_debug('MEMORY', key1, path);
113 | expect(response).to.be.greaterThan(0, 'The response of the debug command');
114 | });
115 |
116 | it('forget function', async () => {
117 | const response = await redis.rejson_module_forget(key2, path);
118 | expect(response).to.equal(1, 'The response of the forget command');
119 | });
120 |
121 | it('resp function', async () => {
122 | const response = await redis.rejson_module_resp(key1, path)
123 | expect(response.toString()).to.equal('{,x,9,str,yyrrr', 'The response of the resp command');
124 | });
125 |
126 | it('toggle function', async () => {
127 | const key = 'toggle'
128 | const path = '.x'
129 | await redis.rejson_module_set(key, '.', '{"x": false, "str": "yy"}');
130 | const response = await redis.rejson_module_toggle(key, path);
131 | expect(response).to.equal('true', 'The response of JSON.TOGGLE')
132 | })
133 |
134 | it('clear function', async () => {
135 | const key = 'clear'
136 | const path = '.'
137 | await redis.rejson_module_set(key, path, '{"x": 1, "str": "yy"}');
138 | const response = await redis.rejson_module_clear(key, path);
139 | expect(response).to.equal(1, 'The response of JSON.CLEAR')
140 | })
141 |
142 | it('del function', async () => {
143 | const response = await redis.rejson_module_del(key1, path);
144 | expect(response).to.equal(1, 'The response of the del command');
145 | });
146 | });
--------------------------------------------------------------------------------
/tests/ris.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 |
6 | describe('RedisIntervalSets Module testing', async function() {
7 | before(async () => {
8 | redis = new RedisModules({
9 | host: cliArguments.host,
10 | port: parseInt(cliArguments.port),
11 | }, { showDebugLogs: true });
12 | await redis.connect();
13 | })
14 | after(async () => {
15 | await redis.disconnect();
16 | })
17 |
18 | it('add function', async () => {
19 | const response = await redis.ris_module_add('ages', [{
20 | name: 'parents',
21 | minimum: 20,
22 | maximum: 100
23 | },{
24 | name: 'kids',
25 | minimum: 0,
26 | maximum: 100
27 | }])
28 | expect(response).to.eql('OK', 'The response of the \'iset.add\' command');
29 | });
30 |
31 | it('get function', async () => {
32 | let sets = await redis.ris_module_get('ages')
33 | expect(sets.length).to.eql(2, 'The number of sets');
34 | sets = await redis.ris_module_get('ages', 'kids')
35 | expect(sets.length).to.eql(1, 'The number of sets');
36 | expect(sets[0].minimum).to.eql(0, 'The minimum score of set')
37 | expect(sets[0].maximum).to.eql(100, 'The maximum score of set')
38 | });
39 |
40 | it('score function', async () => {
41 | let sets = await redis.ris_module_score('ages', 5)
42 | expect(sets.length).to.eql(1, 'The number of sets');
43 | expect(sets[0]).to.eql('kids', 'The name of the set');
44 |
45 | sets = await redis.ris_module_score('ages', 5)
46 | expect(sets.length).to.eql(1, 'The number of sets');
47 | expect(sets[0]).to.eql('kids', 'The name of the set');
48 | });
49 |
50 | it('notScore function', async () => {
51 | let sets = await redis.ris_module_notScore('ages', 5)
52 | expect(sets.length).to.eql(1, 'The number of sets');
53 | expect(sets[0]).to.eql('parents', 'The name of the set');
54 |
55 | sets = await redis.ris_module_notScore('ages', 5)
56 | expect(sets.length).to.eql(1, 'The number of sets');
57 | expect(sets[0]).to.eql('parents', 'The name of the set');
58 | });
59 |
60 | it('del function', async () => {
61 | let response = await redis.ris_module_del('ages', ['kids'])
62 | expect(response).to.eql('OK', 'The response of the \'iset.del\' command');
63 | const sets = await redis.ris_module_get('ages');
64 | expect(sets.length).to.eql(1, 'The sets count')
65 | response = await redis.ris_module_del('ages')
66 | expect(response).to.eql('OK', 'The response of the \'iset.del\' command');
67 | });
68 | });
--------------------------------------------------------------------------------
/tests/rts.ts:
--------------------------------------------------------------------------------
1 | import { cliArguments } from 'cli-argument-parser';
2 | import { expect } from 'chai'
3 | import { RedisModules } from '../';
4 | let redis: RedisModules;
5 | const date = new Date(2019, 11, 24, 19).getTime();
6 | const key1 = 'key:2:32';
7 | const key2 = 'key:2:33';
8 | describe('RTS Module testing', async function() {
9 | before(async () => {
10 | redis = new RedisModules({
11 | host: cliArguments.host,
12 | port: parseInt(cliArguments.port),
13 | });
14 | await redis.connect();
15 | })
16 | after(async () => {
17 | await redis.disconnect();
18 | })
19 |
20 | it('create function', async () => {
21 | let response = await redis.rts_module_create(key1, {
22 | labels:[{
23 | name: 'label',
24 | value: 'value'
25 | }]
26 | })
27 | expect(response).to.equal('OK', 'The response of the create command');
28 | response = await redis.rts_module_create(key2, {
29 | labels:[{
30 | name: 'label1',
31 | value: 'value1'
32 | }]
33 | })
34 | expect(response).to.equal('OK', 'The response of the create command');
35 | });
36 |
37 | it('alter function', async () => {
38 | const response = await redis.rts_module_alter(key1,{retention: 1});
39 | expect(response).to.equal('OK', 'The response of the alter command');
40 | });
41 | it('add function', async () => {
42 | const response = await redis.rts_module_add(key1, `${date}`, '26', { onDuplicate: 'SUM'})
43 | expect(response).to.equal(date, 'The response of the add command');
44 | });
45 | it('madd function', async () => {
46 | const info = await redis.rts_module_info(key1);
47 | const response = await redis.rts_module_madd([{
48 | key: key1,
49 | timestamp: info.firstTimestamp.toString(),
50 | value: '32'
51 | }])
52 | expect(response.length).to.equal(1, 'The response of the madd command');
53 | });
54 | it('incrby function', async () => {
55 | const currentValue = parseInt((await redis.rts_module_get(key1))[1].toString())
56 | await redis.rts_module_incrby(key1, '1')
57 | const newValue = parseInt((await redis.rts_module_get(key1))[1].toString())
58 | expect(newValue).to.be.above(currentValue, 'The response of the incrby command');
59 | });
60 | it('decrby function', async () => {
61 | const currentValue = parseInt((await redis.rts_module_get(key1))[1].toString())
62 | await redis.rts_module_decrby(key1, '1')
63 | const newValue = parseInt((await redis.rts_module_get(key1))[1].toString())
64 | expect(currentValue).to.be.above(newValue, 'The response of the decrby command');
65 | });
66 | it('createrule function', async () => {
67 | const response = await redis.rts_module_createrule({
68 | sourceKey: key1,
69 | destKey: key2,
70 | aggregation: 'avg',
71 | timeBucket: 1
72 | })
73 | expect(response).to.equal('OK', 'The response of the createrule command');
74 | });
75 | it('deleterule function', async () => {
76 | const response = await redis.rts_module_deleterule(key1, key2);
77 | expect(response).to.equal('OK', 'The response of the deleterule command');
78 | });
79 | it('range function', async () => {
80 | const data = await redis.rts_module_get(key1);
81 | let response = await redis.rts_module_range(key1, data[0].toString(), data[1].toString())
82 | expect(response.length).to.equal(0, 'The range items length of the response')
83 | response = await redis.rts_module_range(key1, `${data[0]}`, `${data[1]}`, {
84 | align: 'start',
85 | aggregation: {
86 | type: 'count',
87 | timeBucket: 10
88 | }
89 | });
90 | expect(response.length).to.equal(0, 'The range items length of the response')
91 | });
92 | it('revrange function', async () => {
93 | const data = await redis.rts_module_get(key1);
94 | let response = await redis.rts_module_revrange(key1, data[0].toString(), data[1].toString())
95 | expect(response.length).to.equal(0, 'The range items length of the response')
96 | response = await redis.rts_module_revrange(key1, `${data[0]}`, `${data[1]}`, {
97 | align: 'start',
98 | aggregation: {
99 | type: 'count',
100 | timeBucket: 10
101 | }
102 | });
103 | expect(response.length).to.equal(0, 'The range items length of the response')
104 | });
105 | it('mrange function', async () => {
106 | const info = await redis.rts_module_info(key1);
107 | const fromTimestamp = (info.firstTimestamp-1);
108 | const toTimestamp = (info.lastTimestamp+10000);
109 | const key = 'key:2:32';
110 | const filter = 'label=value';
111 | let response = await redis.rts_module_mrange(`${fromTimestamp}`, `${toTimestamp}`, filter);
112 | expect(response[0][0]).to.equal(key, 'The filtered key name');
113 | response = await redis.rts_module_mrange(`${fromTimestamp}`, `${toTimestamp}`, filter, {
114 | groupBy: {
115 | label: 'label',
116 | reducer: 'MAX'
117 | },
118 | withLabels: true
119 | });
120 | expect(response[0][0]).to.equal(filter, 'The value of the filter');
121 | expect(response[0][1][0][0]).to.equal('label', 'The name of the label');
122 | expect(response[0][1][0][1]).to.equal('value', 'The value of the label value');
123 | expect(response[0][1][1][0]).to.equal('__reducer__', 'The key of the reducer');
124 | expect(response[0][1][1][1]).to.equal('max', 'The value of the reducer');
125 | expect(response[0][1][2][0]).to.equal('__source__', 'The key of the source');
126 | expect(response[0][1][2][1]).to.equal(key, 'The value of the source');
127 | });
128 | it('mrevrange function', async () => {
129 | const info = await redis.rts_module_info(key1);
130 | let response = await redis.rts_module_mrevrange((info.firstTimestamp-1).toString(), (info.lastTimestamp+10000).toString(), 'label=value')
131 | expect(response[0][0]).to.equal('key:2:32', 'The filtered key name');
132 | response = await redis.rts_module_mrevrange(`${info.firstTimestamp-1}`, `${info.lastTimestamp+10000}`, 'label=value', {
133 | align: '+',
134 | aggregation: {
135 | type: 'count',
136 | timeBucket: 10
137 | }
138 | })
139 | expect(response[0][0]).to.equal('key:2:32', 'The filtered key name');
140 | });
141 | it('get function', async () => {
142 | const response = await redis.rts_module_get(key1);
143 | expect(response.length).to.equal(2, 'The response of the get command');
144 | });
145 | it('mget function', async () => {
146 | let response = await redis.rts_module_mget('label=value');
147 | expect(response.length).to.equal(1, 'The response of the mget command');
148 | response = await redis.rts_module_mget('label=value label1=value1');
149 | console.log(response)
150 | expect(response.length).to.equal(0, 'The response of the mget command');
151 | response = await redis.rts_module_mget('label=value x=(a,b,c)');
152 | expect(response.length).to.equal(0, 'The response of the mget command');
153 | });
154 | it('info function', async () => {
155 | const response = await redis.rts_module_info(key1)
156 | expect(response.totalSamples).to.be.greaterThan(0, 'The total samples of the key');
157 | });
158 | it('queryindex function', async () => {
159 | const response = await redis.rts_module_queryindex('label=value')
160 | expect(response.length).eql(1, 'The response of the queryindex command');
161 | });
162 | it('del function', async() => {
163 | const samplesCount = await redis.rts_module_del(key1, date.toString(), new Date().getTime().toString())
164 | expect(samplesCount).eql(3, 'The response TS.DEL command')
165 | })
166 | });
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "commonjs",
5 | "outDir": "./lib",
6 | "declaration": true,
7 | "moduleResolution": "node"
8 | }
9 | }
--------------------------------------------------------------------------------
/tsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tsdoc":{
3 | "source" :"./modules",
4 | "destination" :"./docs",
5 | "tutorials" :"",
6 | "systemName" : "redis-modules-sdk-ts",
7 | "footer" : "",
8 | "copyright" : "redis-modules-sdk-ts Copyright © 2021 Dani Tseitlin",
9 | "outputSourceFiles" : true,
10 | "commentsOnly": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------