|
| 1 | +# Agent Guide for sql-redis |
| 2 | + |
| 3 | +This document is for AI agents that want to **use** sql-redis to query Redis. |
| 4 | +For agents that want to **modify** the library itself, see |
| 5 | +[`docs/for-ais-only/`](docs/for-ais-only/). |
| 6 | + |
| 7 | +## What sql-redis is |
| 8 | + |
| 9 | +A SQL-to-Redis translator. It accepts a SQL `SELECT` string, looks up the |
| 10 | +target index's schema in Redis, and emits a `FT.SEARCH` or `FT.AGGREGATE` |
| 11 | +command. The library does not invent its own query language: SQL goes in, |
| 12 | +Redis search results come out as a `QueryResult(rows, count)`. |
| 13 | + |
| 14 | +## When to reach for it |
| 15 | + |
| 16 | +- The agent has a SQL string (from a planner, an LLM, an ORM, a user) and |
| 17 | + wants to run it against an existing RediSearch / RedisVL index. |
| 18 | +- The agent needs vector search, full-text search, GEO filters, or date |
| 19 | + filtering and wants a single uniform interface instead of building Redis |
| 20 | + command lists by hand. |
| 21 | +- The target index already exists. sql-redis does not run `FT.CREATE`. If |
| 22 | + there is no index, the query fails. Create the index first. |
| 23 | + |
| 24 | +## When NOT to reach for it |
| 25 | + |
| 26 | +- Plain key-value `GET`/`SET` work, list/set/sorted-set commands, streams, |
| 27 | + pub/sub. Use `redis-py` directly for those; sql-redis is a search-only |
| 28 | + translator. |
| 29 | +- Writes. There is no `INSERT`, `UPDATE`, or `DELETE`. Write through `redis-py`. |
| 30 | +- Index creation. Use `FT.CREATE` directly via `redis-py` first. |
| 31 | +- Cross-index joins, subqueries, `HAVING`, `DISTINCT`. Not implemented. |
| 32 | + |
| 33 | +## The minimum useful snippet |
| 34 | + |
| 35 | +```python |
| 36 | +from redis import Redis |
| 37 | +from sql_redis import create_executor |
| 38 | + |
| 39 | +client = Redis() |
| 40 | +executor = create_executor(client) # lazy schema loading; no I/O yet |
| 41 | + |
| 42 | +result = executor.execute( |
| 43 | + "SELECT title, price FROM products WHERE category = :cat AND price < :max LIMIT 10", |
| 44 | + params={"cat": "electronics", "max": 500}, |
| 45 | +) |
| 46 | + |
| 47 | +for row in result.rows: # row keys are bytes by default |
| 48 | + print(row[b"title"], row[b"price"]) |
| 49 | +``` |
| 50 | + |
| 51 | +Pass `decode_responses=True` to the `Redis` client if the agent prefers string |
| 52 | +keys. |
| 53 | + |
| 54 | +## Surface map |
| 55 | + |
| 56 | +| Task | Symbol | |
| 57 | +|---|---| |
| 58 | +| Run a SQL query | [`Executor.execute`](sql_redis/executor.py) / [`AsyncExecutor.execute`](sql_redis/executor.py) | |
| 59 | +| Build an executor | [`create_executor`](sql_redis/executor.py) / [`create_async_executor`](sql_redis/executor.py) | |
| 60 | +| Read or refresh schema | [`SchemaRegistry`](sql_redis/schema.py) / [`AsyncSchemaRegistry`](sql_redis/schema.py) | |
| 61 | +| Translate without executing | [`Translator.translate`](sql_redis/translator.py) returns a `TranslatedQuery` | |
| 62 | + |
| 63 | +Full reference, generated from docstrings, is at `docs/api/`. |
| 64 | + |
| 65 | +## Gotchas an agent should know |
| 66 | + |
| 67 | +1. **Field-key types.** Result rows come back with `bytes` keys when the |
| 68 | + underlying `Redis` client uses default `decode_responses=False`. Use |
| 69 | + `Redis(decode_responses=True)` if you want string keys end-to-end. |
| 70 | +2. **Parameter substitution is token-based.** `:id` will not match inside |
| 71 | + `:product_id`. String values are SQL-escaped automatically (`O'Brien` |
| 72 | + becomes `'O''Brien'`). Pass `bytes` for vector parameters; they are |
| 73 | + substituted as raw bytes after translation. |
| 74 | +3. **`=` on a TEXT field is exact-phrase, not tokenized.** Use `fulltext()` |
| 75 | + for tokenized AND search. See `docs/user_guide/how_to_guides/text-search.md`. |
| 76 | +4. **Stopwords are stripped automatically** before queries reach Redis. A |
| 77 | + `UserWarning` is emitted when this happens. Index with `STOPWORDS 0` to |
| 78 | + keep them. |
| 79 | +5. **`IS NULL` requires Redis 7.4+** and `INDEXMISSING` declared on the field. |
| 80 | +6. **Lazy schema loading is the default.** The first query that touches an |
| 81 | + index issues one `FT.INFO`. Pass `schema_cache_strategy="load_all"` to |
| 82 | + `create_executor` if you want to fail fast on missing indexes at startup. |
| 83 | +7. **No JOIN, subquery, HAVING, or DISTINCT.** The translator raises |
| 84 | + `ValueError`; do not retry with rephrasing. |
| 85 | +8. **GEO uses `POINT(lon, lat)` order.** Longitude first, matching Redis. |
| 86 | + |
| 87 | +## Error model an agent can expect |
| 88 | + |
| 89 | +| Exception | Meaning | Recovery | |
| 90 | +|---|---|---| |
| 91 | +| `ValueError` from `Translator.translate` | The SQL referenced an unknown index, unknown field, or unsupported clause. | Inspect the message; the index/field name is included. Do not retry the same SQL. | |
| 92 | +| `redis.ResponseError` from `Executor.execute` | Redis rejected the generated command. The most common cases are wrong field type or `INDEXMISSING` not declared. | Check the index schema. The library wraps `ismissing()` failures with a hint about Redis 7.4 + `INDEXMISSING`. | |
| 93 | +| `UserWarning` (not raised) | Stopwords stripped, or `IS NULL` used without `INDEXMISSING` declared. | Informational; does not affect the result. | |
| 94 | + |
| 95 | +## Discoverable artifacts in this repo |
| 96 | + |
| 97 | +| Artifact | Purpose | |
| 98 | +|---|---| |
| 99 | +| [`docs/llms.txt`](docs/llms.txt) | Flat index of every doc page with one-line summaries. Cheap to grep, good for in-context injection. | |
| 100 | +| [`docs/api/`](docs/api/) | Sphinx `autoclass` reference for every public symbol. Source of truth for method signatures. | |
| 101 | +| [`docs/user_guide/how_to_guides/`](docs/user_guide/how_to_guides/) | Task-oriented recipes (vector search, GEO, dates, async, parameters). | |
| 102 | +| [`docs/concepts/`](docs/concepts/) | Why-style explanation: architecture, parameter substitution, schema-aware translation. | |
| 103 | +| [`docs/for-ais-only/`](docs/for-ais-only/) | Internal repo map for agents modifying the library. | |
| 104 | + |
| 105 | +## Hub context |
| 106 | + |
| 107 | +sql-redis sits in the [Redis AI Hub](https://redis.io/ai-hub/) under the |
| 108 | +*Experimental* tier as "SQL for Redis". Public docs URL: |
| 109 | +[`docs.redisvl.com/projects/sql-redis/`](https://docs.redisvl.com/projects/sql-redis/). |
| 110 | +The hub's docs standards (Diataxis layout, autoclass-driven API reference, |
| 111 | +AI-agent affordances) are documented at |
| 112 | +[`HUB_DOCS_STANDARDS.md`](https://github.com/redis/docs/blob/main/HUB_DOCS_STANDARDS.md) |
| 113 | +in the hub repo. |
0 commit comments