Package: redisearch
RediSearch Python ClientDeprecation noticeAs ofredis-py 4.0.0this library is deprecated. It's features have been merged into redis-py. Please either install itfrom pypyorthe repo.This is a Python search engine library that utilizes theRediSearch Redis ModuleAPI.It is the "official" client of RediSearch, and should be regarded as its canonical client implementation.FeaturesRediSearch is a source avaliable (RSAL), high performance search engine implemented as aRedis Module.
It uses custom data types to allow fast, stable and feature rich full-text search inside Redis.This client is a wrapper around the RediSearch API protocol, that allows you to utilize its features easily.RediSearch's features include:Full-Text indexing of multiple fields in documents.Incremental indexing without performance loss.Document ranking (provided manually by the user at index time) and field weights.Auto-complete suggestions (with fuzzy prefix suggestions).Exact Phrase Search.Stemming based query expansion inmany languages(usingSnowball).Limiting searches to specific document fields (up to 8 fields supported).Numeric filters and ranges.Automatically index existing HASH keys as documents.For more details, visithttp://redisearch.ioExamplesCreating a client instanceWhen you create a redisearch-py client instance, the only required argument
is the name of the index.fromredisearchimportClientclient=Client("my-index")To connect with a username and/or password, pass those options to the client
initializer.client=Client("my-index",password="my-password")Using core Redis commandsEvery instance ofClientcontains an instance of the redis-pyClientas
well. Use this object to run core Redis commands.importdatetimefromredisearchimportClientSTART_TIME=datetime.datetime.now().strftime("%Y-%m-%d-%H:%M.%S")client=Client("my-index")client.redis.set("start-time",START_TIME)Checking if a RediSearch index existsTo check if a RediSearch index exists, use theFT.INFOcommand and catch
theResponseErrorraised if the index does not exist.fromredisimportResponseErrorfromredisearchimportClientclient=Client("my-index")try:client.info()exceptResponseError:# Index does not exist. We need to create it!Defining a search indexUse an instance ofIndexDefinitionto define a search index. You only need
to do this when you create an index.RediSearch indexes follow Hashes in your Redis databases by watchingkey
prefixes. If a Hash whose key starts with one of the search index's
configured key prefixes is added, updated, or deleted from Redis, RediSearch
will make those changes in the index. You configure a search index's key
prefixes using theprefixparameter of theIndexDefinitioninitializer.NOTE: Once you create an index, RediSearch will continuously index these
keys when their Hashes change.IndexDefinitionalso takes aschema. The schema specifies which fields to
index from within the Hashes that the index follows. The field types are:TextFieldTagFieldNumericFieldGeoFieldFor more information on what these field types mean, consult theRediSearch
documentationon
theFT.CREATEcommand.With redisearch-py, the schema is an iterable ofFieldinstances. Once you
have anIndexDefinitioninstance, you can create the instance by passing a
schema iterable to thecreate_index()method.fromredisimportResponseErrorfromredisearchimportClient,IndexDefinition,TextFieldSCHEMA=(TextField("title",weight=5.0),TextField("body"))client=Client("my-index")definition=IndexDefinition(prefix=['blog:'])try:client.info()exceptResponseError:# Index does not exist. We need to create it!client.create_index(SCHEMA,definition=definition)Indexing a documentA RediSearch 2.0 index continually follows Hashes with the key prefixes you
defined, so if you want to add a document to the index, you only need to
create a Hash with one of those prefixes.# Indexing a document with RediSearch 2.0.doc={'title':'RediSearch','body':'Redisearch adds querying, indexing, and full-text search to Redis'}client.redis.hset('doc:1',mapping=doc)Past versions of RediSearch required that you call theadd_document()method. This method is deprecated, but we include its usage here for
reference.# Indexing a document for RediSearch 1.xclient.add_document("doc:2",title="RediSearch",body="Redisearch implements a search engine on top of redis",)QueryingBasic queriesUse thesearch()method to perform basic full-text and field-specific
searches. This method doesn't take many of the options available to the
RediSearchFT.SEARCHcommand -- read the section on building complex
queries later in this document for information on how to use those.res=client.search("evil wizards")Result objectsResults are wrapped in aResultobject that includes the number of results
and a list of matching documents.>>>print(res.total)2>>>print(res.docs[0].title)"Wizard Story 2: Evil Wizards Strike Back"Building complex queriesYou can use theQueryobject to build complex queries:q=Query("evil wizards").verbatim().no_content().with_scores().paging(0,5)res=client.search(q)For an explanation of these options, see theRediSearch
documentationfor
theFT.SEARCHcommand.Query syntaxThe default behavior of queries is to run a full-text search across allTEXTfields in the index for the intersection of all terms in the query.So the example given in the "Basic queries" section of this README,client.search("evil wizards"), run a full-text search for the intersection
of "evil" and "wizard" in allTEXTfields.Many more types of queries are possible, however! The string you pass into
thesearch()method orQuery()initializer has the full range of query
syntax available in RediSearch.For example, a full-text search against a specificTEXTfield in the index
looks like this:# Full-text searchres=client.search("@title:evil wizards")Finding books published in 2020 or 2021 looks like this:client.search("@published_year:[2020 2021]")To learn more, see theRediSearch
documentationon query
syntax.AggregationsThis library contains a programmatic interface to runaggregation
querieswith RediSearch.Making an aggregation queryTo make an aggregation query, pass an instance of theAggregateRequestclass to thesearch()method of an instance ofClient.For example, here is what finding the most books published in a single year
looks like:fromredisearchimportClientfromredisearchimportreducersfromredisearch.aggregationimportAggregateRequestclient=Client('books-idx')request=AggregateRequest('*').group_by('@published_year',reducers.count().alias("num_published")).group_by([],reducers.max("@num_published").alias("max_books_published_per_year"))result=client.aggregate(request)A redis-cli equivalent queryThe aggregation query just given is equivalent to the followingFT.AGGREGATEcommand entered directly into the redis-cli:FT.AGGREGATEbooks-idx*GROUPBY1@published_yearREDUCECOUNT0ASnum_publishedGROUPBY0REDUCEMAX1@num_publishedASmax_books_published_per_yearThe AggregateResult objectAggregation queries return anAggregateResultobject that contains the rows
returned for the query and a cursor if you're using thecursor
API.fromredisearch.aggregationimportAggregateRequest,Ascrequest=AggregateRequest('*').group_by(['@published_year'],reducers.avg('average_rating').alias('average_rating_for_year')).sort_by(Asc('@average_rating_for_year')).limit(0,10).filter('@published_year > 0')...In[53]:resp=c.aggregate(request)In[54]:resp.rowsOut[54]:[['published_year','1914','average_rating_for_year','0'],['published_year','2009','average_rating_for_year','1.39166666667'],['published_year','2011','average_rating_for_year','2.046'],['published_year','2010','average_rating_for_year','3.125'],['published_year','2012','average_rating_for_year','3.41'],['published_year','1967','average_rating_for_year','3.603'],['published_year','1970','average_rating_for_year','3.71875'],['published_year','1966','average_rating_for_year','3.72666666667'],['published_year','1927','average_rating_for_year','3.77']]Reducer functionsNotice from the example that we used an object from thereducersmodule.
See theRediSearch documentationfor more examples of reducer functions you can use when grouping results.Reducer functions include analias()method that gives the result of the
reducer a specific name. If you don't supply a name, RediSearch will generate
one.Grouping by zero, one, or multiple fieldsThegroup_bystatement can take a single field name as a string, or multiple
field names as a list of strings.AggregateRequest('*').group_by('@published_year',reducers.count())AggregateRequest('*').group_by(['@published_year','@average_rating'],reducers.count())To run a reducer function on every result from an aggregation query, pass an
empty list togroup_by(), which is equivalent to passing the optionGROUPBY 0when writing an aggregation in the redis-cli.AggregateRequest('*').group_by([],reducers.max("@num_published"))NOTE: Aggregation queries require at least onegroup_by()method call.Sorting and limitingUsing anAggregateRequestinstance, you can sort with thesort_by()method
and limit with thelimit()method.For example, finding the average rating of books published each year, sorting
by the average rating for the year, and returning only the first ten results:fromredisearchimportClientfromredisearch.aggregationimportAggregateRequest,Ascc=Client()request=AggregateRequest('*').group_by(['@published_year'],reducers.avg('average_rating').alias('average_rating_for_year')).sort_by(Asc('@average_rating_for_year')).limit(0,10)c.aggregate(request)NOTE: The first option tolimit()is a zero-based offset, and the second
option is the number of results to return.FilteringUse filtering to reject results of an aggregation query after your reducer
functions run. For example, calculating the average rating of books published
each year and only returning years with an average rating higher than 3:fromredisearch.aggregationimportAggregateRequest,Ascreq=AggregateRequest('*').group_by(['@published_year'],reducers.avg('average_rating').alias('average_rating_for_year')).sort_by(Asc('@average_rating_for_year')).filter('@average_rating_for_year > 3')InstallingInstall RediSearchInstall the Python client:$pipinstallredisearchDevelopingCreate a virtualenv to manage your python dependencies, and ensure it's active.virtualenv -v venvInstallpypoetryto manage your dependencies.pip install --user poetryInstall dependencies.poetry installNote: Due to aninteraction betweenand python 3.10, youmayneed to run the following, if you receive a JSONError while installing packages.poetry config experimental.new-installer falseTestingTesting can easily be performed using using Docker.
Run the following:make -C test/docker test PYTHON_VER=3(ReplacePYTHON_VER=3withPYTHON_VER=2to test with Python 2.7.)Alternatively, use the following procedure:First, run:PYTHON_VER=3 ./test/test-setup.shThis will set up a Python virtual environment invenv3(or invenv2ifPYTHON_VER=2is used).Afterwards, run RediSearch in a container as a daemon:docker run -d -p 6379:6379 redislabs/redisearch:2.0.0Finally, invoke the virtual environment and run the tests:. ./venv3/bin/activate
REDIS_PORT=6379 python test/test.py
REDIS_PORT=6379 python test/test_builder.py