4\\x13$=Ӽ\\x1c-F!ﵼ\\x01Hf{\\ue17d=\\x12TB<\\x064Ἥ\\x13:\\x11\\x1b_:2;W\\x0c=Xx;m=\\x02= <+*x=-\"P=:\\\\=k\\x0c=\\x12L42\\x15\\x17!q>s=|;\\x14\\x1eKj;>v\\x1e=!=d&=s<<\\x16<4\"\\x16\"u\\x0e>\\x0b\\x05Ɍ.kY-pض\\x12\\x19<[*\\x13=\\x1ej\\t\\x7f=]p<.\\x1f=0f~;%\\n_=?;\\x1eC\\x19W\\x04=FF<_s<;=B=վȤ\\x14r\\x00\\x0c0<\\n\\x0b^\\x1f<^\\x16=#\\x0ca.t=s\\x19P$\\'%\\x19\\x05===\\x7fWE}A\\r\\r*<`\\x05=TF= ^=\\x0c0=FA;\\x17G\\x01%\\x05U%=\\x0ck5\\x08Hżb[KNN 3 @vector $vec_param AS dist]\").sort_by(\"dist\").dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)\n"
]
},
{
"cell_type": "markdown",
"id": "ef5e1997",
"metadata": {},
"source": [
"### Hybrid filter vector search\n",
"\n",
"Redis allows you to combine filter searches on fields within the index object allowing us to create more specific searches."
]
},
{
"cell_type": "code",
"execution_count": 52,
"id": "d499dcad",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 3 movies: [('Fast & Furious 9', 'action', '6'), ('Mad Max: Fury Road', 'action', '8'), ('Explosive Pursuit', 'action', '7')]\n"
]
}
],
"source": [
"# Search for top 3 movies specifically in the action genre\n",
"\n",
"user_query = \"High tech movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"# Note: genre is a tag field in our schema so the syntax is @:{ | | ...}\n",
"query = Query(\"(@genre:{action})=>[KNN 3 @vector $vec_param AS dist]\").sort_by('dist').dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)"
]
},
{
"cell_type": "code",
"execution_count": 53,
"id": "f59fff2c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 3 movies: [('Mad Max: Fury Road', 'action', '8'), ('Explosive Pursuit', 'action', '7'), ('The Avengers', 'action', '8')]\n"
]
}
],
"source": [
"# Search for top 3 movies specifically in the action genre with ratings at or above a 7\n",
"\n",
"user_query = \"High tech movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"query = Query(\"(@genre:{action} & (@rating:[7 inf]))=>[KNN 3 @vector $vec_param AS dist]\").sort_by('dist').dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)"
]
},
{
"cell_type": "code",
"execution_count": 54,
"id": "8a493ae0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 2 movies: [('Despicable Me', 'comedy', '7'), ('The Dark Knight', 'action', '9')]\n"
]
}
],
"source": [
"# Search with full text search for movies that directly mention \"criminal mastermind\" in the description\n",
"\n",
"user_query = \"High tech movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"query = Query(\"(@description:(criminal mastermind))=>[KNN 3 @vector $vec_param AS dist]\").sort_by('dist').dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)"
]
},
{
"cell_type": "code",
"execution_count": 55,
"id": "c0d7ab60",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 3 movies: [('Despicable Me', 'comedy', '7'), ('The Incredibles', 'comedy', '8'), ('Explosive Pursuit', 'action', '7')]\n"
]
}
],
"source": [
"# Vector search with wild card match\n",
"\n",
"user_query = \"High tech movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"query = Query(\"(@description:(crim*))=>[KNN 3 @vector $vec_param AS dist]\").sort_by('dist').dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)"
]
},
{
"cell_type": "code",
"execution_count": 56,
"id": "748c15ae",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 3 movies: [('The Avengers', 'action', '8'), ('Black Widow', 'action', '7'), ('The Princess Diaries', 'comedy', '6')]\n"
]
}
],
"source": [
"# Vector search with fuzzy match\n",
"\n",
"user_query = \"High tech movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"# Note: fuzzy match is based on Levenshtein distance. Therefore, \"hero\" might return result for \"her\" as an example.\n",
"# See docs for more info https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/query_syntax/\n",
"query = Query(\"(@description:%hero%)=>[KNN 3 @vector $vec_param AS dist]\").sort_by('dist').dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query, query_params = {'vec_param': embedded_user_query})\n",
"\n",
"print_results(res)"
]
},
{
"cell_type": "markdown",
"id": "6bd27cb3",
"metadata": {},
"source": [
"## Range queries\n",
"\n",
"Range queries allow you to set a pre defined \"threshold\" for which we want to return documents. This is helpful when you only want documents with a certain distance from the search query."
]
},
{
"cell_type": "code",
"execution_count": 57,
"id": "cafe1795",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 6 movies: [('The Incredibles', 'comedy', '8'), ('Black Widow', 'action', '7'), ('Despicable Me', 'comedy', '7'), ('Shrek', 'comedy', '8'), ('Monsters, Inc.', 'comedy', '8'), ('Aladdin', 'comedy', '8')]\n"
]
}
],
"source": [
"user_query = \"Family friendly fantasy movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"query = (\n",
" Query(\"@vector:[VECTOR_RANGE $radius $vector]=>{$YIELD_DISTANCE_AS: vector_distance}\")\n",
" .sort_by(\"vector_distance\")\n",
" .return_fields(\"title\", \"rating\", \"genre\", \"vector_distance\")\n",
" .dialect(2)\n",
")\n",
"\n",
"# Find all vectors within 0.8 of the query vector\n",
"query_params = {\n",
" \"radius\": 0.8,\n",
" \"vector\": embedded_user_query\n",
"}\n",
"\n",
"res = client.ft(index_name).search(query, query_params)\n",
"print_results(res)\n"
]
},
{
"cell_type": "markdown",
"id": "a1586ea7",
"metadata": {},
"source": [
"Like the queries above, we can also chain additional filters and conditional operators with range queries. The following adds an `or` condition that returns vector search within the defined range or with a rating at or above 9."
]
},
{
"cell_type": "code",
"execution_count": 58,
"id": "d3110324",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Top 3 movies: [('The Incredibles', 'comedy', '8'), ('The Dark Knight', 'action', '9'), ('Inception', 'action', '9')]\n"
]
}
],
"source": [
"user_query = \"Family friendly fantasy movies\"\n",
"\n",
"embedded_user_query = embed_text(model, user_query)\n",
"\n",
"query = (\n",
" Query(\"@rating:[9 +inf] | @vector:[VECTOR_RANGE $radius $vector]=>{$YIELD_DISTANCE_AS: vector_distance}\")\n",
" .sort_by(\"vector_distance\")\n",
" .return_fields(\"title\", \"rating\", \"genre\", \"vector_distance\")\n",
" .dialect(2)\n",
")\n",
"\n",
"# Find all vectors within 0.8 of the query vector\n",
"query_params = {\n",
" \"radius\": 0.7,\n",
" \"vector\": embedded_user_query\n",
"}\n",
"\n",
"res = client.ft(index_name).search(query, query_params)\n",
"print_results(res)"
]
},
{
"cell_type": "markdown",
"id": "6e435ce5",
"metadata": {},
"source": [
"### Additional queries\n",
"\n",
"In addition to the variety of vector queries shown above redis supports full-text search, aggregations, and various weighting strategies that can be mixed and matched for a wide range of search applications.\n",
"\n",
"### Full text search with BM25\n",
"\n",
"The following query does a pure token based BM25 search with redis."
]
},
{
"cell_type": "code",
"execution_count": 59,
"id": "3307ab80",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Document {'id': '6', 'payload': None, 'score': 4.743066248010575, 'title': 'The Dark Knight', 'genre': 'action', 'rating': '9', 'description': 'Batman faces off against the Joker, a criminal mastermind who threatens to plunge Gotham into chaos.'},\n",
" Document {'id': '17', 'payload': None, 'score': 4.560171658735046, 'title': 'Despicable Me', 'genre': 'comedy', 'rating': '7', 'description': 'When a criminal mastermind uses a trio of orphan girls as pawns for a grand scheme, he finds their love is profoundly changing him for the better.'},\n",
" Document {'id': '0', 'payload': None, 'score': 2.8628170632759318, 'title': 'Explosive Pursuit', 'genre': 'action', 'rating': '7', 'description': 'A daring cop chases a notorious criminal across the city in a high-stakes game of cat and mouse.'}]"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"input = \"Criminal mastermind\"\n",
"\n",
"# Redis breaks searches into key tokens\n",
"def tokenize(query):\n",
" return \" | \".join(query.split(\" \")).lower()\n",
"\n",
"user_query = Query(tokenize(input))\\\n",
" .scorer(\"BM25STD\") \\\n",
" .with_scores() \\\n",
" .return_fields(\"title\", \"genre\", \"rating\", \"description\") \\\n",
" .paging(0, 10) # limits the amount of results to 10\n",
"\n",
"res = client.ft(index_name).search(user_query)\n",
"res.docs"
]
},
{
"cell_type": "markdown",
"id": "1a70df15",
"metadata": {},
"source": [
"# Aggregations\n",
"\n",
"Redis aggregate queries allow you to group, filter, and compute metrics (like counts, sums, and averages) over indexed data stored in Redis. For instance, the following returns the average rating per genre."
]
},
{
"cell_type": "code",
"execution_count": 60,
"id": "efb45202",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[b'genre', b'action', b'avg_rating', b'7.8'],\n",
" [b'genre', b'comedy', b'avg_rating', b'7.5']]"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from redis.commands.search.aggregation import AggregateRequest\n",
"import redis.commands.search.reducers as reducers\n",
"\n",
"req = (\n",
" AggregateRequest(\"*\")\n",
" .group_by([\"@genre\"], reducers.avg(\"rating\").alias(\"avg_rating\"))\n",
" .dialect(2)\n",
" )\n",
"\n",
"res = client.ft(index_name).aggregate(req)\n",
"res.rows"
]
},
{
"cell_type": "markdown",
"id": "ef001bef",
"metadata": {},
"source": [
"### Weighting (boosting)\n",
"\n",
"Sometimes you might want a search to lean more heavily towards one condition over another and weight it higher in the result set.\n",
"\n",
"In this example, you can see that even though `The Incredibles` isn't an `action` movie it is still the top result because it ranks highly on the fuzzy search for `%superhero%`."
]
},
{
"cell_type": "code",
"execution_count": 61,
"id": "f11af548",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Document {'id': '15', 'payload': None, 'title': 'The Incredibles', 'genre': 'comedy', 'rating': '8', 'description': \"A family of undercover superheroes, while trying to live the quiet suburban life, are forced into action to save the world. Bob Parr (Mr. Incredible) and his wife Helen (Elastigirl) were among the world's greatest crime fighters, but now they must assume civilian identities and retreat to the suburbs to live a 'normal' life with their three children. However, the family's desire to help the world pulls them back into action when they face a new and dangerous enemy.\"},\n",
" Document {'id': '0', 'payload': None, 'title': 'Explosive Pursuit', 'genre': 'action', 'rating': '7', 'description': 'A daring cop chases a notorious criminal across the city in a high-stakes game of cat and mouse.'},\n",
" Document {'id': '1', 'payload': None, 'title': 'Skyfall', 'genre': 'action', 'rating': '8', 'description': 'James Bond returns to track down a dangerous new enemy who threatens global security.'}]"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"query = Query('((@genre:{action}=>{$weight: 1}) | (@description:(%superhero%)=>{$weight: 10}))') \\\n",
" .return_fields(\"title\", \"genre\", \"rating\", \"description\") \\\n",
" .paging(0, 3) \\\n",
" .dialect(2)\n",
"\n",
"res = client.ft(index_name).search(query)\n",
"res.docs"
]
},
{
"cell_type": "code",
"execution_count": 62,
"id": "1902b43b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# clean up!\n",
"client.flushall()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}