redisredis-pyredisearch

Redisearch-Py search Redis JSON schema for entries belonging to a specific tag


We are using Redisearch to insert structured JSON into Redis and searching by content to power autocomplete.

This is how our code looks like in Python:

rs = r.ft("a:a")

schema = (
TextField("$.key", as_name="key"),  
 NumericField("$.p", as_name="p"),  
 )


rs.create_index(
schema,  
 definition=IndexDefinition( 
  prefix=["a:"], index_type=IndexType.JSON 
   )
    )

With this structure, we are able to insert keys and retrieve by values, like this:

testDic = "{'key': 'glooler-61245', 'val': 'sighgh', 'dom': 'test'}"
r.json().set("a:glooler-erree61245", Path.root_path(), ast.literal_eval(testDic))

prodDic = "{'key': 'booler-erree61245', 'val': 'brouhaha', 'dom': 'prod'}"
r.json().set("a:booler-erree61245", Path.root_path(), ast.literal_eval(prodDic))

rs.search(Query(f"glooler")) # returns only first entry

This works fine, as expected. But our requirement is that when we are in prod environment, our search should work only on the keys belonging to prod domain (the second key above). How do we specify that in our search query, or schema, so that the search doesn't even look at the data belonging to other domains? There is something like tag in Redis, unable to find out how to use it in our scenario, so a little help would be appreciated.


Solution

  • You are on to something using tags for search isolation.

    First, set up a TagField for the dom field in the schema.

    import redis
    from redis.commands.json.path import Path
    from redis.commands.search.indexDefinition import IndexDefinition, IndexType
    from redis.commands.search.field import NumericField, TagField, TextField
    from redis.commands.search.query import Query
    
    conn = redis.Redis(host="localhost", port=6379, db=0)
    
    search = conn.ft("a:a")
    
    schema = (
        TextField("$.key", as_name="key"),
        NumericField("$.p", as_name="p"),
        TagField("$.dom", as_name="dom"),
    )
    
    search.dropindex(delete_documents=False)
    index = search.create_index(
        schema, definition=IndexDefinition(prefix=["a:"], index_type=IndexType.JSON)
    )
    
    testDic = {"key": "glooler-61245", "val": "sighgh", "dom": "test"}
    conn.json().set("a:glooler-erree61245", Path.root_path(), testDic)
    
    prodDict = {"key": "booler-erree61245", "val": "brouhaha", "dom": "prod"}
    conn.json().set("a:booler-erree61245", Path.root_path(), prodDict)
    

    Then tack on the tag in the query.

    The following example looks up objects with dom tag value of test and matching *ool*

    result = search.search(Query("*ool* @dom:{test}"))  # returns only objects with test tag
    print(result)
    # Result{1 total, docs: [Document {'id': 'a:glooler-erree61245', 'payload': None, 'json': '{"key":"glooler-61245","val":"sighgh","dom":"test"}'}]}
    

    The following example looks up objects with dom tag value of prod and matching *ool*

    result = search.search(Query("*ool* @dom:{prod}"))  # returns only objects with the prod tag
    print(result)
    # Result{1 total, docs: [Document {'id': 'a:booler-erree61245', 'payload': None, 'json': '{"key":"booler-erree61245","val":"brouhaha","dom":"prod"}'}]}
    

    In order to sort the results by a priority field in the document, configure in the index the field.

    import redis
    from redis.commands.json.path import Path
    from redis.commands.search.indexDefinition import IndexDefinition, IndexType
    from redis.commands.search.field import NumericField, TagField, TextField
    from redis.commands.search.query import Query
    
    conn = redis.Redis(host="localhost", port=6379, db=0)
    
    search = conn.ft("a:a")
    
    schema = (
        TextField("$.key", as_name="key"),
        NumericField("$.p", as_name="p"),
        TagField("$.dom", as_name="dom"),
        NumericField("$.priority", as_name="priority", sortable=True)
    )
    
    search.dropindex(delete_documents=False)
    index = search.create_index(
        schema, definition=IndexDefinition(prefix=["a:"], index_type=IndexType.JSON)
    )
    
    prodDict1 = {"key": "booler-erree61245", "val": "brouhaha", "dom": "prod", "priority": 1}
    prodDict2 = {"key": "booler-erree61246", "val": "brouhaha2", "dom": "prod", "priority": 2}
    
    conn.json().set("a:booler-erree61245", Path.root_path(), prodDict1)
    conn.json().set("a:booler-erree61246", Path.root_path(), prodDict2)
    

    Then configure include sort_by parameter in the search request as follows:

    result = search.search(Query("*ool* @dom:{prod}").sort_by("priority", asc=True))  # returns only prod documents ordered by priority in ascending order.
    print(result)