Schema Design
The schema uses nested fields for profile data and exact matching for identifiers:- TypeScript
- Redis CLI
Copy
Ask AI
import { Redis, s } from "@upstash/redis";
const redis = Redis.fromEnv();
const users = await redis.search.createIndex({
name: "users",
dataType: "json",
prefix: "user:",
schema: s.object({
// Exact username matching (no tokenization)
username: s.string().noTokenize(),
// Nested profile fields
profile: s.object({
// Name search without stemming (proper nouns)
firstName: s.string().noStem(),
lastName: s.string().noStem(),
displayName: s.string().noStem(),
// Email as exact match (contains special characters)
email: s.string().noTokenize(),
// Searchable bio/about text
bio: s.string(),
}),
// Department and role for filtering
department: s.string().noTokenize(),
role: s.string().noTokenize(),
title: s.string(),
// Boolean flags
isActive: s.boolean(),
isAdmin: s.boolean(),
// Dates for filtering
hiredAt: s.date().fast(),
lastActiveAt: s.date().fast(),
}),
});
Copy
Ask AI
SEARCH.CREATE users ON JSON PREFIX 1 user: SCHEMA username TEXT NOTOKENIZE profile.firstName TEXT NOSTEM profile.lastName TEXT NOSTEM profile.displayName TEXT NOSTEM profile.email TEXT NOTOKENIZE profile.bio TEXT department TEXT NOTOKENIZE role TEXT NOTOKENIZE title TEXT isActive BOOL isAdmin BOOL hiredAt DATE FAST lastActiveAt DATE FAST
Sample Data
- TypeScript
- Redis CLI
Copy
Ask AI
await redis.json.set("user:1", "$", {
username: "jsmith",
profile: {
firstName: "Jane",
lastName: "Smith",
displayName: "Jane Smith",
email: "jane.smith@company.com",
bio: "Senior software engineer focused on backend systems and distributed computing.",
},
department: "Engineering",
role: "Individual Contributor",
title: "Senior Software Engineer",
isActive: true,
isAdmin: false,
hiredAt: "2021-03-15T00:00:00Z",
lastActiveAt: "2024-03-25T14:30:00Z",
});
await redis.json.set("user:2", "$", {
username: "mjohnson",
profile: {
firstName: "Michael",
lastName: "Johnson",
displayName: "Mike Johnson",
email: "michael.johnson@company.com",
bio: "Engineering manager leading the platform team. Passionate about developer experience.",
},
department: "Engineering",
role: "Manager",
title: "Engineering Manager",
isActive: true,
isAdmin: true,
hiredAt: "2019-06-01T00:00:00Z",
lastActiveAt: "2024-03-25T16:45:00Z",
});
await redis.json.set("user:3", "$", {
username: "swilliams",
profile: {
firstName: "Sarah",
lastName: "Williams",
displayName: "Sarah Williams",
email: "sarah.williams@company.com",
bio: "Product designer specializing in user research and interaction design.",
},
department: "Design",
role: "Individual Contributor",
title: "Senior Product Designer",
isActive: true,
isAdmin: false,
hiredAt: "2022-01-10T00:00:00Z",
lastActiveAt: "2024-03-24T11:20:00Z",
});
await redis.json.set("user:4", "$", {
username: "rbrown",
profile: {
firstName: "Robert",
lastName: "Brown",
displayName: "Rob Brown",
email: "robert.brown@company.com",
bio: "Former engineering lead, now focused on technical writing and documentation.",
},
department: "Engineering",
role: "Individual Contributor",
title: "Staff Engineer",
isActive: false,
isAdmin: false,
hiredAt: "2018-09-20T00:00:00Z",
lastActiveAt: "2023-12-15T09:00:00Z",
});
Copy
Ask AI
JSON.SET user:1 $ '{"username": "jsmith", "profile": {"firstName": "Jane", "lastName": "Smith", "displayName": "Jane Smith", "email": "jane.smith@company.com", "bio": "Senior software engineer focused on backend systems and distributed computing."}, "department": "Engineering", "role": "Individual Contributor", "title": "Senior Software Engineer", "isActive": true, "isAdmin": false, "hiredAt": "2021-03-15T00:00:00Z", "lastActiveAt": "2024-03-25T14:30:00Z"}'
JSON.SET user:2 $ '{"username": "mjohnson", "profile": {"firstName": "Michael", "lastName": "Johnson", "displayName": "Mike Johnson", "email": "michael.johnson@company.com", "bio": "Engineering manager leading the platform team. Passionate about developer experience."}, "department": "Engineering", "role": "Manager", "title": "Engineering Manager", "isActive": true, "isAdmin": true, "hiredAt": "2019-06-01T00:00:00Z", "lastActiveAt": "2024-03-25T16:45:00Z"}'
JSON.SET user:3 $ '{"username": "swilliams", "profile": {"firstName": "Sarah", "lastName": "Williams", "displayName": "Sarah Williams", "email": "sarah.williams@company.com", "bio": "Product designer specializing in user research and interaction design."}, "department": "Design", "role": "Individual Contributor", "title": "Senior Product Designer", "isActive": true, "isAdmin": false, "hiredAt": "2022-01-10T00:00:00Z", "lastActiveAt": "2024-03-24T11:20:00Z"}'
JSON.SET user:4 $ '{"username": "rbrown", "profile": {"firstName": "Robert", "lastName": "Brown", "displayName": "Rob Brown", "email": "robert.brown@company.com", "bio": "Former engineering lead, now focused on technical writing and documentation."}, "department": "Engineering", "role": "Individual Contributor", "title": "Staff Engineer", "isActive": false, "isAdmin": false, "hiredAt": "2018-09-20T00:00:00Z", "lastActiveAt": "2023-12-15T09:00:00Z"}'
Waiting for Indexing
Index updates are batched for performance, so newly added data may not appear in search results immediately. UseSEARCH.WAITINDEXING to ensure all pending updates are processed before querying:
- TypeScript
- Redis CLI
Copy
Ask AI
await users.waitIndexing();
Copy
Ask AI
SEARCH.WAITINDEXING users
Autocomplete Search
Use$fuzzy with prefix: true for search-as-you-type functionality. This approach handles
both incomplete words and typos, providing a more forgiving autocomplete experience:
- TypeScript
- Redis CLI
Copy
Ask AI
// As user types "ja" in the search box
const suggestions = await users.query({
filter: {
"profile.displayName": {
$fuzzy: {
value: "ja",
prefix: true,
},
},
},
limit: 5,
});
// Matches "Jane Smith", "James Wilson", etc.
// As user types "jn" (typo for "ja")
const typoSuggestions = await users.query({
filter: {
"profile.displayName": {
$fuzzy: {
value: "jn",
prefix: true,
transpositionCostOne: true,
},
},
},
limit: 5,
});
// Still matches "Jane Smith", "James Wilson", etc.
// As user types "jane smi"
const refinedSuggestions = await users.query({
filter: {
"profile.displayName": "jane smi",
},
limit: 5,
});
// Smart matching applies fuzzy prefix to last word automatically
// Matches "Jane Smith", "Jane Smithson", etc.
Copy
Ask AI
# As user types "ja"
SEARCH.QUERY users '{"profile.displayName": {"$fuzzy": {"value": "ja", "prefix": true}}}' LIMIT 5
# As user types "jn" (typo)
SEARCH.QUERY users '{"profile.displayName": {"$fuzzy": {"value": "jn", "prefix": true, "transpositionCostOne": true}}}' LIMIT 5
# As user types "jane smi" (smart matching handles fuzzy prefix on last word)
SEARCH.QUERY users '{"profile.displayName": "jane smi"}' LIMIT 5
Fuzzy Name Search
Handle typos and misspellings in name searches:- TypeScript
- Redis CLI
Copy
Ask AI
// User types "Micheal" (common misspelling of "Michael")
const results = await users.query({
filter: {
"profile.firstName": {
$fuzzy: "Micheal",
},
},
});
// Matches "Michael Johnson"
// Search with more tolerance for longer names
const fuzzyResults = await users.query({
filter: {
"profile.lastName": {
$fuzzy: {
value: "Willaims", // Typo in "Williams"
distance: 2,
},
},
},
});
// Search across first and last name with fuzzy matching
const combinedFuzzy = await users.query({
filter: {
$should: [
{ "profile.firstName": { $fuzzy: "Srah" } }, // Typo
{ "profile.lastName": { $fuzzy: "Srah" } },
],
},
});
Copy
Ask AI
# Fuzzy first name search
SEARCH.QUERY users '{"profile.firstName": {"$fuzzy": "Micheal"}}'
# Fuzzy with higher distance tolerance
SEARCH.QUERY users '{"profile.lastName": {"$fuzzy": {"value": "willaims", "distance": 2}}}'
# Search both first and last name
SEARCH.QUERY users '{"$should": [{"profile.firstName": {"$fuzzy": "srah"}}, {"profile.lastName": {"$fuzzy": "srah"}}]}'
Exact Username/Email Lookup
Find users by exact username or email:- TypeScript
- Redis CLI
Copy
Ask AI
// Exact username lookup
const user = await users.query({
filter: {
username: "jsmith",
},
});
// Exact email lookup
const userByEmail = await users.query({
filter: {
"profile.email": "jane.smith@company.com",
},
});
// Find users with email at specific domain
const companyUsers = await users.query({
filter: {
"profile.email": {
$regex: "jane.*@company\\.com",
},
},
});
Copy
Ask AI
# Exact username lookup
SEARCH.QUERY users '{"username": "jsmith"}'
# Exact email lookup
SEARCH.QUERY users '{"profile.email": "jane.smith@company.com"}'
# Users with specific email domain
SEARCH.QUERY users '{"profile.email": {"$regex": "jane.*@company\\.com"}}'
Department and Role Filtering
Filter users by department, role, or both:- TypeScript
- Redis CLI
Copy
Ask AI
// All users in Engineering
const engineers = await users.query({
filter: {
department: "Engineering",
isActive: true,
},
});
// All managers across departments
const managers = await users.query({
filter: {
role: "Manager",
isActive: true,
},
});
// Engineers who are managers
const engineeringManagers = await users.query({
filter: {
$must: {
department: "Engineering",
role: "Manager",
isActive: true,
},
},
});
// Users in Engineering or Design
const productTeam = await users.query({
filter: {
department: {
$in: ["Engineering", "Design", "Product"],
},
isActive: true,
},
});
Copy
Ask AI
# All users in Engineering
SEARCH.QUERY users '{"department": "Engineering", "isActive": true}'
# All managers
SEARCH.QUERY users '{"role": "Manager", "isActive": true}'
# Engineering managers
SEARCH.QUERY users '{"$must": {"department": "Engineering", "role": "Manager", "isActive": true}}'
# Users in multiple departments
SEARCH.QUERY users '{"department": {"$in": ["Engineering", "Design", "Product"]}, "isActive": true}'
Search by Skills in Bio
Find users with specific skills or expertise:- TypeScript
- Redis CLI
Copy
Ask AI
// Find users who mention "distributed" in their bio
const distributedExperts = await users.query({
filter: {
"profile.bio": "distributed",
isActive: true,
},
});
// Find users with multiple skills
const backendEngineers = await users.query({
filter: {
$must: {
"profile.bio": "backend",
department: "Engineering",
},
},
});
// Search for phrase in bio
const uxResearchers = await users.query({
filter: {
"profile.bio": {
$phrase: "user research",
},
},
});
Copy
Ask AI
# Find users mentioning "distributed" in bio
SEARCH.QUERY users '{"profile.bio": "distributed", "isActive": true}'
# Backend engineers
SEARCH.QUERY users '{"$must": {"profile.bio": "backend", "department": "Engineering"}}'
# Phrase search in bio
SEARCH.QUERY users '{"profile.bio": {"$phrase": "user research"}}'
Admin User Search
Find administrators or users with specific permissions:- TypeScript
- Redis CLI
Copy
Ask AI
// All admin users
const admins = await users.query({
filter: {
isAdmin: true,
isActive: true,
},
});
// Active non-admin users in Engineering
const regularEngineers = await users.query({
filter: {
$must: {
department: "Engineering",
isActive: true,
},
$mustNot: {
isAdmin: true,
},
},
});
Copy
Ask AI
# All admin users
SEARCH.QUERY users '{"isAdmin": true, "isActive": true}'
# Active non-admin engineers
SEARCH.QUERY users '{"$must": {"department": "Engineering", "isActive": true}, "$mustNot": {"isAdmin": true}}'
Key Takeaways
- Use
NOTOKENIZEfor usernames, emails, and exact-match identifiers - Use
NOSTEMfor proper nouns like names to prevent incorrect stemming - Use
$fuzzywithprefix: truefor search-as-you-type autocomplete with typo tolerance - Use
$fuzzyto handle typos in name searches - Combine nested field paths (e.g.,
profile.firstName) for structured data