Package: redis-search-django
redis-search-djangoAboutA Django package that providesauto indexingandsearchingcapabilities for Django model instances usingRediSearch.FeaturesManagement Command to create, update and populate the RediSearch Index.Auto Index on Model object Create, Update and Delete.Auto Index on Related Model object Add, Update, Remove and Delete.Easy to create Document classes (Uses Django Model Form Class like structure).Index nested models (e.g:OneToOneField,ForeignKeyandManyToManyField).Search documents usingredis-om.Search Result Pagination.Search Result Sorting.RediSearch Result to Django QuerySet.Faceted Search.RequirementsPython: 3.7, 3.8, 3.9, 3.10Django: 3.2, 4.0, 4.1redis-om: >= 0.0.27RedisDownloading RedisThe latest version of Redis is available fromRedis.io. You can also install Redis with your operating system's package manager.RediSearch and RedisJSONredis-search-djangorelies on theRediSearchandRedisJSONRedis modules to support rich queries and embedded models.
You need these Redis modules to useredis-search-django.The easiest way to run these Redis modules during local development is to use theredis-stackDocker image.Docker ComposeThere is adocker-compose.yamlfile provided in the project's root directory.
This file will run Redis with RedisJSON and RediSearch modules during development.Run the following command to start the Redis container:dockercomposeup-dExample ProjectThere is an example project available atExample Project.DocumentationInstallationpipinstallredis-search-djangoThen addredis_search_djangoto yourINSTALLED_APPS:INSTALLED_APPS=[...'redis_search_django',]UsageDocument TypesThere are3 typesof documents class available:JsonDocument:This usesRedisJSONto store the document. If you want to use Embedded Documents (Required ForOneToOneField,ForeignKeyandManyToManyField) then useJsonDocument.EmbeddedJsonDocument:If the document will be embedded inside another document class then use this. Embedded Json Documents are used forOneToOneField,ForeignKeyandManyToManyFieldor any types of nested documents.HashDocument:This usesRedisHashto store the documents. It can not be used for nested documents.Creating Document ClassesYou need to inherit from The Base Document Classes mentioned above to build a document class.Simple Example1. For Django Model:# models.pyfromdjango.dbimportmodelsclassCategory(models.Model):name=models.CharField(max_length=30)slug=models.SlugField(max_length=30)def__str__(self)->str:returnself.name2. You can create a document class like this:Note:Document classes must be stored indocuments.pyfile.# documents.pyfromredis_search_django.documentsimportJsonDocumentfrom.modelsimportCategoryclassCategoryDocument(JsonDocument):classDjango:model=Categoryfields=["name","slug"]3. Run Index Django Management Command to create the index on Redis:pythonmanage.pyindexNote:This will also populate the index with existing data from the databaseNow category objects will be indexed on create/update/delete.More Complex Example1. For Django Models:# models.pyfromdjango.dbimportmodelsclassTag(models.Model):name=models.CharField(max_length=30)def__str__(self)->str:returnself.nameclassVendor(models.Model):name=models.CharField(max_length=30)email=models.EmailField()establishment_date=models.DateField()def__str__(self)->str:returnself.nameclassProduct(models.Model):name=models.CharField(max_length=256)description=models.TextField(blank=True)vendor=models.OneToOneField(Vendor,on_delete=models.CASCADE)tags=models.ManyToManyField(Tag,blank=True)price=models.DecimalField(max_digits=6,decimal_places=2)def__str__(self)->str:returnself.name2. You can create a document classes like this:Note:Document classes must be stored indocuments.pyfile.# documents.pyfromtypingimportListfromdjango.dbimportmodelsfromredis_omimportFieldfromredis_search_django.documentsimportEmbeddedJsonDocument,JsonDocumentfrom.modelsimportProduct,Tag,VendorclassTagDocument(EmbeddedJsonDocument):custom_field:str=Field(index=True,full_text_search=True)classDjango:model=Tag# Model Fieldsfields=["name"]@classmethoddefprepare_custom_field(cls,obj):return"CUSTOM FIELD VALUE"classVendorDocument(EmbeddedJsonDocument):classDjango:model=Vendor# Model Fieldsfields=["name","establishment_date"]classProductDocument(JsonDocument):# OnetoOneField, with null=Falsevendor:VendorDocument# ManyToManyFieldtags:List[TagDocument]classDjango:model=Product# Model Fieldsfields=["name","description","price"]# Related Model Optionsrelated_models={Vendor:{"related_name":"product","many":False,},Tag:{"related_name":"product_set","many":True,},}@classmethoddefget_queryset(cls)->models.QuerySet:"""Override Queryset to filter out available products."""returnsuper().get_queryset().filter(available=True)@classmethoddefprepare_name(cls,obj):"""Use this to update field value."""returnobj.name.upper()Note:You can not inherit fromHashDocumentfor documents that include nested fields.You need to inherit fromEmbeddedJsonDocumentfor document classes that will be embedded inside another document class.You need to explicitly addOneToOneField,ForeignKeyorManyToManyField(e.g:tags: List[TagDocument]) with an embedded document class if you want to index them.
you can not add it in theDjango.fieldsoption.Forrelated_modelsoption, you need to specify the fieldsrelated_nameand if it is aManyToManyFieldor aForeignKeyField then specify"many": True.related_modelswill be used when a related object is saved that contributes to the document.You can defineprepare_{field_name}method to update the value of a field before indexing.If it is a custom field (not a model field) you must define aprepare_{field_name}method that returns the value of the field.You can overrideget_querysetmethod to provide more filtering. This will be used while indexing a queryset.Field names must match model field names or define aprepare_{field_name}method.3. Run Index Django Management Command to create the index on Redis:pythonmanage.pyindexNote:This will also populate the index with existing data from the databaseManagement CommandThis package comes withindexmanagement command that can be used to index all the model instances to Redis index if it has a Document class defined.Note:Make sure that Redis is running before running the command.Run the following command to indexallmodels that have Document classes defined:pythonmanage.pyindexYou can use--migrate-onlyoption toonlyupdate theindex schema.pythonmanage.pyindex--migrate-onlyYou can use--modelstospecifywhich models to index (models must have a Document class defined to be indexed).pythonmanage.pyindex--modelsapp_name.ModelNameapp_name2.ModelName2ViewsYou can use theredis_search_django.mixin.RediSearchListViewMixinwith a Django Generic View to search for documents.RediSearchPaginatorwhich helps paginateReadiSearchresults is also added to this mixin.Example# views.pyfromdjango.utils.functionalimportcached_propertyfromdjango.views.genericimportListViewfromredis.commands.searchimportreducersfromredis_search_django.mixinsimportRediSearchListViewMixinfrom.documentsimportProductDocumentfrom.modelsimportProductclassSearchView(RediSearchListViewMixin,ListView):paginate_by=20model=Producttemplate_name="core/search.html"document_class=ProductDocument@cached_propertydefsearch_query_expression(self):query=self.request.GET.get("query")query_expression=Noneifquery:query_expression=(self.document_class.name%query|self.document_class.description%query)returnquery_expression@cached_propertydefsort_by(self):returnself.request.GET.get("sort")deffacets(self):ifself.search_query_expression:request=self.document_class.build_aggregate_request(self.search_query_expression)else:request=self.document_class.build_aggregate_request()result=self.document_class.aggregate(request.group_by(["@tags_name"],reducers.count().alias("count"),))returnresultSearchThis package usesredis-omto search for documents.Examplefrom.documentsimportProductDocumentcategories=["category1","category2"]tags=["tag1","tag2"]# Search For Products That Match The Search Query (name or description)query_expression=(ProductDocument.name%"Some search query"|ProductDocument.description%"Some search query")# Search For Products That Match The Price Rangequery_expression=(ProductDocument.price>=float(10)&ProductDocument.price<=float(100))# Search for Products that include following Categoriesquery_expression=ProductDocument.category.name<<["category1","category2"]# Search for Products that include following Tagsquery_expression=ProductDocument.tags.name<<["tag1","tag2"]# Query expression can be passed on the `find` methodresult=ProductDocument.find(query_expression).sort_by("-price").execute()For more details checkoutredis-om docsRediSearch Aggregation / Faceted Searchredis-omdoes not support faceted search (RediSearch Aggregation). So this package usesredis-pyto do faceted search.Examplefromredis.commands.searchimportreducersfrom.documentsimportProductDocumentquery_expression=(ProductDocument.name%"Some search query"|ProductDocument.description%"Some search query")# First we need to build the aggregation requestrequest1=ProductDocument.build_aggregate_request(query_expression)request2=ProductDocument.build_aggregate_request(query_expression)# Get the number of products for each categoryProductDocument.aggregate(request1.group_by(["@category_name"],reducers.count().alias("count"),))# >> [{"category_name": "Shoes", "count": "112"}, {"category_name": "Cloths", "count": "200"}]# Get the number of products for each tagProductDocument.aggregate(request2.group_by(["@tags_name"],reducers.count().alias("count"),))# >> [{"tags_name": "Blue", "count": "14"}, {"tags_name": "Small", "count": "57"}]For more details checkoutredis-py docsandRediSearch Aggregation docsSettingsEnvironment VariablesREDIS_OM_URL(Default:redis://localhost:6379): This environment variable follows theredis-pyURL format. If you are using external redis server
You need to set this variable with the URL of the redis server following this pattern:redis://[[username]:[password]]@[host]:[post]/[database number]Example:redis://redis_user:password@some.other.part.cloud.redislabs.com:6379/0For more details checkoutredis-om docsDjango Document OptionsYou can add these options on theDjangoclass of each Document class:# documents.pyfromredis_search_django.documentsimportJsonDocumentfrom.modelsimportCategory,Product,Tag,VendorclassProductDocument(JsonDocument):classDjango:model=Productfields=["name","description","price","created_at"]select_related_fields=["vendor","category"]prefetch_related_fields=["tags"]auto_index=Truerelated_models={Vendor:{"related_name":"product","many":False,},Category:{"related_name":"product_set","many":True,},Tag:{"related_name":"product_set","many":True,},}model(Required): Django Model class to index.auto_index(Default:True, Optional): If True, the model instances will be indexed on create/update/delete.fields(Default:[], Optional): List of model fields to index. (Do not addOneToOneField,ForeignKeyorManyToManyFieldhere. These need to be explicitly added to the Document class usingEmbeddedJsonDocument.)select_related_fields(Default:[], Optional): List of fields to use onqueryset.select_related().prefetch_related_fields(Default:[], Optional): List of fields to use onqueryset.prefetch_related().related_models(Default:{}, Optional): Dictionary of related models.
You need to specify the fieldsrelated_nameand if it is aManyToManyFieldor aForeignKeyField then specify"many": True.
These are used to update the document data if any of the related model instances are updated.related_modelswill be used when a related object is saved/added/removed/deleted that contributes to the document.Forredis-omspecific options checkoutredis-om docsGlobal OptionsYou can add these options to your Djangosettings.pyFile:REDIS_SEARCH_AUTO_INDEX(Default:True): Enable or Disable Auto Index when model instance is created/updated/deleted for all document classes.Example Application ScreenshotLicenseThe code in this project is released under theMIT License.