{"id":16989595,"url":"https://github.com/didas-git/nekdis","last_synced_at":"2025-04-12T03:31:54.387Z","repository":{"id":129291542,"uuid":"495570501","full_name":"Didas-git/Nekdis","owner":"Didas-git","description":"Redis-OM proposal for improvements on the interface","archived":false,"fork":false,"pushed_at":"2024-04-09T12:58:43.000Z","size":1281,"stargazers_count":10,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-06T05:35:48.073Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Didas-git.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-23T20:54:42.000Z","updated_at":"2024-08-29T17:20:09.000Z","dependencies_parsed_at":"2023-11-06T10:51:08.343Z","dependency_job_id":"602d75b2-31b4-4d22-83a1-6f80c926a211","html_url":"https://github.com/Didas-git/Nekdis","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Didas-git%2FNekdis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Didas-git%2FNekdis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Didas-git%2FNekdis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Didas-git%2FNekdis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Didas-git","download_url":"https://codeload.github.com/Didas-git/Nekdis/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248512787,"owners_count":21116680,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T03:07:11.486Z","updated_at":"2025-04-12T03:31:54.114Z","avatar_url":"https://github.com/Didas-git.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nekdis\n\nNekdis is a [Redis](https://redis.com/) ODM and mainly a proposal for [redis-om](https://github.com/redis/redis-om-node) that aims to improve the user experience and performance.\n\n# Table of contents\n\n- [Nekdis](#nekdis)\n- [Table of contents](#table-of-contents)\n- [Installation](#installation)\n- [Getting Started](#getting-started)\n  - [Connecting to the database](#connecting-to-the-database)\n  - [Creating a Schema](#creating-a-schema)\n  - [Creating a Model](#creating-a-model)\n  - [Creating a document/record](#creating-a-documentrecord)\n- [Glossary](#glossary)\n  - [RecordID](#recordid)\n  - [Document](#document)\n  - [Reference](#reference)\n  - [Relation](#relation)\n- [Advanced concepts](#advanced-concepts)\n  - [Self managed instance](#self-managed-instance)\n    - [Option 1](#option-1)\n    - [Option 2](#option-2)\n  - [Vector similarity search](#vector-similarity-search)\n    - [Pure queries](#pure-queries)\n    - [Hybrid queries](#hybrid-queries)\n    - [Range queries](#range-queries)\n  - [Multiple value queries (`or` queries)](#multiple-value-queries-or-queries)\n  - [Relational concepts](#relational-concepts)\n    - [Using references](#using-references)\n    - [Using relations](#using-relations)\n  - [References VS Relations](#references-vs-relations)\n    - [What is a Reference](#what-is-a-reference)\n      - [The problem](#the-problem)\n    - [What is a relation](#what-is-a-relation)\n      - [The problem](#the-problem-1)\n    - [Pros and Cons](#pros-and-cons)\n    - [When to and not to use relations](#when-to-and-not-to-use-relations)\n  - [Modularity with client modules](#modularity-with-client-modules)\n  - [Modularity with base injections](#modularity-with-base-injections)\n- [Schema field list](#schema-field-list)\n- [Nekdis VS Redis-OM](#nekdis-vs-redis-om)\n  - [Client](#client)\n  - [Schema](#schema)\n  - [Model vs Repository](#model-vs-repository)\n    - [Nekdis Document](#nekdis-document)\n      - [Creating and saving](#creating-and-saving)\n      - [Creating and mutating](#creating-and-mutating)\n  - [Search](#search)\n  - [Nested objects](#nested-objects)\n  - [A Simple example](#a-simple-example)\n  - [Benchmarks](#benchmarks)\n\n# Installation\n\nNekdis is available on the npm registry and can be installed with your preferred package manager\n\n```sh\npnpm i nekdis\n```\n\n# Getting Started\n\n## Connecting to the database\n\nNekdis already exports a global client but you can also create your own instance with the `Client` class.\n\n```ts\nimport { client } from \"nekdis\";\n\nawait client.connect();\n```\n\nBut you can also use your own node-redis instance.\n\n```ts\nimport { createClient } from \"redis\";\nimport { client } from \"nekdis\";\n\nclient.redisClient = createClient();\n\n// We still advise to use Nekdis to connect and disconnect\nawait client.connect();\n```\n\n## Creating a Schema\n\nSchemas are what defines the shape of your data and can be created using the `schema` method of the client.\n\n```ts\nconst CatSchema = client.schema({\n    name: { type: \"string\" }\n});\n```\n\n## Creating a Model\n\nModels are what you use to interact with the database and collections that have the shape of the schema you pass in. They can be crated using the `model` method of the client.\n\n```ts\nconst CatModel = client.model(\"Cat\", CatSchema);\n```\n\n## Creating a document/record\n\nThe model provides some methods to handle your documents but the simplest way to create one is to do as follows:\n\n```ts\nconst aCat = CatModel.create({\n    name: \"Nozomi\"\n});\n\nawait CatModel.save(aCat);\n\n// Alternatively\nawait CatModel.createAndSave({\n    name: \"Vanilla\"\n});\n\n```\n\n# Glossary\n\nNekdis does naming a little different than redis does and here is a short explanation of some of them.\n\n## RecordID\n\nNekdis introduces the concept of the `RecordID` which is as highly customizable id that is divided into pieces, in redis itself this is just the `key`. So when you see documentation talking about an `id` or you read the types and you see `id` remember that it is just a redis `key` and shorthand for a `RecordID` in Nekdis.\n\nThe structure of a `RecordID` is as follows:\n![](./recordid.png)\n\n## Document\n\nA document in Nekdis is just the data within a key plus the id and its parts.\nThe id parts can be accessed with `$globalPrefix`, `$prefix`, `$modelName`, `$suffix`, `$id` and `$recordId` respectively.\n\n## Reference\n\nA `reference` in nekdis is nothing more nothing less than a foreign key, so when you defined a field in your schema as being a `reference` you are just saying that a field contains one or more keys.\n\n## Relation\n\nA `relation` is a bit more complex than a relation since it is \"virtual\" and not part of the document.\nFor more details you can check the [references versus relations](#references-vs-relations) section on the readme.\n\n# Advanced concepts\n\nHere im going to explain a bit more of the possibilities of Nekdis.\n\n## Self managed instance\n\nNekdis allows you to pass your own redis client to it, this allows a higher degree of control over the connection and some freedom.\n\n### Option 1\n\n```ts\nimport { client } from \"./src\";\nimport { createClient } from \"redis\";\n\nclient.redisClient = createClient();\n```\n\n### Option 2\n\n```ts\nimport { Client } from \"./src\";\nimport { createClient } from \"redis\";\n\nconst client = new Client({}, createClient());\n```\n\n## Vector similarity search\n\nRedis supports indexing vectors on the database for search, with this in mind we built a way to query them that resembles the point search using the same builder pattern.\n\nThere are plans to add more functionality to it in the future but for now using it is really simple.\n\n### Pure queries\n\n```ts\ntestModel.search().where(\"vec\").eq((vector) =\u003e vector\n    .knn()\n    .from(new Float32Array([0.1, 0.1]))\n    .return(8))\n.returnAll();\n// Generates the following query\n// \"*=\u003e[KNN 8 @vec $BLOB]\" \"PARAMS\" \"2\" \"BLOB\" \"\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\" \"DIALECT\" \"2\"\n```\n\n### Hybrid queries\n\n```ts\ntestModel.search().where(\"age\").between(18, 30)\n    .and(\"vec\").eq((vector) =\u003e vector\n        .knn()\n        .from(new Float32Array([0.1, 0.1]))\n        .return(8))\n    .returnAll();\n// Generates the following query\n// \"((@age:[18 30])) =\u003e[KNN 8 @vec $BLOB]\" \"PARAMS\" \"2\" \"BLOB\" \"\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\" \"DIALECT\" \"2\"\n```\n\n### Range queries\n\n```ts\ntestModel.search().where(\"vec\").eq((vector) =\u003e vector\n    .range(5)\n    .from(new Float32Array([0.1, 0.1])))\n.returnAll();\n// Generates the following query\n// \"((@vec:[VECTOR_RANGE 5 $BLOB])) \" \"PARAMS\" \"2\" \"BLOB\" \"\\xcd\\xcc\\xcc=\\xcd\\xcc\\xcc=\" \"DIALECT\" \"2\"\n```\n\n## Multiple value queries (`or` queries)\n\nIn Nekdis you can pass in an array of values to `eq`/`equals`/`equalsTo` to make an \"or query\". Another alias for it is `includes`.\n\n\u003e **NOTE**\n\u003e This only works on `TAG` (excluding boolean) and `TEXT` fields for now\n\n```ts\nconst UserSchema = client.schema({\n    name: { type: \"text\", index: true }\n})\n\nconst UserModel = client.model(\"User\", UserSchema);\n\n// Return all users with name `DidaS` or `Leibale`\nawait UserModel.search().where(\"name\").eq([\"DidaS\", \"Leibale\"]).returnAll();\n```\n\n## Relational concepts\n\nDue to some design limitations all relational concepts within nekdis, be it references or relations, are many-to-many, you can still do one-to-many and many-to-one relations with this but there are no constrains to do it explicitly meaning that you could on accident add another relation. With that also comes the disadvantage of not being able to optimize queries for those cases.\n\nThere are plans to introduce explicitly one-to-many and many-to-one **relations** but this change would not affect references and the default/implicit type would be many-to-many.\n\n### Using references\n\nAs references are only foreign keys you can mix and match it with RediSearch to have functionality close to SQL, the problem however is that you will probably end up with non-atomic actions which may cause unwanted behavior down the line.\n\n```ts\nconst UserSchema = client.schema({\n    name: { type: \"text\", index: true },\n    friends: { type: \"reference\", schema: \"self\" }\n})\n\nconst UserModel = client.model(\"User\", UserSchema);\nawait UserModel.createIndex();\n\nconst user1id = await UserModel.createAndSave({\n    name: \"DidaS\"\n});\n\nconst user2id = await UserModel.createAndSave({\n    name: \"Niek\",\n    // \u003cReferenceArray\u003e.reference accepts a Document or a recordId.\n    friends: new ReferenceArray().reference(user1id)\n})\n\n// Getting all the references\nawait UserModel.get(user2id, { withReferences: true });\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: [ JSONDocument { name: 'DidaS', friends: ReferenceArray(0) [] } ]\n}\n*/\n\n// Searching and returning the array of keys (just normal search)\nawait UserModel.search().where(\"name\").eq(\"Niek\").returnFirst();\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: ReferenceArray(1) [\n    'Nekdis:V1:User:028af7e1-11b0-4239-a51d-351564eef5bf'\n    ]\n}\n*/\n\n// Searching and returning the references\nawait UserModel.search().where(\"name\").eq(\"Niek\").returnFirst(true);\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: [ JSONDocument { name: 'DidaS', friends: ReferenceArray(0) [] } ]\n}\n*/\n```\n\n### Using relations\n\nRelations provide a more robust api but as of now there is no way to auto fetch relations when using `search` so you can only auto fetch relations when using `get`, this will change and the search methods will have the same api as `get` in the future.\n\n```ts\nconst UserSchema = client.schema({\n    name: { type: \"text\", index: true },\n    friends: { type: \"relation\", schema: \"self\", meta: { age: \"number\" } }\n})\n\nconst UserModel = client.model(\"User\", UserSchema);\nawait UserModel.createIndex();\n\nconst user1id = await UserModel.createAndSave({\n    name: \"DidaS\"\n});\n\nconst user2id = await UserModel.createAndSave({\n    name: \"Niek\"\n})\n\nconst user3id = await UserModel.createAndSave({\n    name: \"Otis\"\n})\n\n// \u003cModel\u003e.relate accepts a Document, an id or a recordId\nawait UserModel.relate(user2id).to(user1id).as(\"friends\").with({ age: 19 }).exec();\nawait UserModel.relate(user2id).to(user3id).as(\"friends\").exec();\n\n// Getting all the relations\nawait UserModel.get(user2id, { withRelations: true });\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: [ JSONDocument { name: 'Otis' }, JSONDocument { name: 'DidaS' } ]\n}\n*/\n\n// Getting all the relations metadata\nawait UserModel.get(user2id, { withRelations: true, returnMetadataOverRelation: true });\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: [\n    JSONDocument {\n        age: undefined,\n        in: 'Nekdis:V1:User:c59e67d5-69f7-4746-8476-2354b5113069',\n        out: 'Nekdis:V1:User:35c49ab7-112f-4938-aff7-a4e8374513df'\n    },\n    JSONDocument {\n        age: 19,\n        in: 'Nekdis:V1:User:c59e67d5-69f7-4746-8476-2354b5113069',\n        out: 'Nekdis:V1:User:a92df323-a6c2-4576-bd11-21565db48331'\n    }\n    ]\n}\n*/\n\n// Search on the relation metadata\nawait UserModel.get(user2id, {\n    withRelations: true,\n    relationsConstrain: {\n        // Lets only search user friends related to the user in question\n        friends: (s) =\u003e s.where(\"in\").eq(UserModel.sanitize(user2id)).and(\"age\").eq(19)\n    }\n});\n/*\nJSONDocument {\n    name: 'Niek',\n    friends: [ JSONDocument { name: 'DidaS' } ]\n}\n*/\n```\n\n## References VS Relations\n\nIn this section i hope to explain a little bit more about the differences between this two methods of creation some sort of relationship between documents.\n\n### What is a Reference\n\nReferences are a pretty simple concept, they are pretty much an array of foreign keys, you can store keys in a field of a document as a form of saying \"x keys is related in y form to this document\".\n\n#### The problem\n\nWhile Nekdis provides a way to auto fetch this relations, meaning it will fetch the keys and append them to the field as an array, this **is not Atomic** which can lead to some problems.\nWhile this could be made atomic it would be somewhat of a breaking that im not sure its worth to make specially because relations exist.\n\n### What is a relation\n\nA relation as the name implies is a way to relate documents to each other, they work somewhat like graph relations and were inspired by the way [SurrealDB](https://surrealdb.com/) does relations.\n\n#### The problem\n\nRelations are expensive to run given redis runs on memory, this is because a lot of work and what we call omitted documents have to be created on the background for everything to work.\n\n### Pros and Cons\n\n| Type      | Is Atomic | Requires Lua | Works in redis-cli | Heavy on memory | Searchable | Allows Metadata |\n| --------- | :-------: | :----------: | :----------------: | :-------------: | :--------: | :-------------: |\n| Reference |     ❌     |      ❌       |         ❌          |        ❌        |     ❌      |        ❌        |\n| Relation  |     ✔️     |      ✔️       |         ✔️          |        ✔️        |     ✔️      |        ✔️        |\n\n### When to and not to use relations\n\nWhile relations provide an amazing api they are expensive on ram specially at scale, im not here to say not to use them but to be aware of the drawbacks and requirements before doing so.\n\nReferences are yes way more limiting than relations as of now but you can still map a lot of relationships with them if you use them properly.\n\n## Modularity with client modules\n\nNekdis allows you to add your own modules to the client, this allows easier access to the raw node-redis client and a way to export all your higher functions in one go.\n\nHowever there was a slight problem if you will regarding implementing this in a way that the types would still work, thats why instead of passing them to the constructor we provide a `withModules` method so even in javascript you can have intellisense on your modules.\n\n```ts\nimport { type Client, client } from \"nekdis\";\n\nclass MyModule {\n    constructor(client: Client) {\n        // Do something\n    }\n\n    myFunction() {\n        // Do something\n    }\n}\n\nclient.withModules({ name: \"myMod\", ctor: MyModule });\n\n// Access it\nclient.myMod.myFunction()\n```\n\n## Modularity with base injections\n\nNekdis provides a way to set bases to your schemas, this means that you can have \"global\" definitions, methods and options that will be applied to every schema you create.\n\nThis is pretty useful specially when creating methods that you want to have across all your models.\n\nA good example of this can be found [\u003cu\u003ehere\u003c/u\u003e](./examples/table-module.ts).\n\n# Schema field list\n\n| Type        | String Notation | Literal value support | Sortable | Index type |\n| ----------- | :-------------: | :-------------------: | :------: | :--------: |\n| `string`    |        ✔️        |           ✔️           |    ✔️     |   `TAG`    |\n| `number`    |        ✔️        |           ✔️           |    ✔️     | `NUMERIC`  |\n| `bigint`    |        ✔️        |           ✔️           |    ✔️     |   `TAG`    |\n| `boolean`   |        ✔️        |           ❌           |    ✔️     |   `TAG`    |\n| `text`      |        ✔️        |           ❌           |    ✔️     |   `TEXT`   |\n| `date`      |        ✔️        |           ❌           |    ✔️     | `NUMERIC`  |\n| `point`     |        ✔️        |           ❌           |    ✔️     |   `GEO`    |\n| `vector`    |        ✔️        |           ❌           |    ❌     |  `VECTOR`  |\n| `array`     |        ✔️        |           ❌           |    ❌     |     -      |\n| `tuple`     |        ❌        |           ❌           |    ❌     |     -      |\n| `object`    |        ❌        |           ❌           |    ❌     |     -      |\n| `reference` |        ❌        |           ❌           |    ❌     |     -      |\n| `relation`  |        ❌        |           ❌           |    ❌     |     -      |\n\nTo check the extra options for each field, refer to the [types](./src/typings/schema-and-fields-definition.ts).\n\n# Nekdis VS Redis-OM\n\nIn this part of the document im going to cover how this proposal compares to the current redis-om (0.4.2) and the major differences.\n\n## Client\n\nIn Nekdis the `Client` does not provide any methods to interact directly with the database and its pretty much only used to store your models and handle the connection, **however** you can access the `node-redis` client by accessing `client.raw`.\n\n## Schema\n\nThe schema in Nekdis is just where you define the shape of your data while in redis-om it also takes care of creating indexes and some other internal bits.\n\nWith this comes the big question \"Well, why not use just a plain object then\", the simple answer to this question is ease of use but to explain it further, having the schema defined this way allows the library to internally check if there isn't anything missing and parse it so you are allowed to use the shorthand methods like `field: \"string\"`, this approach also allows for you to define methods an options that will be passed down to the model down the road and not to mention that this is one of the only ways to have references working properly without affecting performance.\n\n## Model vs Repository\n\nIn redis-om you use a `repository` to interact with the db by using methods like `fetch`, `save` and `search`.\n\nIn Nekdis the `model` is not that different but it allows you to add more functionality to it (see: [Custom Methods](#custom-methods)) and overall gives more functionality out of the box.\n\n### Nekdis Document\n\nIn Nekdis you have what are called documents, this is just an abstraction to the data to allow better interaction with references and faster parsing.\n\nAt first this might look daunting compared to redis-om that now uses plain objects but i can assure you that there isn't that much of a difference, and i will give some examples to demonstrate it.\n\n#### Creating and saving\n\nSee, its just as easy\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eNekdis\u003c/th\u003e\n\u003cth\u003eRedis-OM\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\nawait model.createAndSave({\n    name: \"DidaS\"\n});\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\nawait repository.save({\n    name: \"DidaS\"\n});\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n#### Creating and mutating\n\nThis is where things start to be a bit different, even tho you **can** use a plain object that isn't recommended since it would just use more memory.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eNekdis\u003c/th\u003e\n\u003cth\u003eNekdis with plain object\u003c/th\u003e\n\u003cth\u003eRedis-OM\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// You can pass values directly to it\n// just like in createAndSave\nconst data = model.create({\n    name: \"DidaS\"\n});\n\n// mutate data\ndata.year = 2023;\n\nawait model.save(data);\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// Doing it this way will use more memory\n// and you wont have intellisense\nconst data = {\n    name: \"DidaS\"\n}\n\n// mutate data\ndata.year = 2023;\n\nawait model.createAndSave(data);\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\nconst data = {\n    name: \"DidaS\"\n}\n\n// mutate data\ndata.year = 2023;\n\nawait repository.save(data);\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Search\n\nLooking at search for the first time it is pretty much the same, the only difference is that `equals` operations exist in **every** data type so a lot of times changing the data type in the schema wont break the query **and** the best part is that `eq`, `equals` and other operators like them support arrays (so they pretty much work like an `in` operator).\n\n## Nested objects\n\nCurrently in redis-om you need to define a path for each field to define your nested objects, meanwhile in Nekdis they just work like normal js objects!\n\nThere are several advantages to this, two of the main ones being, faster serialization/deserialization and simpler to use, here is an example comparing both\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eNekdis\u003c/th\u003e\n\u003cth\u003eRedis-OM\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\nclient.schema({\n    field: {\n        type: \"object\",\n        properties: {\n            aNumberInsideOfIt: \"number\",\n            nesting: {\n                type: \"object\",\n                properties: {\n                    doubleNested: \"boolean\"\n                }\n            }\n        }\n    }\n})\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\nSchema(\"OM\", {\n    aNumberInsideOfIt: {\n        type: \"number\",\n        path: \"$.field.aNumberInsideOfIt\"\n    },\n    doubleNested: {\n        type: \"boolean\",\n        path: \"$.field.nesting.doubleNested\"\n    }\n})\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## A Simple example\n\nThis is a simple program example that generates 30 random users with random ages and fetches the ones matching a certain age just to show the differences between the libraries\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eNekdis\u003c/th\u003e\n\u003cth\u003eRedis-OM\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// Import the global client\nimport { client } from \"nekdis\";\n\n// Connect to the db\nawait client.connect();\n\n// Create the schema\nconst userSchema = client.schema({\n    age: { type: \"number\", index: true }\n}, {\n    // Define function to help repetitive task\n    findBetweenAge: async function (min: number, max: number) {\n        return await this.search().where(\"age\").between(min, max).returnAll();\n    }\n    // Add creation date to the key\n}, { suffix: () =\u003e Date.now().toString() });\n\n// Create the interface\nconst userModel = client.model(\"User\", userSchema);\n\n// Create the search index\nawait userModel.createIndex();\n\n// Generate 30 users\nfor (let i = 0; i \u003c 30; i++) {\n    await userModel.createAndSave({\n        age: between(18, 90)\n    });\n}\n\n// Get the users that are between 30 and 50 years old\nconst users = await userModel.findBetweenAge(30, 50);\n\n// Log the users\nconsole.log(users)\n\n// Close the connection\nawait client.disconnect();\n\n// A helper function that generates a random number between min and max\nfunction between(min: number, max: number) {\n    return Math.round(Math.random() * (max - min + 1)) + min;\n};\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// Node stuff for the id\nimport { randomUUID } from \"node:crypto\";\n// Import the redis client\nimport { createClient } from \"redis\";\n// Import OM utilities\nimport { Schema, Repository, Entity, EntityId } from \"redis-om\";\n\n// Create Client\nconst client = createClient()\n\n// Connect to the db\nawait client.connect();\n\n// Create the schema\nconst userSchema = new Schema(\"User\", {\n    age: { type: \"number\" }\n});\n\n// Create an interface to allow type safe manipulation\n// However you will need to use it everywhere \n// If you are using js you would need to do it in jsdoc for it to work\ninterface UserEntity extends Entity {\n    age: number\n}\n\n// Create the interface\nconst userRepository = new Repository(userSchema, client);\n\n// Create the search index\nawait userRepository.createIndex();\n\n// Generate 30 users\nfor (let i = 0; i \u003c 30; i++) {\n    await userRepository.save({\n        // We set the \"suffix\" and random id to somewhat match Nekdis (still not 100% accurate you would need even more) \n        [EntityId]: `${Date.now()}:${randomUUID()}`,\n        age: between(18, 90)\n    });\n}\n\n// Get the users that are between 30 and 50 years old\nconst users = await findBetweenAge(userRepository, 30, 50);\n\n// Log the users\nconsole.log(users)\n\n// Close the connection\nawait client.disconnect();\n\n// Define function to help repetitive task\nasync function findBetweenAge(repository: Repository, min: number, max: number): Promise\u003cArray\u003cUserEntity\u003e\u003e {\n    // Type assertion so ts does not complain\n    return \u003cArray\u003cUserEntity\u003e\u003eawait repository.search().where(\"age\").between(min, max).returnAll();\n}\n\n// A helper function that generates a random number between min and max\nfunction between(min: number, max: number) {\n    return Math.round(Math.random() * (max - min + 1)) + min;\n};\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Benchmarks\n\nThere were a lot of benchmarks made and they can be found [here](./BENCHRESULTS.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidas-git%2Fnekdis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidas-git%2Fnekdis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidas-git%2Fnekdis/lists"}