E-commerce Search
This recipe demonstrates building a product catalog search with filtering, sorting, typo tolerance, and relevance boosting.
Schema Design
The schema balances searchability with filtering and sorting capabilities:
import { Redis, s } from "@upstash/redis";const redis = Redis.fromEnv();const products = await redis.search.createIndex({ name: "products", dataType: "json", prefix: "product:", schema: s.object({ // Full-text searchable fields name: s.string(), description: s.string(), brand: s.string().noStem(), // "Nike" shouldn't stem to "Nik" // Exact-match category for filtering category: s.string().noTokenize(), // "Electronics > Audio" as single token // Numeric fields for filtering and sorting price: s.number("F64"), // Enable sorting by price rating: s.number("F64"), // Enable sorting by rating reviewCount: s.number("U64"), // Boolean for stock filtering inStock: s.boolean(), // Date for "new arrivals" queries createdAt: s.date().fast(), }),});from upstash_redis import Redisredis = Redis.from_env()products = redis.search.create_index( name="products", data_type="json", prefix="product:", schema={ # Full-text searchable fields "name": "TEXT", "description": "TEXT", "brand": {"type": "TEXT", "nostem": True}, # "Nike" shouldn't stem to "Nik" # Exact-match category for filtering "category": {"type": "TEXT", "notokenize": True}, # "Electronics > Audio" as single token # Numeric fields for filtering and sorting "price": "F64", # Enable sorting by price "rating": "F64", # Enable sorting by rating "reviewCount": "U64", # Boolean for stock filtering "inStock": "BOOL", # Date for "new arrivals" queries "createdAt": {"type": "DATE", "fast": True}, },)SEARCH.CREATE products ON JSON PREFIX 1 product: SCHEMA name TEXT description TEXT brand TEXT NOSTEM category TEXT NOTOKENIZE price F64 FAST rating F64 FAST reviewCount U64 inStock BOOL createdAt DATE FASTSample Data
await redis.json.set("product:1", "$", { name: "Sony WH-1000XM5 Wireless Headphones", description: "Industry-leading noise cancellation with premium sound quality. 30-hour battery life with quick charging.", brand: "Sony", category: "Electronics > Audio > Headphones", price: 349.99, rating: 4.8, reviewCount: 2847, inStock: true, createdAt: "2024-01-15T00:00:00Z",});await redis.json.set("product:2", "$", { name: "Apple AirPods Pro 2nd Generation", description: "Active noise cancellation, transparency mode, and spatial audio. MagSafe charging case included.", brand: "Apple", category: "Electronics > Audio > Earbuds", price: 249.99, rating: 4.7, reviewCount: 5621, inStock: true, createdAt: "2024-02-20T00:00:00Z",});await redis.json.set("product:3", "$", { name: "Bose QuietComfort Ultra Headphones", description: "World-class noise cancellation with immersive spatial audio. Luxurious comfort for all-day wear.", brand: "Bose", category: "Electronics > Audio > Headphones", price: 429.99, rating: 4.6, reviewCount: 1253, inStock: false, createdAt: "2024-03-10T00:00:00Z",});redis.json().set("product:1", "$", { "name": "Sony WH-1000XM5 Wireless Headphones", "description": "Industry-leading noise cancellation with premium sound quality. 30-hour battery life with quick charging.", "brand": "Sony", "category": "Electronics > Audio > Headphones", "price": 349.99, "rating": 4.8, "reviewCount": 2847, "inStock": True, "createdAt": "2024-01-15T00:00:00Z",})redis.json().set("product:2", "$", { "name": "Apple AirPods Pro 2nd Generation", "description": "Active noise cancellation, transparency mode, and spatial audio. MagSafe charging case included.", "brand": "Apple", "category": "Electronics > Audio > Earbuds", "price": 249.99, "rating": 4.7, "reviewCount": 5621, "inStock": True, "createdAt": "2024-02-20T00:00:00Z",})redis.json().set("product:3", "$", { "name": "Bose QuietComfort Ultra Headphones", "description": "World-class noise cancellation with immersive spatial audio. Luxurious comfort for all-day wear.", "brand": "Bose", "category": "Electronics > Audio > Headphones", "price": 429.99, "rating": 4.6, "reviewCount": 1253, "inStock": False, "createdAt": "2024-03-10T00:00:00Z",})JSON.SET product:1 $ '{"name": "Sony WH-1000XM5 Wireless Headphones", "description": "Industry-leading noise cancellation with premium sound quality. 30-hour battery life with quick charging.", "brand": "Sony", "category": "Electronics > Audio > Headphones", "price": 349.99, "rating": 4.8, "reviewCount": 2847, "inStock": true, "createdAt": "2024-01-15T00:00:00Z"}'JSON.SET product:2 $ '{"name": "Apple AirPods Pro 2nd Generation", "description": "Active noise cancellation, transparency mode, and spatial audio. MagSafe charging case included.", "brand": "Apple", "category": "Electronics > Audio > Earbuds", "price": 249.99, "rating": 4.7, "reviewCount": 5621, "inStock": true, "createdAt": "2024-02-20T00:00:00Z"}'JSON.SET product:3 $ '{"name": "Bose QuietComfort Ultra Headphones", "description": "World-class noise cancellation with immersive spatial audio. Luxurious comfort for all-day wear.", "brand": "Bose", "category": "Electronics > Audio > Headphones", "price": 429.99, "rating": 4.6, "reviewCount": 1253, "inStock": false, "createdAt": "2024-03-10T00:00:00Z"}'Waiting for Indexing
Index updates are batched for performance, so newly added data may not appear in search results immediately.
Use SEARCH.WAITINDEXING to ensure all pending updates are processed before querying:
// Wait for all pending index updates to completeawait products.waitIndexing();# Wait for all pending index updates to completeproducts.wait_indexing()SEARCH.WAITINDEXING productsThis is especially useful in scripts or tests where you need to query immediately after inserting data. In production, the slight indexing delay is usually acceptable and calling this after every write is not recommended.
Basic Product Search
The simplest search uses smart matching for natural language queries:
// User types "wireless headphones" in search boxconst results = await products.query({ filter: { name: "wireless headphones", },});# User types "wireless headphones" in search boxresults = products.query( filter={"name": "wireless headphones"},)SEARCH.QUERY products '{"name": "wireless headphones"}'Smart matching automatically handles this by:
- Prioritizing exact phrase matches ("wireless headphones" adjacent)
- Including documents with both terms in any order
- Finding fuzzy matches for typos
Search with Typo Tolerance
Users often misspell product names. Use fuzzy matching to handle typos:
// User types "wireles headphons" (two typos)const results = await products.query({ filter: { $should: [ { name: { $fuzzy: "wireles" } }, { name: { $fuzzy: "headphons" } }, ], },});# User types "wireles headphons" (two typos)results = products.query( filter={ "$should": [ {"name": {"$fuzzy": "wireles"}}, {"name": {"$fuzzy": "headphons"}}, ], },)SEARCH.QUERY products '{"$should": [{"name": {"$fuzzy": "wireles"}}, {"name": {"$fuzzy": "headphons"}}]}'Filtered Search
Combine text search with filters for category, price, and availability:
// Search within a category, price range, and only in-stock itemsconst results = await products.query({ filter: { $must: { description: "noise cancellation", category: "Electronics > Audio > Headphones", inStock: true, price: { $gte: 200, $lte: 400 }, }, },});# Search within a category, price range, and only in-stock itemsresults = products.query( filter={ "$must": { "description": "noise cancellation", "category": "Electronics > Audio > Headphones", "inStock": True, "price": {"$gte": 200, "$lte": 400}, }, },)SEARCH.QUERY products '{"$must": {"description": "noise cancellation", "category": "Electronics > Audio > Headphones", "inStock": true, "price": {"$gte": 200, "$lte": 400}}}'Boosting Premium Results
Promote featured products or preferred brands using score boosting:
// Search for headphones, boosting Sony and in-stock itemsconst results = await products.query({ filter: { $must: { name: "headphones", }, $should: [ { brand: "Sony", $boost: 5.0 }, // Preferred brand { inStock: true, $boost: 10.0 }, // Strongly prefer in-stock { description: "premium", $boost: 2.0 }, ], },});# Search for headphones, boosting Sony and in-stock itemsresults = products.query( filter={ "$must": { "name": "headphones", }, "$should": [ {"brand": "Sony", "$boost": 5.0}, # Preferred brand {"inStock": True, "$boost": 10.0}, # Strongly prefer in-stock {"description": "premium", "$boost": 2.0}, ], },)SEARCH.QUERY products '{"$must": {"name": "headphones"}, "$should": [{"brand": "Sony", "$boost": 5.0}, {"inStock": true, "$boost": 10.0}, {"description": "premium", "$boost": 2.0}]}'Sorting and Pagination
Sort results by price or rating, with pagination for large result sets:
// Page 1: Top-rated headphones, 20 per pageconst page1 = await products.query({ filter: { category: "Electronics > Audio > Headphones", }, orderBy: { rating: "DESC", }, limit: 20,});// Page 2const page2 = await products.query({ filter: { category: "Electronics > Audio > Headphones", }, orderBy: { rating: "DESC", }, limit: 20, offset: 20,});// Sort by price, cheapest firstconst cheapest = await products.query({ filter: { name: "headphones", inStock: true, }, orderBy: { price: "ASC", }, limit: 10,});# Page 1: Top-rated headphones, 20 per pagepage1 = products.query( filter={"category": "Electronics > Audio > Headphones"}, order_by={"rating": "DESC"}, limit=20,)# Page 2page2 = products.query( filter={"category": "Electronics > Audio > Headphones"}, order_by={"rating": "DESC"}, limit=20, offset=20,)# Sort by price, cheapest firstcheapest = products.query( filter={"name": "headphones", "inStock": True}, order_by={"price": "ASC"}, limit=10,)# Page 1: Top-rated headphonesSEARCH.QUERY products '{"category": "Electronics > Audio > Headphones"}' ORDERBY rating DESC LIMIT 20# Page 2SEARCH.QUERY products '{"category": "Electronics > Audio > Headphones"}' ORDERBY rating DESC LIMIT 20 OFFSET 20# Sort by price, cheapest firstSEARCH.QUERY products '{"name": "headphones", "inStock": true}' ORDERBY price ASC LIMIT 10New Arrivals
Find recently added products using date range queries:
// Products added after a specific dateconst newArrivals = await products.query({ filter: { createdAt: { $gte: "2026-01-01T00:00:00Z" }, inStock: true, }, orderBy: { createdAt: "DESC", }, limit: 10,});# Products added after a specific datenew_arrivals = products.query( filter={ "createdAt": {"$gte": "2026-01-01T00:00:00Z"}, "inStock": True, }, order_by={"createdAt": "DESC"}, limit=10,)# Products added after a specific dateSEARCH.QUERY products '{"createdAt": {"$gte": "2026-01-01T00:00:00Z"}, "inStock": true}' ORDERBY createdAt DESC LIMIT 10Excluding Out-of-Stock Items
Use $mustNot to filter out unavailable products:
// Search results excluding out-of-stock itemsconst results = await products.query({ filter: { $must: { name: "headphones", }, $mustNot: { inStock: false, }, },});# Search results excluding out-of-stock itemsresults = products.query( filter={ "$must": { "name": "headphones", }, "$mustNot": { "inStock": False, }, },)SEARCH.QUERY products '{"$must": {"name": "headphones"}, "$mustNot": {"inStock": false}}'Multi-Category Search
Search across multiple categories using $in:
// Find products in either headphones or earbuds categoriesconst results = await products.query({ filter: { $must: { description: "noise cancellation", category: { $in: [ "Electronics > Audio > Headphones", "Electronics > Audio > Earbuds", ], }, }, },});# Find products in either headphones or earbuds categoriesresults = products.query( filter={ "$must": { "description": "noise cancellation", "category": { "$in": [ "Electronics > Audio > Headphones", "Electronics > Audio > Earbuds", ], }, }, },)SEARCH.QUERY products '{"$must": {"description": "noise cancellation", "category": {"$in": ["Electronics > Audio > Headphones", "Electronics > Audio > Earbuds"]}}}'Counting Results
Use SEARCH.COUNT to get the number of matching documents without retrieving them.
This is useful for pagination UI ("Showing 1-20 of 156 results") or analytics:
// Count all products in a categoryconst totalHeadphones = await products.count({ filter: { category: "Electronics > Audio > Headphones", },});# Count all products in a categorytotal_headphones = products.count( filter={"category": "Electronics > Audio > Headphones"},)# Count all products in a categorySEARCH.COUNT products '{"category": "Electronics > Audio > Headphones"}'Key Takeaways
- Use
NOTOKENIZEfor categories and codes that should match exactly - Use
NOSTEMfor brand names to prevent unwanted stemming - Mark price, rating, and date fields as
FASTfor sorting - Combine
$must,$should, and$mustNotfor complex filtering - Use
$boostto promote featured or preferred items - Use
SEARCH.COUNTto get result counts for pagination UI - Smart matching handles most natural language queries automatically