@@ -154,7 +154,7 @@ The layered approach emerged from TDD — writing tests first revealed natural b
154154- [x] Computed fields: ` price * 0.9 AS discounted `
155155- [x] Vector KNN search: ` vector_distance(field, :param) `
156156- [x] Hybrid search (filters + vector)
157- - [x] Full-text search: ` LIKE 'prefix%' ` (prefix), ` fulltext(field, 'terms') ` function
157+ - [x] Full-text search: exact phrase, fuzzy, proximity, OR/union, LIKE patterns, BM25 scoring (see below)
158158- [x] GEO field queries with full operator support (see below)
159159- [x] Date functions: ` YEAR() ` , ` MONTH() ` , ` DAY() ` , ` DATE_FORMAT() ` , etc. (see below)
160160
@@ -166,6 +166,59 @@ The layered approach emerged from TDD — writing tests first revealed natural b
166166- [ ] DISTINCT
167167- [ ] Index creation from SQL (CREATE INDEX)
168168
169+ ### TEXT Search
170+
171+ Full-text search on TEXT fields with multiple search modes:
172+
173+ | Feature | SQL Syntax | RediSearch Output |
174+ | ---------| -----------| -------------------|
175+ | Exact phrase | ` title = 'gaming laptop' ` | ` @title:"gaming laptop" ` |
176+ | Tokenized search | ` fulltext(title, 'gaming laptop') ` | ` @title:(gaming laptop) ` |
177+ | Fuzzy LD=1 | ` fuzzy(title, 'laptap') ` | ` @title:%laptap% ` |
178+ | Fuzzy LD=2 | ` fuzzy(title, 'laptap', 2) ` | ` @title:%%laptap%% ` |
179+ | Fuzzy LD=3 | ` fuzzy(title, 'laptap', 3) ` | ` @title:%%%laptap%%% ` |
180+ | OR / union | ` fulltext(title, 'laptop OR tablet') ` | ` @title:(laptop\|tablet) ` |
181+ | Prefix | ` title LIKE 'lap%' ` | ` @title:lap* ` |
182+ | Suffix | ` title LIKE '%top' ` | ` @title:*top ` |
183+ | Contains | ` title LIKE '%apt%' ` | ` @title:*apt* ` |
184+ | Proximity (slop) | ` fulltext(title, 'gaming laptop', 2) ` | ` @title:(gaming laptop) => { $slop: 2; } ` |
185+ | Proximity + order | ` fulltext(title, 'gaming laptop', 2, true) ` | ` @title:(gaming laptop) => { $slop: 2; $inorder: true; } ` |
186+ | Optional term | ` fulltext(title, 'laptop ~gaming') ` | ` @title:(laptop ~gaming) ` |
187+ | BM25 score | ` SELECT score() AS relevance FROM idx ` | ` FT.SEARCH ... WITHSCORES ` |
188+ | Negation | ` NOT fulltext(title, 'refurbished') ` | ` -@title:(refurbished) ` |
189+
190+ ** Examples:**
191+
192+ ``` sql
193+ -- Exact phrase match (stopwords preserved)
194+ SELECT * FROM products WHERE title = ' bank of america'
195+
196+ -- Fuzzy search for typos (Levenshtein distance 2)
197+ SELECT * FROM products WHERE fuzzy(title, ' laptap' , 2 )
198+
199+ -- OR search across terms
200+ SELECT * FROM products WHERE fulltext(title, ' laptop OR tablet OR phone' )
201+
202+ -- Proximity: terms within 3 words of each other, in order
203+ SELECT * FROM products WHERE fulltext(title, ' gaming laptop' , 3 , true)
204+
205+ -- Suffix/contains pattern matching
206+ SELECT * FROM products WHERE title LIKE ' %phone%'
207+
208+ -- BM25 relevance scoring
209+ SELECT title, score() AS relevance FROM products WHERE fulltext(title, ' laptop' )
210+
211+ -- Multi-field search
212+ SELECT * FROM products WHERE fulltext(title, ' laptop' ) OR fulltext(description, ' laptop' )
213+ ```
214+
215+ ** Notes:**
216+ - ` = ` on TEXT fields performs ** exact phrase** matching (preserves stopwords)
217+ - ` fulltext() ` performs ** tokenized** search (stopwords are filtered with a warning)
218+ - ` fuzzy() ` and ` fulltext() ` only work on TEXT fields — using them on TAG or NUMERIC raises ` ValueError `
219+ - OR is case-insensitive: ` 'laptop OR tablet' ` , ` 'laptop or tablet' ` , and ` 'laptop Or tablet' ` all work
220+ - Special characters (` @ ` , ` | ` , ` - ` , ` * ` , ` + ` , etc.) in search terms are automatically escaped
221+
169222### DATE/DATETIME Handling
170223
171224Redis does not have a native DATE field type. Dates are stored as ** NUMERIC fields** with Unix timestamps.
0 commit comments