{"id":18830263,"url":"https://github.com/sopherapps/orredis","last_synced_at":"2025-04-14T03:42:11.347Z","repository":{"id":60331373,"uuid":"534742167","full_name":"sopherapps/orredis","owner":"sopherapps","description":"A Fast pydantic python ORM for redis built in rust","archived":false,"fork":false,"pushed_at":"2023-01-02T06:51:50.000Z","size":285,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T19:52:07.468Z","etag":null,"topics":["orm","pydantic","redis"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sopherapps.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-09T17:30:00.000Z","updated_at":"2024-12-21T07:16:39.000Z","dependencies_parsed_at":"2023-02-01T01:55:17.393Z","dependency_job_id":null,"html_url":"https://github.com/sopherapps/orredis","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sopherapps%2Forredis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sopherapps%2Forredis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sopherapps%2Forredis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sopherapps%2Forredis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sopherapps","download_url":"https://codeload.github.com/sopherapps/orredis/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248818736,"owners_count":21166468,"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":["orm","pydantic","redis"],"created_at":"2024-11-08T01:48:18.911Z","updated_at":"2025-04-14T03:42:11.329Z","avatar_url":"https://github.com/sopherapps.png","language":"Rust","readme":"# orredis\n\n[![PyPI version](https://badge.fury.io/py/orredis.svg)](https://badge.fury.io/py/orredis) ![CI](https://github.com/sopherapps/orredis/actions/workflows/CI.yml/badge.svg)\n\nA fast pydantic-based ORM for redis supporting synchronous and asynchronous interaction with pooled connections to\nredis. It is written in rust but runs in python v3.7+\n\n## Purpose\n\nAn object-relational-mapping makes writing business logic intuitive because the data representation is closer to what\nthe real-life situation is. It helps decouple the way such data is programmed from the way such data is actually\npersisted\nin any of the data persistence technologies we have, typically a database.\n\nTake the example of a book.\nIn code, one will represent a book as an object with a number of properties such as \"title\", \"edition\", \"author\" etc.\n\n```python\nclass Book(Model):\n  title: str\n  edition: int\n  author: Author\n```\n\nHowever, in the underlying data store, the same book could be saved as say, a row in a table for a relational database\nlike PostgreSQL,\nor as a document in a document-based NoSQL databases like MongoDB or as a hashmap in redis.\nOf these, the document-based NoSQL databases are the closest to the definition in code.\n\nFor MongoDB, the same book might be represented as the object below:\n\n```json\n{\n  \"id\": \"some-random-string\",\n  \"title\": \"the title of the book\",\n  \"edition\": 2,\n  \"author\": {\n    \"name\": \"Charles Payne\",\n    \"yearsActive\": [\n      1992,\n      2008\n    ]\n  }\n}\n```\n\nAs you can see, it is still quite different.\n\nHowever, for redis, the representation is even going to be further off.\nIt will most likely be saved as hashmap, with a given key. The properties of book will be 'fields' for that hashmap.\n\nIn order to interact with the book representation in the redis server, one has to write commands like:\n\n```shell\n# to save the book in the data store\nHSET \"some key\" \"title\" \"the title of the book\" \"edition\" 2 \"author\" \"{\\\"name\\\":\\\"Charles Payne\\\",\\\"yearsActive\\\":[1992,2008]}\"\n# to retrieve the entire book\nHGETALL \"some key\"\n# to retrieve just a few details of the book\nHMGET \"some key\" \"title\" \"edition\"\n# to update the book - see the confusion? are you saving a new book or updating one?\nHSET \"some key\" \"edition\" 2\n# to delete the book\nDEL \"some key\"\n```\n\nThe above is so unrelated to the business logic that most of us will take a number of minutes or hours trying to\nunderstand what kind of data is even being saved. Is it a book or some random stuff?\n\nNow consider something like this:\n\n```python\nbook = Book(title=\"some title\", edition=2, author=Author(name=\"Charles Payne\", years_active=(1992, 2008)))\nstore = Store(url=\"redis://localhost:6379/0\", pool_size=5, default_ttl=3000, timeout=1)\nstore.create_collection(model=Book, primary_key_field=\"title\")\nbook_collection = store.get_collection(Book)\n# Do I even need to add a comment here?\n# to save books\nbook_collection.add_one(book)\nbook_collection.add_many([book, book.with_changes(edition=2), book.with_changes(title=\"another title\")])\n# to retrieve books\nbook_collection.get_one(id=\"some title\")\nbook_collection.get_all()\nbook_collection.get_many(ids=[\"some title\", \"another title\"])\n# to get a few details of books (returns a dictionary)\nbook_collection.get_one_partially(\"some title\", fields=[\"title\", \"edition\"])\nbook_collection.get_all_partiallly(fields=[\"title\", \"edition\"])\nbook_collection.get_many_partially(ids=[\"some title\", \"another title\"], __fields=[\"title\", \"edition\"])\n\n# or to update\nbook_collection.update_one(\"some title\", data={\"edition\": 1})\n# or to delete\nbook_collection.delete_many(ids=[\"some title\", \"another title\"])\n\n# clear all data in store\nstore.clear()\n```\n\nBeautiful, isn't it?\n\nNow imagine using all that, and getting the extra perk of your code running really really fast because it was\nimplemented\nin rust, just for the fun of it.\n\nUh? You like?\n\nThat is the purpose of this project.\n\n## Dependencies\n\n- python +v3.7\n- redis server (yes, you need have a redis server somewhere)\n- [pydantic](https://github.com/samuelcolvin/pydantic/)\n\n## Quick Start (Synchronous API)\n\n- Install the package\n\n  ```bash\n  pip install orredis\n  ```\n\n- Import the `Store` and the `Model` classes and use accordingly\n\n```python\nfrom datetime import date\nfrom typing import Tuple, List\nfrom orredis import Model, Store\n\n\n# type annotations are the schema. \n# Don't leave them out or you will just be getting strings for every property when you retrieve an object\nclass Author(Model):\n  name: str\n  active_years: Tuple[int, int]\n\n\nclass Book(Model):\n  title: str\n  author: Author\n  rating: float\n  published_on: date\n  tags: List[str] = []\n  in_stock: bool = True\n\n\nclass Library(Model):\n  name: str\n  address: str\n\n\n# Create the store and add create a collection for each model\n# - `default_ttl` is the default time to live for each record is the store. \n#   records never expire if there is no default_ttl set, and no `ttl` is given when adding that record to the store\n# - `timeout` is the number of milliseconds beyond which the connection to redis will raise a timeout error if\n#   it fails to establish a connection.\nstore = Store(url=\"redis://localhost:6379/0\", pool_size=5, default_ttl=3000, timeout=1000)\n# - `identifier_fields` are the properties on the model that uniquely identify a single record. They form an id.\nstore.create_collection(model=Author, primary_key_field=\"name\")\nstore.create_collection(model=Book, primary_key_field=\"title\")\nstore.create_collection(model=Library, primary_key_field=\"name\")\n\n# sample authors. You can create as many as you wish anywhere in the code\n\nauthors = {\n  \"charles\": Author(name=\"Charles Dickens\", active_years=(1220, 1280)),\n  \"jane\": Author(name=\"Jane Austen\", active_years=(1580, 1640)),\n}\n\n# Sample books.\nbooks = [\n  Book(title=\"Oliver Twist\", author=authors[\"charles\"], published_on=date(year=1215, month=4, day=4),\n       in_stock=False, rating=2, tags=[\"Classic\"]),\n  Book(title=\"Great Expectations\", author=authors[\"charles\"], published_on=date(year=1220, month=4, day=4),\n       rating=5,\n       tags=[\"Classic\"]),\n  Book(title=\"Jane Eyre\", author=authors[\"charles\"], published_on=date(year=1225, month=6, day=4), in_stock=False,\n       rating=3.4, tags=[\"Classic\", \"Romance\"]),\n  Book(title=\"Wuthering Heights\", author=authors[\"jane\"], published_on=date(year=1600, month=4, day=4),\n       rating=4.0,\n       tags=[\"Classic\", \"Romance\"]),\n]\n\n# Some library objects\nlibraries = [\n  Library(name=\"The Grand Library\", address=\"Kinogozi, Hoima, Uganda\"),\n  Library(name=\"Christian Library\", address=\"Buhimba, Hoima, Uganda\")\n]\n\n# Get the collections\nbook_collection = store.get_collection(\n  model=Book)  # you can have as many instances of this collection as you wish to have\nlibrary_collection = store.get_collection(model=Library)\nauthor_collection = store.get_collection(model=Author)\n\n# insert the data\nbook_collection.add_many(books)  # (the associated authors will be automatically inserted)\nlibrary_collection.add_many(libraries,\n                            ttl=3000)  # you can even specify the ttl for only these libraries when adding them\n\n# Get all books\nall_books = book_collection.get_all()\nprint(\n  all_books)  # Will print [Book(title=\"Oliver Twist\", author=\"Charles Dickens\", published_on=date(year=1215, month=4, day=4), in_stock=False), Book(...]\n\n# Or get some books\nsome_books = book_collection.get_many(ids=[\"Oliver Twist\", \"Jane Eyre\"])\nprint(some_books)  # Will print only those two books\n\n# Or select some authors\nsome_authors = author_collection.get_many(ids=[\"Jane Austen\"])\nprint(some_authors)  # Will print Jane Austen even though you didn't explicitly insert her in the Author's collection\n\n# Or only get a few some properties of the book. THIS RETURNS DICTIONARIES not MODEL Instances\nbooks_with_few_fields = book_collection.get_all_partially(fields=[\"author\", \"in_stock\"])\nprint(books_with_few_fields)  # Will print [{\"author\": \"'Charles Dickens\", \"in_stock\": \"True\"},...]\n# there is also get_one_partially, get_many_partially\n\n# Update any book or library\nbook_collection.update_one(\"Oliver Twist\", data={\"author\": authors[\"jane\"]})\n# You could even update a given author's details by nesting their new data in a book update\nupdated_jane = authors[\"jane\"].with_changes(\n  {\"active_years\": (1999, 2008)})  # create a new record from an old one, with only a few changes\nbook_collection.update_one(\"Oliver Twist\", data={\"author\": updated_jane})\n# Trying to retrieve jane directly will return her with the new details\n# All other books that have Jane Austen as author will also have their data updated. (like a real relationship)\nauthor_collection.get_one(\"Jane Austen\")\n\n# Delete any number of items\nlibrary_collection.delete_many(ids=[\"The Grand Library\"])\n```\n\n## Quick Start (Asynchronous API)\n\n- Install the package\n\n  ```bash\n  pip install orredis\n  ```\n\n- Import the `AsyncStore` and the `Model` classes and use accordingly\n\n```python\nimport asyncio\nfrom datetime import date\nfrom typing import Tuple, List\nfrom orredis import Model, AsyncStore\n\n\n# type annotations are the schema.\n# Don't leave them out or you will just be getting strings for every property when you retrieve an object\nclass Author(Model):\n  name: str\n  active_years: Tuple[int, int]\n\n\nclass Book(Model):\n  title: str\n  author: Author\n  rating: float\n  published_on: date\n  tags: List[str] = []\n  in_stock: bool = True\n\n\nclass Library(Model):\n  name: str\n  address: str\n\n\n# Create the store and add create a collection for each model\n# - `default_ttl` is the default time to live for each record is the store.\n#   records never expire if there is no default_ttl set, and no `ttl` is given when adding that record to the store\n# - `timeout` is the number of milliseconds beyond which the connection to redis will raise a timeout error if\n#   it fails to establish a connection.\nstore = AsyncStore(url=\"redis://localhost:6379/0\", pool_size=5, default_ttl=3000, timeout=1000)\n# - `identifier_fields` are the properties on the model that uniquely identify a single record. They form an id.\nstore.create_collection(model=Author, primary_key_field=\"name\")\nstore.create_collection(model=Book, primary_key_field=\"title\")\nstore.create_collection(model=Library, primary_key_field=\"name\")\n\n# sample authors. You can create as many as you wish anywhere in the code\n\nauthors = {\n  \"charles\": Author(name=\"Charles Dickens\", active_years=(1220, 1280)),\n  \"jane\": Author(name=\"Jane Austen\", active_years=(1580, 1640)),\n}\n\n# Sample books.\nbooks = [\n  Book(title=\"Oliver Twist\", author=authors[\"charles\"], published_on=date(year=1215, month=4, day=4),\n       in_stock=False, rating=2, tags=[\"Classic\"]),\n  Book(title=\"Great Expectations\", author=authors[\"charles\"], published_on=date(year=1220, month=4, day=4),\n       rating=5,\n       tags=[\"Classic\"]),\n  Book(title=\"Jane Eyre\", author=authors[\"charles\"], published_on=date(year=1225, month=6, day=4), in_stock=False,\n       rating=3.4, tags=[\"Classic\", \"Romance\"]),\n  Book(title=\"Wuthering Heights\", author=authors[\"jane\"], published_on=date(year=1600, month=4, day=4),\n       rating=4.0,\n       tags=[\"Classic\", \"Romance\"]),\n]\n\n# Some library objects\nlibraries = [\n  Library(name=\"The Grand Library\", address=\"Kinogozi, Hoima, Uganda\"),\n  Library(name=\"Christian Library\", address=\"Buhimba, Hoima, Uganda\")\n]\n\n\nasync def run_async_example():\n  # Get the collections\n  book_collection = store.get_collection(\n    model=Book)  # you can have as many instances of this collection as you wish to have\n  library_collection = store.get_collection(model=Library)\n  author_collection = store.get_collection(model=Author)\n\n  # insert the data\n  await book_collection.add_many(books)  # (the associated authors will be automatically inserted)\n  await library_collection.add_many(libraries,\n                                    ttl=3000)  # you can even specify the ttl for only these libraries when adding them\n\n  # Get all books\n  all_books = await book_collection.get_all()\n  print(\n    all_books)  # Will print [Book(title=\"Oliver Twist\", author=\"Charles Dickens\", published_on=date(year=1215, month=4, day=4), in_stock=False), Book(...]\n\n  # Or get some books\n  some_books = await book_collection.get_many(ids=[\"Oliver Twist\", \"Jane Eyre\"])\n  print(some_books)  # Will print only those two books\n\n  # Or select some authors\n  some_authors = await author_collection.get_many(ids=[\"Jane Austen\"])\n  print(\n    some_authors)  # Will print Jane Austen even though you didn't explicitly insert her in the Author's collection\n\n  # Or only get a few some properties of the book. THIS RETURNS DICTIONARIES not MODEL Instances\n  books_with_few_fields = await book_collection.get_all_partially(fields=[\"author\", \"in_stock\"])\n  print(books_with_few_fields)  # Will print [{\"author\": \"'Charles Dickens\", \"in_stock\": \"True\"},...]\n  # there is also get_one_partially, get_many_partially\n\n  # Update any book or library\n  await book_collection.update_one(\"Oliver Twist\", data={\"author\": authors[\"jane\"]})\n  # You could even update a given author's details by nesting their new data in a book update\n  updated_jane = authors[\"jane\"].with_changes(\n    {\"active_years\": (1999, 2008)})  # create a new record from an old one, with only a few changes\n  await book_collection.update_one(\"Oliver Twist\", data={\"author\": updated_jane})\n  # Trying to retrieve jane directly will return her with the new details\n  # All other books that have Jane Austen as author will also have their data updated. (like a real relationship)\n  await author_collection.get_one(\"Jane Austen\")\n\n  # Delete any number of items\n  await library_collection.delete_many(ids=[\"The Grand Library\"])\n\n\nasyncio.run(run_async_example())\n```\n\n## Benchmarks\n\nThis package has been benchmarked against some of the pre-existing ORMs for redis and this is how it stacks up against\nthem:\n\n### orredis (asynchronous)\n\n```\n---------------------------------------------------------- benchmark: 11 tests ----------------------------------------------------------\nName (time in us)                                                                Mean                Min                    Max          \n-----------------------------------------------------------------------------------------------------------------------------------------\nbenchmark_async_delete[async_book_collection-Wuthering Heights]               21.3601 (1.66)      6.1050 (1.0)      19,299.5530 (17.09)  \nbenchmark_async_add_one[async_book_collection-book0]                          12.8834 (1.0)       6.1730 (1.01)      1,281.1460 (1.13)   \nbenchmark_async_update_one[async_book_collection-Wuthering Heights-data0]     13.8155 (1.07)      6.3400 (1.04)     15,867.4010 (14.05)  \nbenchmark_async_add_many[async_book_collection]                               17.5144 (1.36)      7.1700 (1.17)      1,129.5450 (1.0)    \nbenchmark_async_bulk_delete[async_book_collection]                            25.1278 (1.95)      7.1840 (1.18)     23,385.2130 (20.70)  \nbenchmark_async_get_all[async_book_collection]                                23.2657 (1.81)      8.2150 (1.35)      3,417.0570 (3.03)   \nbenchmark_async_get_one[async_book_collection-book0]                          22.6506 (1.76)      8.2610 (1.35)      1,202.5950 (1.06)   \nbenchmark_async_get_many_partially[async_book_collection]                     25.1589 (1.95)     10.7620 (1.76)      1,369.5500 (1.21)   \nbenchmark_async_get_one_partially[async_book_collection-book0]                25.0272 (1.94)     11.4470 (1.88)     15,109.6220 (13.38)  \nbenchmark_async_get_many[async_book_collection]                               24.9438 (1.94)     11.6200 (1.90)      2,231.5890 (1.98)   \nbenchmark_async_get_all_partially[async_book_collection]                      25.7168 (2.00)     11.8590 (1.94)     17,399.2010 (15.40)  \n-----------------------------------------------------------------------------------------------------------------------------------------\n\n```\n\n### orredis (synchronous)\n\n``` \n----------------------------------------------------- benchmark: 11 tests -----------------------------------------------------\nName (time in us)                                                     Mean                 Min                    Max          \n-------------------------------------------------------------------------------------------------------------------------------\nbenchmark_delete[book_collection-Wuthering Heights]                73.2168 (1.0)       59.7780 (1.0)         293.7510 (1.20)   \nbenchmark_bulk_delete[book_collection]                             76.1323 (1.04)      63.0270 (1.05)        244.3730 (1.0)    \nbenchmark_update_one[book_collection-Wuthering Heights-data0]     124.3903 (1.70)     102.1310 (1.71)        296.9740 (1.22)   \nbenchmark_add_one[book_collection-book0]                          155.5704 (2.12)     129.7560 (2.17)        393.7910 (1.61)   \nbenchmark_get_one_partially[book_collection-book0]                169.0863 (2.31)     137.9540 (2.31)        338.4000 (1.38)   \nbenchmark_get_one[book_collection-book0]                          202.6351 (2.77)     167.3440 (2.80)        580.5420 (2.38)   \nbenchmark_get_many_partially[book_collection]                     213.8824 (2.92)     181.9030 (3.04)        513.8720 (2.10)   \nbenchmark_get_many[book_collection]                               265.6097 (3.63)     221.0640 (3.70)        641.8550 (2.63)   \nbenchmark_get_all_partially[book_collection]                      298.5290 (4.08)     258.2100 (4.32)        606.2200 (2.48)   \nbenchmark_add_many[book_collection]                               352.7892 (4.82)     287.3370 (4.81)     15,414.2120 (63.08)  \nbenchmark_get_all[book_collection]                                398.7546 (5.45)     356.0230 (5.96)        813.4560 (3.33)   \n-------------------------------------------------------------------------------------------------------------------------------\n\n```\n\n### [pydantic-redis (pure-python)](https://github.com/sopherapps/pydantic-redis)\n\n``` \n------------------------------------------------- benchmark: 20 tests -------------------------------------------------\nName (time in us)                                              Mean                 Min                   Max          \n-----------------------------------------------------------------------------------------------------------------------\nbenchmark_select_columns_for_one_id[redis_store-book1]     143.5316 (1.08)     117.4340 (1.0)        347.5900 (1.0)    \nbenchmark_select_columns_for_one_id[redis_store-book3]     151.6032 (1.14)     117.6690 (1.00)       405.4620 (1.17)   \nbenchmark_select_columns_for_one_id[redis_store-book0]     133.0856 (1.0)      117.8720 (1.00)       403.9400 (1.16)   \nbenchmark_select_columns_for_one_id[redis_store-book2]     156.8152 (1.18)     118.7220 (1.01)       569.9800 (1.64)   \nbenchmark_select_columns_for_some_items[redis_store]       138.0488 (1.04)     120.1550 (1.02)       350.7040 (1.01)   \nbenchmark_delete[redis_store-Wuthering Heights]            199.9205 (1.50)     127.6990 (1.09)     1,092.2190 (3.14)   \nbenchmark_bulk_delete[redis_store]                         178.4756 (1.34)     143.7480 (1.22)       647.6660 (1.86)   \nbenchmark_select_all_for_one_id[redis_store-book1]         245.7787 (1.85)     195.2030 (1.66)       528.9250 (1.52)   \nbenchmark_select_all_for_one_id[redis_store-book0]         239.1152 (1.80)     199.4360 (1.70)       767.2540 (2.21)   \nbenchmark_select_all_for_one_id[redis_store-book3]         243.8724 (1.83)     200.8060 (1.71)       535.3640 (1.54)   \nbenchmark_select_all_for_one_id[redis_store-book2]         256.1625 (1.92)     202.4630 (1.72)       701.3000 (2.02)   \nbenchmark_update[redis_store-Wuthering Heights-data0]      329.1363 (2.47)     266.9700 (2.27)       742.1360 (2.14)   \nbenchmark_select_some_items[redis_store]                   301.0471 (2.26)     268.9410 (2.29)       551.1060 (1.59)   \nbenchmark_select_columns[redis_store]                      313.4356 (2.36)     281.4460 (2.40)       578.7730 (1.67)   \nbenchmark_single_insert[redis_store-book2]                 348.5624 (2.62)     297.3610 (2.53)       580.8780 (1.67)   \nbenchmark_single_insert[redis_store-book1]                 342.1879 (2.57)     297.5410 (2.53)       650.5420 (1.87)   \nbenchmark_single_insert[redis_store-book0]                 366.4513 (2.75)     310.1640 (2.64)       660.5380 (1.90)   \nbenchmark_single_insert[redis_store-book3]                 377.6208 (2.84)     327.5290 (2.79)       643.4090 (1.85)   \nbenchmark_select_default[redis_store]                      486.6931 (3.66)     428.8810 (3.65)     1,181.9620 (3.40)   \nbenchmark_bulk_insert[redis_store]                         897.7862 (6.75)     848.7410 (7.23)     1,188.5160 (3.42)   \n-----------------------------------------------------------------------------------------------------------------------\n```\n\n## Contributions\n\nContributions are welcome. The docs have to maintained, the code has to be made cleaner, more idiomatic and faster,\nand there might be need for someone else to take over this repo in case I move on to other things. It happens!\n\nFirst thing is you probably need to know a bit of rust. Consider reading\nthe [rust book](https://doc.rust-lang.org/book/).\nIt can be a very interesting read, albeit a long one.\n\nWhen you are ready, look at the [CONTRIBUTIONS GUIDELINES](./docs/CONTRIBUTING.md)\n\nThen you can read through the [SYSTEM DESIGN](./docs/SYSTEM_DESIGN.md) document to get a feel of what exactly is going\non under the hood.\n\n### TODO:\n\n- [ ] Add pagination for `collection.get_all()` and `collection.get_all_partially()`\n- [x] Add an asynchronous API with same exact data manipulation and querying methods\n- [ ] Add and host proper documentation\n\n### How to Test\n\n\n- Clone the repo and enter its root folder\n\n  ```bash\n  git clone https://github.com/sopherapps/orredis.git \u0026\u0026 cd orredis\n  ```\n\n- Create a virtual environment and activate it\n\n  ```bash\n  virtualenv -p /usr/bin/python3.7 env \u0026\u0026 source env/bin/activate\n  ```\n\n- Install the dependencies\n\n  ```bash\n  pip install -r requirements.txt\n  ```\n\n- Install orredis package in the virtual environment\n\n  ```bash\n  maturin develop\n  ```\n\n  For optimized build use:\n\n  ```bash\n  maturin develop -r\n  ```\n\n- Run the tests command\n\n  ```bash\n  pytest --benchmark-disable\n  ```\n\n- Run benchmarks\n\n  ```bash\n  pytest --benchmark-compare --benchmark-autosave\n  ```\n\n  OR the summary\n\n  ```shell\n  # synchronous API\n  pytest test/test_benchmarks.py --benchmark-columns=mean,min,max --benchmark-name=short \n  ```\n\n  ```shell\n  # asynchronous API\n  pytest test/test_async_benchmarks.py --benchmark-columns=mean,min,max --benchmark-name=short\n  ```\n\n## Acknowledgement\n\n- The asyncio module code was adapted from [pyo3-asyncio](https://github.com/awestlake87/pyo3-asyncio)\n- The python-rust bindings were got from [the pyo3 project](https://github.com/PyO3)\n\n## License\n\nLicensed under both the [MIT License](./LICENSE-MIT) and the [APACHE (2.0) License](./LICENSE-APACHE)\n\nCopyright (c) 2022 [Martin Ahindura](https://github.com/tinitto)\n\nCopyright (c) 2017-present PyO3 Project and Contributors\n\n## Gratitude\n\n\u003e \"Jesus answered him(Thomas), 'I am the way, the truth and the life; no one goes to the Father except by Me'\"\n\u003e\n\u003e -- John 14: 6\n\nAll glory be to God.\n\n\u003ca href=\"https://www.buymeacoffee.com/martinahinJ\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" \u003e\u003c/a\u003e\n","funding_links":["https://www.buymeacoffee.com/martinahinJ"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsopherapps%2Forredis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsopherapps%2Forredis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsopherapps%2Forredis/lists"}