├── .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 | [![Stargazers repo roster for @danitseitlin/redis-modules-sdk](https://reporoster.com/stars/danitseitlin/redis-modules-sdk-ts)](https://github.com/danitseitlin/redis-modules-sdk-ts/stargazers) 77 | # Forkers :fork_and_knife: 78 | [![Forkers repo roster for @danitseitlin/redis-modules-sdk](https://reporoster.com/forks/danitseitlin/redis-modules-sdk-ts)](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 | ![CI](https://github.com/danitseitlin/redis-modules-sdk/blob/master/.github/images/ci.png) 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 | a Placeholder* 4 | dtype0* 5 | shape: 6 | , 7 | b Placeholder* 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 | --------------------------------------------------------------------------------