{"id":13501301,"url":"https://github.com/saadmk11/redis-search-django","last_synced_at":"2025-04-04T13:05:47.668Z","repository":{"id":56722380,"uuid":"524121282","full_name":"saadmk11/redis-search-django","owner":"saadmk11","description":"Django package that provides auto indexing and searching capabilities for Django model instances using RediSearch.","archived":false,"fork":false,"pushed_at":"2025-03-03T21:27:26.000Z","size":190,"stargazers_count":93,"open_issues_count":10,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T12:05:51.021Z","etag":null,"topics":["django","django-application","django-packages","django-redis","django-search","django-search-engine","python","python-redis","python3","redis","redis-django","redis-python","redis-search","redis-search-django","redisearch","redisjson"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/redis-search-django/","language":"Python","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/saadmk11.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-08-12T14:39:09.000Z","updated_at":"2025-02-25T08:17:41.000Z","dependencies_parsed_at":"2023-09-26T13:37:51.358Z","dependency_job_id":"90f21f5b-5187-4ff6-83fa-48a6604a5ecb","html_url":"https://github.com/saadmk11/redis-search-django","commit_stats":{"total_commits":68,"total_committers":4,"mean_commits":17.0,"dds":"0.17647058823529416","last_synced_commit":"d0fd8203524dab7ae7665e68bec6860ca732be18"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saadmk11%2Fredis-search-django","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saadmk11%2Fredis-search-django/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saadmk11%2Fredis-search-django/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saadmk11%2Fredis-search-django/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saadmk11","download_url":"https://codeload.github.com/saadmk11/redis-search-django/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182335,"owners_count":20897379,"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":["django","django-application","django-packages","django-redis","django-search","django-search-engine","python","python-redis","python3","redis","redis-django","redis-python","redis-search","redis-search-django","redisearch","redisjson"],"created_at":"2024-07-31T22:01:32.301Z","updated_at":"2025-04-04T13:05:47.649Z","avatar_url":"https://github.com/saadmk11.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# redis-search-django\n\n[![Pypi Version](https://img.shields.io/pypi/v/redis-search-django.svg?style=flat-square)](https://pypi.org/project/redis-search-django/)\n[![Supported Python Versions](https://img.shields.io/pypi/pyversions/redis-search-django?style=flat-square)](https://pypi.org/project/redis-search-django/)\n[![Supported Django Versions](https://img.shields.io/pypi/frameworkversions/django/redis-search-django?color=darkgreen\u0026style=flat-square)](https://pypi.org/project/redis-search-django/)\n[![License](https://img.shields.io/github/license/saadmk11/redis-search-django?style=flat-square)](https://github.com/saadmk11/redis-search-django/blob/main/LICENSE)\n\n![Django Tests](https://img.shields.io/github/actions/workflow/status/saadmk11/redis-search-django/test.yml??label=Test\u0026style=flat-square\u0026branch=main)\n![Codecov](https://img.shields.io/codecov/c/github/saadmk11/redis-search-django?style=flat-square\u0026token=ugjHXbEKib)\n![pre-commit.ci](https://img.shields.io/badge/pre--commit.ci-enabled-brightgreen?logo=pre-commit\u0026logoColor=white\u0026style=flat-square)\n![Changelog-CI](https://img.shields.io/github/actions/workflow/status/saadmk11/redis-search-django/changelog-ci.yaml??label=Changelog-CI\u0026style=flat-square\u0026branch=main)\n![Code Style](https://img.shields.io/badge/Code%20Style-Black-black?style=flat-square)\n\n# About\n\nA Django package that provides **auto indexing** and **searching** capabilities for Django model instances using **[RediSearch](https://redis.io/docs/stack/search/)**.\n\n# Features\n\n- Management Command to create, update and populate the RediSearch Index.\n- Auto Index on Model object Create, Update and Delete.\n- Auto Index on Related Model object Add, Update, Remove and Delete.\n- Easy to create Document classes (Uses Django Model Form Class like structure).\n- Index nested models (e.g: `OneToOneField`, `ForeignKey` and `ManyToManyField`).\n- Search documents using `redis-om`.\n- Search Result Pagination.\n- Search Result Sorting.\n- RediSearch Result to Django QuerySet.\n- Faceted Search.\n\n# Requirements\n\n- Python: 3.7, 3.8, 3.9, 3.10\n- Django: 3.2, 4.0, 4.1\n- redis-om: \u003e= 0.0.27\n\n# Redis\n\n## Downloading Redis\n\nThe latest version of Redis is available from [Redis.io](https://redis.io/). You can also install Redis with your operating system's package manager.\n\n## RediSearch and RedisJSON\n\n`redis-search-django` relies on the [RediSearch](https://redis.io/docs/stack/search/) and [RedisJSON](https://redis.io/docs/stack/json/) Redis modules to support rich queries and embedded models.\nYou need these Redis modules to use `redis-search-django`.\n\nThe easiest way to run these Redis modules during local development is to use the [redis-stack](https://hub.docker.com/r/redis/redis-stack) Docker image.\n\n## Docker Compose\n\nThere is a `docker-compose.yaml` file provided in the project's root directory.\nThis file will run Redis with RedisJSON and RediSearch modules during development.\n\nRun the following command to start the Redis container:\n\n```bash\ndocker compose up -d\n```\n\n# Example Project\n\nThere is an example project available at [Example Project](https://github.com/saadmk11/redis-search-django/tree/main/example).\n\n\n# Documentation\n\n## Installation\n\n```bash\npip install redis-search-django\n```\n\nThen add `redis_search_django` to your `INSTALLED_APPS`:\n\n```bash\nINSTALLED_APPS = [\n    ...\n    'redis_search_django',\n]\n```\n\n## Usage\n\n### Document Types\n\nThere are **3 types** of documents class available:\n\n- **JsonDocument:** This uses `RedisJSON` to store the document. If you want to use Embedded Documents (Required For `OneToOneField`, `ForeignKey` and `ManyToManyField`) then use `JsonDocument`.\n- **EmbeddedJsonDocument:** If the document will be embedded inside another document class then use this. Embedded Json Documents are used for `OneToOneField`, `ForeignKey` and `ManyToManyField` or any types of nested documents.\n- **HashDocument:** This uses `RedisHash` to store the documents. It can not be used for nested documents.\n\n### Creating Document Classes\n\nYou need to inherit from The Base Document Classes mentioned above to build a document class.\n\n#### Simple Example\n\n**1. For Django Model:**\n\n```python\n# models.py\n\nfrom django.db import models\n\n\nclass Category(models.Model):\n    name = models.CharField(max_length=30)\n    slug = models.SlugField(max_length=30)\n\n    def __str__(self) -\u003e str:\n        return self.name\n```\n\n**2. You can create a document class like this:**\n\n**Note:** Document classes must be stored in `documents.py` file.\n\n```python\n# documents.py\n\nfrom redis_search_django.documents import JsonDocument\n\nfrom .models import Category\n\n\nclass CategoryDocument(JsonDocument):\n    class Django:\n        model = Category\n        fields = [\"name\", \"slug\"]\n```\n\n**3. Run Index Django Management Command to create the index on Redis:**\n\n```bash\npython manage.py index\n```\n\n**Note:** This will also populate the index with existing data from the database\n\nNow category objects will be indexed on create/update/delete.\n\n#### More Complex Example\n\n**1. For Django Models:**\n\n```python\n# models.py\n\nfrom django.db import models\n\n\nclass Tag(models.Model):\n    name = models.CharField(max_length=30)\n\n    def __str__(self) -\u003e str:\n        return self.name\n\n\nclass Vendor(models.Model):\n    name = models.CharField(max_length=30)\n    email = models.EmailField()\n    establishment_date = models.DateField()\n\n    def __str__(self) -\u003e str:\n        return self.name\n\n\nclass Product(models.Model):\n    name = models.CharField(max_length=256)\n    description = models.TextField(blank=True)\n    vendor = models.OneToOneField(Vendor, on_delete=models.CASCADE)\n    tags = models.ManyToManyField(Tag, blank=True)\n    price = models.DecimalField(max_digits=6, decimal_places=2)\n\n    def __str__(self) -\u003e str:\n        return self.name\n```\n\n**2. You can create a document classes like this:**\n\n**Note:** Document classes must be stored in `documents.py` file.\n\n```python\n# documents.py\n\nfrom typing import List\n\nfrom django.db import models\nfrom redis_om import Field\n\nfrom redis_search_django.documents import EmbeddedJsonDocument, JsonDocument\n\nfrom .models import Product, Tag, Vendor\n\n\nclass TagDocument(EmbeddedJsonDocument):\n    custom_field: str = Field(index=True, full_text_search=True)\n\n    class Django:\n        model = Tag\n        # Model Fields\n        fields = [\"name\"]\n\n    @classmethod\n    def prepare_custom_field(cls, obj):\n        return \"CUSTOM FIELD VALUE\"\n\n\nclass VendorDocument(EmbeddedJsonDocument):\n    class Django:\n        model = Vendor\n        # Model Fields\n        fields = [\"name\", \"establishment_date\"]\n\n\nclass ProductDocument(JsonDocument):\n    # OnetoOneField, with null=False\n    vendor: VendorDocument\n    # ManyToManyField\n    tags: List[TagDocument]\n\n    class Django:\n        model = Product\n        # Model Fields\n        fields = [\"name\", \"description\", \"price\"]\n        # Related Model Options\n        related_models = {\n            Vendor: {\n                \"related_name\": \"product\",\n                \"many\": False,\n            },\n            Tag: {\n                \"related_name\": \"product_set\",\n                \"many\": True,\n            },\n        }\n\n    @classmethod\n    def get_queryset(cls) -\u003e models.QuerySet:\n        \"\"\"Override Queryset to filter out available products.\"\"\"\n        return super().get_queryset().filter(available=True)\n\n    @classmethod\n    def prepare_name(cls, obj):\n        \"\"\"Use this to update field value.\"\"\"\n        return obj.name.upper()\n```\n\n**Note:**\n\n- You can not inherit from `HashDocument` for documents that include nested fields.\n- You need to inherit from `EmbeddedJsonDocument` for document classes that will be embedded inside another document class.\n- You need to explicitly add `OneToOneField`, `ForeignKey` or `ManyToManyField` (e.g: `tags: List[TagDocument]`) with an embedded document class if you want to index them.\n  you can not add it in the `Django.fields` option.\n- For `related_models` option, you need to specify the fields `related_name` and if it is a `ManyToManyField` or a `ForeignKey` Field then specify `\"many\": True`.\n- `related_models` will be used when a related object is saved that contributes to the document.\n- You can define `prepare_{field_name}` method to update the value of a field before indexing.\n- If it is a custom field (not a model field) you must define a `prepare_{field_name}` method that returns the value of the field.\n- You can override `get_queryset` method to provide more filtering. This will be used while indexing a queryset.\n- Field names must match model field names or define a `prepare_{field_name}` method.\n\n\n**3. Run Index Django Management Command to create the index on Redis:**\n\n```bash\npython manage.py index\n```\n\n**Note:** This will also populate the index with existing data from the database\n\n\n### Management Command\n\nThis package comes with `index` management command that can be used to index all the model instances to Redis index if it has a Document class defined.\n\n**Note:** Make sure that Redis is running before running the command.\n\nRun the following command to index **all** models that have Document classes defined:\n\n```bash\npython manage.py index\n```\n\nYou can use `--migrate-only` option to **only** update the **index schema**.\n\n```bash\npython manage.py index --migrate-only\n```\n\nYou can use `--models` to **specify** which models to index (models must have a Document class defined to be indexed).\n\n```bash\npython manage.py index --models app_name.ModelName app_name2.ModelName2\n```\n\n### Views\n\nYou can use the `redis_search_django.mixin.RediSearchListViewMixin` with a Django Generic View to search for documents.\n`RediSearchPaginator` which helps paginate `ReadiSearch` results is also added to this mixin.\n\n#### Example\n\n```python\n# views.py\n\nfrom django.utils.functional import cached_property\nfrom django.views.generic import ListView\nfrom redis.commands.search import reducers\n\nfrom redis_search_django.mixins import RediSearchListViewMixin\n\nfrom .documents import ProductDocument\nfrom .models import Product\n\n\nclass SearchView(RediSearchListViewMixin, ListView):\n    paginate_by = 20\n    model = Product\n    template_name = \"core/search.html\"\n    document_class = ProductDocument\n\n    @cached_property\n    def search_query_expression(self):\n        query = self.request.GET.get(\"query\")\n        query_expression = None\n\n        if query:\n            query_expression = (\n                self.document_class.name % query\n                | self.document_class.description % query\n            )\n\n        return query_expression\n\n    @cached_property\n    def sort_by(self):\n        return self.request.GET.get(\"sort\")\n\n    def facets(self):\n        if self.search_query_expression:\n            request = self.document_class.build_aggregate_request(\n                self.search_query_expression\n            )\n        else:\n            request = self.document_class.build_aggregate_request()\n\n        result = self.document_class.aggregate(\n            request.group_by(\n                [\"@tags_name\"],\n                reducers.count().alias(\"count\"),\n            )\n        )\n        return result\n```\n\n### Search\n\nThis package uses `redis-om` to search for documents.\n\n#### Example\n\n```python\nfrom .documents import ProductDocument\n\n\ncategories = [\"category1\", \"category2\"]\ntags = [\"tag1\", \"tag2\"]\n\n# Search For Products That Match The Search Query (name or description)\nquery_expression = (\n    ProductDocument.name % \"Some search query\"\n    | ProductDocument.description % \"Some search query\"\n)\n\n# Search For Products That Match The Price Range\nquery_expression = (\n    ProductDocument.price \u003e= float(10) \u0026 ProductDocument.price \u003c= float(100)\n)\n\n# Search for Products that include following Categories\nquery_expression = ProductDocument.category.name \u003c\u003c [\"category1\", \"category2\"]\n\n# Search for Products that include following Tags\nquery_expression = ProductDocument.tags.name \u003c\u003c [\"tag1\", \"tag2\"]\n\n# Query expression can be passed on the `find` method\nresult = ProductDocument.find(query_expression).sort_by(\"-price\").execute()\n```\n\nFor more details checkout [redis-om docs](https://github.com/redis/redis-om-python/blob/main/docs/getting_started.md)\n\n### RediSearch Aggregation / Faceted Search\n\n`redis-om` does not support faceted search (RediSearch Aggregation). So this package uses `redis-py` to do faceted search.\n\n#### Example\n\n```python\nfrom redis.commands.search import reducers\n\nfrom .documents import ProductDocument\n\n\nquery_expression = (\n    ProductDocument.name % \"Some search query\"\n    | ProductDocument.description % \"Some search query\"\n)\n\n# First we need to build the aggregation request\nrequest1 = ProductDocument.build_aggregate_request(query_expression)\nrequest2 = ProductDocument.build_aggregate_request(query_expression)\n\n# Get the number of products for each category\nProductDocument.aggregate(\n    request1.group_by(\n        [\"@category_name\"],\n        reducers.count().alias(\"count\"),\n    )\n)\n# \u003e\u003e [{\"category_name\": \"Shoes\", \"count\": \"112\"}, {\"category_name\": \"Cloths\", \"count\": \"200\"}]\n\n\n# Get the number of products for each tag\nProductDocument.aggregate(\n    request2.group_by(\n        [\"@tags_name\"],\n        reducers.count().alias(\"count\"),\n    )\n)\n# \u003e\u003e [{\"tags_name\": \"Blue\", \"count\": \"14\"}, {\"tags_name\": \"Small\", \"count\": \"57\"}]\n```\n\nFor more details checkout [redis-py docs](https://redis.readthedocs.io/en/stable/examples/search_json_examples.html?highlight=aggregate#Aggregation) and\n[RediSearch Aggregation docs](https://redis.io/docs/stack/search/reference/aggregations/)\n\n### Settings\n\n#### Environment Variables\n\n- **`REDIS_OM_URL`** (Default: `redis://localhost:6379`): This environment variable follows the `redis-py` URL format. If you are using external redis server\nYou need to set this variable with the URL of the redis server following this pattern: `redis://[[username]:[password]]@[host]:[post]/[database number]`\n\n**Example:** `redis://redis_user:password@some.other.part.cloud.redislabs.com:6379/0`\n\nFor more details checkout [redis-om docs](https://github.com/redis/redis-om-python/blob/main/docs/getting_started.md#setting-the-redis-url-environment-variable)\n\n\n#### Django Document Options\n\nYou can add these options on the `Django` class of each Document class:\n\n```python\n# documents.py\n\nfrom redis_search_django.documents import JsonDocument\n\nfrom .models import Category, Product, Tag, Vendor\n\n\nclass ProductDocument(JsonDocument):\n    class Django:\n        model = Product\n        fields = [\"name\", \"description\", \"price\", \"created_at\"]\n        select_related_fields = [\"vendor\", \"category\"]\n        prefetch_related_fields = [\"tags\"]\n        auto_index = True\n        related_models = {\n            Vendor: {\n                \"related_name\": \"product\",\n                \"many\": False,\n            },\n            Category: {\n                \"related_name\": \"product_set\",\n                \"many\": True,\n            },\n            Tag: {\n                \"related_name\": \"product_set\",\n                \"many\": True,\n            },\n        }\n```\n\n- **`model`** (Required): Django Model class to index.\n- **`auto_index`** (Default: `True`, Optional): If True, the model instances will be indexed on create/update/delete.\n- **`fields`** (Default: `[]`, Optional): List of model fields to index. (Do not add `OneToOneField`, `ForeignKey` or `ManyToManyField` here. These need to be explicitly added to the Document class using `EmbeddedJsonDocument`.)\n- **`select_related_fields`** (Default: `[]`, Optional): List of fields to use on `queryset.select_related()`.\n- **`prefetch_related_fields`** (Default: `[]`, Optional): List of fields to use on `queryset.prefetch_related()`.\n- **`related_models`** (Default: `{}`, Optional): Dictionary of related models.\n  You need to specify the fields `related_name` and if it is a `ManyToManyField` or a `ForeignKey` Field then specify `\"many\": True`.\n  These are used to update the document data if any of the related model instances are updated.\n  `related_models` will be used when a related object is saved/added/removed/deleted that contributes to the document.\n\nFor `redis-om` specific options checkout [redis-om docs](https://github.com/redis/redis-om-python/blob/main/docs/models.md)\n\n#### Global Options\n\nYou can add these options to your Django `settings.py` File:\n\n- **`REDIS_SEARCH_AUTO_INDEX`** (Default: `True`): Enable or Disable Auto Index when model instance is created/updated/deleted for all document classes.\n\n\n# Example Application Screenshot\n\n![RediSearch Django](https://user-images.githubusercontent.com/24854406/185760315-4e12d02b-68a2-499a-a6d6-88d8162b5447.png)\n\n\n# License\n\nThe code in this project is released under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaadmk11%2Fredis-search-django","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaadmk11%2Fredis-search-django","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaadmk11%2Fredis-search-django/lists"}