Conversation
There was a problem hiding this comment.
Pull request overview
This pull request introduces two major features to the redisvl library: SQL query support and multi-prefix index capability. The PR bumps the version from 0.13.2 to 0.14.0, reflecting the addition of significant new functionality.
Changes:
- Adds SQLQuery class to translate SQL SELECT statements into Redis FT.SEARCH/FT.AGGREGATE commands via the sql-redis library
- Implements multi-prefix index support allowing a single index to monitor multiple key prefixes
- Adds comprehensive test coverage for both new features
Reviewed changes
Copilot reviewed 15 out of 17 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Version bump to 0.14.0, adds sql-redis optional dependency |
| uv.lock | Updates lock file with sql-redis and sqlglot dependencies |
| redisvl/query/sql.py | New SQLQuery class with parameter substitution and Redis command translation |
| redisvl/query/init.py | Exports SQLQuery from the query module |
| redisvl/index/index.py | Adds multi-prefix support and _sql_query method for executing SQL queries |
| tests/unit/test_sql_parameter_substitution.py | Unit tests for SQL parameter substitution edge cases |
| tests/integration/test_sql_redis_json.py | Integration tests for SQL queries against JSON storage |
| tests/integration/test_sql_redis_hash.py | Integration tests for SQL queries against Hash storage |
| tests/integration/test_multi_prefix.py | Integration tests verifying multi-prefix index functionality |
| examples/multi_prefix_example.py | Example demonstrating multi-prefix index usage |
| docs/api/query.rst | API documentation for SQLQuery |
| docs/user_guide/index.md | Adds reference to new SQL query guide |
| docs/user_guide/01_getting_started.ipynb | Updates prefix configuration example |
| docs/user_guide/02_hybrid_queries.ipynb | Re-executed notebook with updated outputs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import pytest | ||
|
|
||
| from redisvl.query.sql import SQLQuery | ||
|
|
||
|
|
There was a problem hiding this comment.
Import of 'pytest' is not used.
| import pytest | |
| from redisvl.query.sql import SQLQuery | |
| from redisvl.query.sql import SQLQuery |
|
@rbs333 Looks like this has leaked commits from the SQL work. Can you pare it back to the multi-prefix additions? |
vishal-bala
left a comment
There was a problem hiding this comment.
No major comments from me aside a question about whether building the registry/executor for SQL translation is expensive. There are a few Copilot comments that I agreed should be addressed (left a 👍 on them) - aside from those, it generally looks good! Super nice to be able to have SQL queries translated 🙌
ff5fedd to
80a61ef
Compare
🛡️ Jit Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1c705ea to
bb6104c
Compare
bb6104c to
6bdacb6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
pyproject.toml:72
- The
allextra still pinssql-redis>=0.1.1while the dedicatedsql-redisextra was bumped to>=0.1.2. This makespip install redisvl[all]potentially pull an older version than intended. Update theallextra to match (or intentionally justify why it should differ).
sql-redis = [
"sql-redis>=0.1.2",
]
all = [
"mistralai>=1.0.0",
"openai>=1.1.0",
"nltk>=3.8.1,<4",
"cohere>=4.44",
"voyageai>=0.2.2",
"sentence-transformers>=3.4.0,<4",
"langcache>=0.11.0",
"google-cloud-aiplatform>=1.26,<2.0.0",
"protobuf>=5.28.0,<6.0.0",
"boto3>=1.36.0,<2",
"urllib3<2.2.0",
"pillow>=11.3.0",
"sql-redis>=0.1.1",
]
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { | ||
| "cells": [ | ||
| { | ||
| "attachments": {}, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Write SQL Queries for Redis\n", | ||
| "\n", | ||
| "While Redis does not natively support SQL, RedisVL provides a `SQLQuery` class that translates SQL-like queries into Redis queries.\n", | ||
| "\n", | ||
| "The `SQLQuery` class wraps the [`sql-redis`](https://pypi.org/project/sql-redis/) package. This package is not installed by default, so install it with:\n", | ||
| "\n", | ||
| "```bash\n", | ||
| "pip install redisvl[sql-redis]\n", | ||
| "```\n", | ||
| "\n", | ||
| "## Prerequisites\n", | ||
| "\n", | ||
| "Before you begin, ensure you have:\n", | ||
| "- Installed RedisVL with SQL support: `pip install redisvl[sql-redis]`\n", | ||
| "- A running Redis instance ([Redis 8+](https://redis.io/downloads/) or [Redis Cloud](https://redis.io/cloud))\n", | ||
| "\n", | ||
| "## What You'll Learn\n", | ||
| "\n", | ||
| "By the end of this guide, you will be able to:\n", | ||
| "- Write SQL-like queries for Redis using `SQLQuery`\n", | ||
| "- Translate SELECT, WHERE, and ORDER BY clauses to Redis queries\n", | ||
| "- Combine SQL queries with vector search\n", | ||
| "- Use aggregate functions and grouping" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Create an index to search" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 1, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:01.542482Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:01.542270Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.311130Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.310567Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stderr", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "/Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", | ||
| " from .autonotebook import tqdm as notebook_tqdm\n" | ||
| ] | ||
| } | ||
| ], | ||
| "source": [ | ||
| "from redisvl.utils.vectorize import HFTextVectorizer\n", | ||
| "\n", | ||
| "hf = HFTextVectorizer()\n", | ||
| "\n", | ||
| "schema = {\n", | ||
| " \"index\": {\n", | ||
| " \"name\": \"user_simple\",\n", | ||
| " \"prefix\": \"user_simple_docs\",\n", | ||
| " \"storage_type\": \"json\",\n", | ||
| " },\n", | ||
| " \"fields\": [\n", | ||
| " {\"name\": \"user\", \"type\": \"tag\"},\n", | ||
| " {\"name\": \"region\", \"type\": \"tag\"},\n", | ||
| " {\"name\": \"job\", \"type\": \"tag\"},\n", | ||
| " {\"name\": \"job_description\", \"type\": \"text\"},\n", | ||
| " {\"name\": \"age\", \"type\": \"numeric\"},\n", | ||
| " {\n", | ||
| " \"name\": \"job_embedding\",\n", | ||
| " \"type\": \"vector\",\n", | ||
| " \"attrs\": {\n", | ||
| " \"dims\": len(hf.embed(\"get embed length\")),\n", | ||
| " \"distance_metric\": \"cosine\",\n", | ||
| " \"algorithm\": \"flat\",\n", | ||
| " \"datatype\": \"float32\"\n", | ||
| " }\n", | ||
| " }\n", | ||
| " ]\n", | ||
| "}" | ||
| ] | ||
| }, | ||
| { | ||
| "attachments": {}, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Create sample dataset" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 2, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.312894Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.312512Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.621061Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.620563Z" | ||
| } | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "data = [\n", | ||
| " {\n", | ||
| " 'user': 'john',\n", | ||
| " 'age': 34,\n", | ||
| " 'job': 'software engineer',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job_description': 'Designs, develops, and maintains software applications and systems.'\n", | ||
| " },\n", | ||
| " {\n", | ||
| " 'user': 'bill',\n", | ||
| " 'age': 54,\n", | ||
| " 'job': 'engineer',\n", | ||
| " 'region': 'us-central',\n", | ||
| " 'job_description': 'Applies scientific and mathematical principles to solve technical problems.'\n", | ||
| " },\n", | ||
| " {\n", | ||
| " 'user': 'mary',\n", | ||
| " 'age': 24,\n", | ||
| " 'job': 'doctor',\n", | ||
| " 'region': 'us-central',\n", | ||
| " 'job_description': 'Diagnoses and treats illnesses, injuries, and other medical conditions in the healthcare field.'\n", | ||
| " },\n", | ||
| " {\n", | ||
| " 'user': 'joe',\n", | ||
| " 'age': 27,\n", | ||
| " 'job': 'dentist',\n", | ||
| " 'region': 'us-east',\n", | ||
| " 'job_description': 'Provides oral healthcare including diagnosing and treating teeth and gum issues.'\n", | ||
| " },\n", | ||
| " {\n", | ||
| " 'user': 'stacy',\n", | ||
| " 'age': 61,\n", | ||
| " 'job': 'project manager',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job_description': 'Plans, organizes, and oversees projects from inception to completion.'\n", | ||
| " }\n", | ||
| "]\n", | ||
| "\n", | ||
| "data = [\n", | ||
| " { \n", | ||
| " **d,\n", | ||
| " \"job_embedding\": hf.embed(f\"{d['job_description']=} {d['job']=}\"),\n", | ||
| " } \n", | ||
| " for d in data\n", | ||
| "]" | ||
| ] | ||
| }, | ||
| { | ||
| "attachments": {}, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Create a `SearchIndex`\n", | ||
| "\n", | ||
| "With the schema and sample dataset ready, create a `SearchIndex`." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Bring your own Redis connection instance\n", | ||
| "\n", | ||
| "This is ideal in scenarios where you have custom settings on the connection instance or if your application will share a connection pool:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 3, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.622451Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.622366Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.630721Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.630403Z" | ||
| } | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from redisvl.index import SearchIndex\n", | ||
| "from redis import Redis\n", | ||
| "\n", | ||
| "client = Redis.from_url(\"redis://localhost:6379\")\n", | ||
| "index = SearchIndex.from_dict(schema, redis_client=client, validate_on_load=True)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Let the index manage the connection instance\n", | ||
| "\n", | ||
| "This is ideal for simple cases:" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 4, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.632020Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.631934Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.633821Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.633429Z" | ||
| } | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "index = SearchIndex.from_dict(schema, redis_url=\"redis://localhost:6379\", validate_on_load=True)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Create the index\n", | ||
| "\n", | ||
| "Now that we are connected to Redis, we need to run the create command." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 5, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.634818Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.634741Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.640648Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.640244Z" | ||
| } | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "index.create(overwrite=True, drop=True)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Load Data to `SearchIndex`\n", | ||
| "\n", | ||
| "Load the sample dataset to Redis.\n", | ||
| "\n", | ||
| "### Validate data entries on load\n", | ||
| "RedisVL uses pydantic validation under the hood to ensure loaded data is valid and confirms to your schema. This setting is optional and can be configured in the `SearchIndex` class." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 6, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.641629Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.641563Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.751366Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.750887Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "['user_simple_docs:01KHKJGG26AR3VW2RJA381R8YK', 'user_simple_docs:01KHKJGG2R8EZP6H15MG1V4E53', 'user_simple_docs:01KHKJGG369F5R0R51PW2HP8MV', 'user_simple_docs:01KHKJGG3MGVPAZ6XEQVEWXZFC', 'user_simple_docs:01KHKJGG44ZEKJVRQJ0EF72PV7']\n" | ||
| ] | ||
| } | ||
| ], | ||
| "source": [ | ||
| "keys = index.load(data)\n", | ||
| "\n", | ||
| "print(keys)" | ||
| ] | ||
| }, | ||
| { | ||
| "attachments": {}, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Create a `SQLQuery` Object\n", | ||
| "\n", | ||
| "First, let's test a simple select statement such as the one below." | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 7, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.752548Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.752471Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:19.754355Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:19.753935Z" | ||
| } | ||
| }, | ||
| "outputs": [], | ||
| "source": [ | ||
| "from redisvl.query import SQLQuery\n", | ||
| "\n", | ||
| "sql_str = \"\"\"\n", | ||
| " SELECT user, region, job, age\n", | ||
| " FROM user_simple\n", | ||
| " WHERE age > 17\n", | ||
| " \"\"\"\n", | ||
| "\n", | ||
| "sql_query = SQLQuery(sql_str) " | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Check the created query string" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 8, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:19.755445Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:19.755366Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.018643Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.018223Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "'FT.SEARCH user_simple \"@age:[(17 +inf]\" RETURN 4 user region job age'" | ||
| ] | ||
| }, | ||
| "execution_count": 8, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| ], | ||
| "source": [ | ||
| "sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Executing the query" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 9, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:20.019728Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:20.019644Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.026215Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.025841Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "[{'user': 'john',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job': 'software engineer',\n", | ||
| " 'age': '34'},\n", | ||
| " {'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'},\n", | ||
| " {'user': 'mary', 'region': 'us-central', 'job': 'doctor', 'age': '24'},\n", | ||
| " {'user': 'joe', 'region': 'us-east', 'job': 'dentist', 'age': '27'},\n", | ||
| " {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'}]" | ||
| ] | ||
| }, | ||
| "execution_count": 9, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| ], | ||
| "source": [ | ||
| "results = index.query(sql_query)\n", | ||
| "results" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "## Additional query support\n", | ||
| "\n", | ||
| "### Conditional operators" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 10, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:20.027232Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:20.027154Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.036830Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.036450Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Resulting redis query: FT.SEARCH user_simple \"@age:[(17 +inf] @region:{us\\-west}\" RETURN 4 user region job age\n" | ||
| ] | ||
| }, | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "[{'user': 'john',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job': 'software engineer',\n", | ||
| " 'age': '34'},\n", | ||
| " {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'}]" | ||
| ] | ||
| }, | ||
| "execution_count": 10, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| ], | ||
| "source": [ | ||
| "sql_str = \"\"\"\n", | ||
| " SELECT user, region, job, age\n", | ||
| " FROM user_simple\n", | ||
| " WHERE age > 17 and region = 'us-west'\n", | ||
| "\"\"\"\n", | ||
| "\n", | ||
| "sql_query = SQLQuery(sql_str)\n", | ||
| "redis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\n", | ||
| "print(\"Resulting redis query: \", redis_query)\n", | ||
| "results = index.query(sql_query)\n", | ||
| "results" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 11, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:20.037744Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:20.037670Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.047532Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.047144Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Resulting redis query: FT.SEARCH user_simple \"((@region:{us\\-west})|(@region:{us\\-central}))\" RETURN 4 user region job age\n" | ||
| ] | ||
| }, | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "[{'user': 'john',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job': 'software engineer',\n", | ||
| " 'age': '34'},\n", | ||
| " {'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'},\n", | ||
| " {'user': 'stacy', 'region': 'us-west', 'job': 'project manager', 'age': '61'},\n", | ||
| " {'user': 'mary', 'region': 'us-central', 'job': 'doctor', 'age': '24'}]" | ||
| ] | ||
| }, | ||
| "execution_count": 11, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| ], | ||
| "source": [ | ||
| "sql_str = \"\"\"\n", | ||
| " SELECT user, region, job, age\n", | ||
| " FROM user_simple\n", | ||
| " WHERE region = 'us-west' or region = 'us-central'\n", | ||
| " \"\"\"\n", | ||
| "\n", | ||
| "sql_query = SQLQuery(sql_str)\n", | ||
| "redis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\n", | ||
| "print(\"Resulting redis query: \", redis_query)\n", | ||
| "results = index.query(sql_query)\n", | ||
| "results" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 12, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:20.048481Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:20.048419Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.057250Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.056891Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Resulting redis query: FT.SEARCH user_simple \"@job:{software engineer|engineer|pancake tester}\" RETURN 4 user region job age\n" | ||
| ] | ||
| }, | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "[{'user': 'john',\n", | ||
| " 'region': 'us-west',\n", | ||
| " 'job': 'software engineer',\n", | ||
| " 'age': '34'},\n", | ||
| " {'user': 'bill', 'region': 'us-central', 'job': 'engineer', 'age': '54'}]" | ||
| ] | ||
| }, | ||
| "execution_count": 12, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| ], | ||
| "source": [ | ||
| "# job is a tag field therefore this syntax works\n", | ||
| "sql_str = \"\"\"\n", | ||
| " SELECT user, region, job, age\n", | ||
| " FROM user_simple\n", | ||
| " WHERE job IN ('software engineer', 'engineer', 'pancake tester')\n", | ||
| " \"\"\"\n", | ||
| "\n", | ||
| "sql_query = SQLQuery(sql_str)\n", | ||
| "redis_query = sql_query.redis_query_string(redis_url=\"redis://localhost:6379\")\n", | ||
| "print(\"Resulting redis query: \", redis_query)\n", | ||
| "results = index.query(sql_query)\n", | ||
| "results" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "### Text based searches\n", | ||
| "\n", | ||
| "See [the docs](https://redis.io/docs/latest/develop/ai/search-and-query/query/full-text/) for available text queries in Redis.\n", | ||
| "\n", | ||
| "For more on exact matching see [here](https://redis.io/docs/latest/develop/ai/search-and-query/query/exact-match/)" | ||
| ] | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "execution_count": 13, | ||
| "metadata": { | ||
| "execution": { | ||
| "iopub.execute_input": "2026-02-16T15:20:20.058215Z", | ||
| "iopub.status.busy": "2026-02-16T15:20:20.058144Z", | ||
| "iopub.status.idle": "2026-02-16T15:20:20.067897Z", | ||
| "shell.execute_reply": "2026-02-16T15:20:20.067471Z" | ||
| } | ||
| }, | ||
| "outputs": [ | ||
| { | ||
| "name": "stdout", | ||
| "output_type": "stream", | ||
| "text": [ | ||
| "Resulting redis query: FT.SEARCH user_simple \"@job_description:sci*\" RETURN 5 user region job job_description age\n" | ||
| ] | ||
| }, | ||
| { | ||
| "data": { | ||
| "text/plain": [ | ||
| "[{'user': 'bill',\n", | ||
| " 'region': 'us-central',\n", | ||
| " 'job': 'engineer',\n", | ||
| " 'job_description': 'Applies scientific and mathematical principles to solve technical problems.',\n", | ||
| " 'age': '54'}]" | ||
| ] | ||
| }, | ||
| "execution_count": 13, | ||
| "metadata": {}, | ||
| "output_type": "execute_result" | ||
| } | ||
| "cells": [ | ||
| { | ||
| "attachments": {}, | ||
| "cell_type": "markdown", | ||
| "metadata": {}, | ||
| "source": [ | ||
| "# Write SQL Queries for Redis\n", | ||
| "\n", | ||
| "While Redis does not natively support SQL, RedisVL provides a `SQLQuery` class that translates SQL-like queries into Redis queries.\n", |
There was a problem hiding this comment.
This notebook was re-serialized with different JSON indentation/formatting, causing an enormous diff that’s hard to review and makes future git blame noisy. Other notebooks in docs/user_guide/ use a consistent 2-space JSON style; consider re-saving with the repo’s standard notebook serialization (or reverting this reformat-only change) so the PR stays focused on the multi-prefix feature.
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 7 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return prefix[0] if isinstance(prefix, list) else prefix | ||
|
|
||
| @property | ||
| def prefixes(self) -> List[str]: |
There was a problem hiding this comment.
Is there a need for any extra validation here (e.g. if a prefix happens to be an empty string, etc.), or is that better left to raise an error somewhere else?
| "sql_str = \"\"\"\n", | ||
| " SELECT user, region, job, age\n", | ||
| " FROM user_simple\n", | ||
| " WHERE job IN ('software engineer', 'engineer', 'pancake tester')\n", |
There was a problem hiding this comment.
A comment highlighting that there's a syntactic difference in the Redis translation of region = 'a' OR region = 'b' and region IN ('a', 'b') could be worthwhile (but not critical).
This PR let's you pass prefix as a list. It's not a recommend pattern but a blocker for a key customer. The main place this can cause issues in data loading.