|
| 1 | +# Redis OM Spring Demo - Hybrid Search |
| 2 | + |
| 3 | +This demo showcases Redis OM Spring's **Hybrid Search** capability, which combines full-text search (BM25) with vector similarity search to provide powerful semantic search functionality. |
| 4 | + |
| 5 | +## What is Hybrid Search? |
| 6 | + |
| 7 | +Hybrid search combines two complementary search approaches: |
| 8 | + |
| 9 | +1. **Full-Text Search (BM25)**: Traditional keyword-based search that matches query terms against document text |
| 10 | +2. **Vector Similarity Search**: Semantic search using embeddings that captures meaning and context |
| 11 | + |
| 12 | +The results are scored using a weighted combination: |
| 13 | + |
| 14 | +``` |
| 15 | +hybrid_score = (1 - alpha) × text_score + alpha × vector_similarity |
| 16 | +``` |
| 17 | + |
| 18 | +Where: |
| 19 | +- `alpha = 0.0` → Pure text search (BM25 only) |
| 20 | +- `alpha = 0.5` → Balanced hybrid search |
| 21 | +- `alpha = 0.7` → Default, favoring semantic similarity |
| 22 | +- `alpha = 1.0` → Pure vector search (semantic only) |
| 23 | + |
| 24 | +## Features |
| 25 | + |
| 26 | +This demo demonstrates: |
| 27 | + |
| 28 | +- **Hybrid search** combining text and vector similarity |
| 29 | +- **Filtered hybrid search** (e.g., category + price range filters) |
| 30 | +- **Comparison** between text-only, vector-only, and hybrid search |
| 31 | +- **Alpha parameter tuning** to adjust text vs. vector weighting |
| 32 | +- **Real product data** with mock embeddings |
| 33 | + |
| 34 | +## Prerequisites |
| 35 | + |
| 36 | +- Java 21+ |
| 37 | +- Redis Stack 7.4+ running locally (for hybrid search support) |
| 38 | +- Gradle |
| 39 | + |
| 40 | +## Running Redis Stack |
| 41 | + |
| 42 | +Start Redis Stack using Docker: |
| 43 | + |
| 44 | +```bash |
| 45 | +docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack:latest |
| 46 | +``` |
| 47 | + |
| 48 | +Or use Docker Compose from the project root: |
| 49 | + |
| 50 | +```bash |
| 51 | +docker compose up |
| 52 | +``` |
| 53 | + |
| 54 | +## Running the Demo |
| 55 | + |
| 56 | +From the project root: |
| 57 | + |
| 58 | +```bash |
| 59 | +./mvnw install -Dmaven.test.skip && ./mvnw spring-boot:run -pl demos/roms-hybrid |
| 60 | +``` |
| 61 | + |
| 62 | +Or using Gradle: |
| 63 | + |
| 64 | +```bash |
| 65 | +./gradlew :demos:roms-hybrid:bootRun |
| 66 | +``` |
| 67 | + |
| 68 | +The application will: |
| 69 | +1. Start on port 8080 |
| 70 | +2. Connect to Redis on localhost:6379 |
| 71 | +3. Create search indexes |
| 72 | +4. Load sample product data |
| 73 | + |
| 74 | +## API Endpoints |
| 75 | + |
| 76 | +### Basic Operations |
| 77 | + |
| 78 | +**Get all products:** |
| 79 | +```bash |
| 80 | +curl http://localhost:8080/api/products |
| 81 | +``` |
| 82 | + |
| 83 | +**Get product by ID:** |
| 84 | +```bash |
| 85 | +curl http://localhost:8080/api/products/elec-001 |
| 86 | +``` |
| 87 | + |
| 88 | +**Get products by category:** |
| 89 | +```bash |
| 90 | +curl http://localhost:8080/api/products/category/Electronics |
| 91 | +``` |
| 92 | + |
| 93 | +### Search Operations |
| 94 | + |
| 95 | +**1. Hybrid Search (Recommended)** |
| 96 | + |
| 97 | +Combines text and semantic search for best results: |
| 98 | + |
| 99 | +```bash |
| 100 | +curl -X POST http://localhost:8080/api/products/hybrid-search \ |
| 101 | + -H "Content-Type: application/json" \ |
| 102 | + -d '{ |
| 103 | + "text": "wireless headphones for music", |
| 104 | + "embedding": [0.8, 0.2, 0.7, ...], # 384 dimensions |
| 105 | + "alpha": 0.7, |
| 106 | + "limit": 5 |
| 107 | + }' |
| 108 | +``` |
| 109 | + |
| 110 | +**With filters:** |
| 111 | +```bash |
| 112 | +curl -X POST "http://localhost:8080/api/products/hybrid-search?text=headphones&alpha=0.7&category=Electronics&minPrice=100&maxPrice=300&limit=10" \ |
| 113 | + -H "Content-Type: application/json" \ |
| 114 | + -d @embedding.json |
| 115 | +``` |
| 116 | + |
| 117 | +**2. Text-Only Search** |
| 118 | + |
| 119 | +Traditional full-text search: |
| 120 | + |
| 121 | +```bash |
| 122 | +curl "http://localhost:8080/api/products/search/text?query=wireless%20headphones&limit=5" |
| 123 | +``` |
| 124 | + |
| 125 | +**3. Vector-Only Search** |
| 126 | + |
| 127 | +Pure semantic similarity: |
| 128 | + |
| 129 | +```bash |
| 130 | +curl -X POST http://localhost:8080/api/products/search/semantic \ |
| 131 | + -H "Content-Type: application/json" \ |
| 132 | + -d @embedding.json |
| 133 | +``` |
| 134 | + |
| 135 | +## Sample Data |
| 136 | + |
| 137 | +The demo includes 12 products across 4 categories: |
| 138 | + |
| 139 | +- **Electronics**: Headphones, TV, Laptop, Mouse |
| 140 | +- **Home & Kitchen**: Coffee Maker, Cookware, Robot Vacuum |
| 141 | +- **Sports & Outdoors**: Yoga Mat, Running Shoes, Tent |
| 142 | +- **Books**: Redis Guide, Machine Learning Basics |
| 143 | + |
| 144 | +Each product has: |
| 145 | +- Full-text searchable description |
| 146 | +- 384-dimensional embedding (mock data based on category/features) |
| 147 | +- Filterable attributes (category, price, brand, stock) |
| 148 | + |
| 149 | +## Example Queries |
| 150 | + |
| 151 | +### Find "wireless audio devices" (semantic understanding) |
| 152 | + |
| 153 | +```bash |
| 154 | +# Hybrid search will find "Wireless Bluetooth Headphones" even though |
| 155 | +# the query says "audio devices" and product says "headphones" |
| 156 | +curl -X POST "http://localhost:8080/api/products/hybrid-search?text=audio%20devices&alpha=0.7&limit=5" \ |
| 157 | + -H "Content-Type: application/json" \ |
| 158 | + -d @headphone_embedding.json |
| 159 | +``` |
| 160 | + |
| 161 | +### Find Electronics under $500 |
| 162 | + |
| 163 | +```bash |
| 164 | +curl -X POST "http://localhost:8080/api/products/hybrid-search?text=electronics&alpha=0.5&category=Electronics&maxPrice=500&limit=10" \ |
| 165 | + -H "Content-Type: application/json" \ |
| 166 | + -d @electronics_embedding.json |
| 167 | +``` |
| 168 | + |
| 169 | +### Compare Search Methods |
| 170 | + |
| 171 | +Try the same query with different alpha values: |
| 172 | +- `alpha=0.0` - Pure text matching (may miss semantic relationships) |
| 173 | +- `alpha=0.5` - Balanced (good for most use cases) |
| 174 | +- `alpha=1.0` - Pure semantic (may miss exact keyword matches) |
| 175 | + |
| 176 | +## Code Examples |
| 177 | + |
| 178 | +### Using EntityStream API |
| 179 | + |
| 180 | +```java |
| 181 | +@Autowired |
| 182 | +private EntityStream entityStream; |
| 183 | + |
| 184 | +// Hybrid search with filters |
| 185 | +List<Product> products = entityStream.of(Product.class) |
| 186 | + .filter(Product$.CATEGORY.eq("Electronics")) |
| 187 | + .filter(Product$.PRICE.between(100.0, 500.0)) |
| 188 | + .hybridSearch( |
| 189 | + "wireless headphones", |
| 190 | + Product$.DESCRIPTION, |
| 191 | + queryEmbedding, |
| 192 | + Product$.EMBEDDING, |
| 193 | + 0.7f // 70% vector, 30% text |
| 194 | + ) |
| 195 | + .limit(10) |
| 196 | + .collect(Collectors.toList()); |
| 197 | +``` |
| 198 | + |
| 199 | +### Adjusting Search Behavior |
| 200 | + |
| 201 | +```java |
| 202 | +// More text-focused (good for specific keyword matching) |
| 203 | +.hybridSearch(text, textField, vector, vectorField, 0.3f) |
| 204 | + |
| 205 | +// Balanced approach |
| 206 | +.hybridSearch(text, textField, vector, vectorField, 0.5f) |
| 207 | + |
| 208 | +// More semantic-focused (good for conceptual similarity) |
| 209 | +.hybridSearch(text, textField, vector, vectorField, 0.7f) |
| 210 | +``` |
| 211 | + |
| 212 | +## How It Works |
| 213 | + |
| 214 | +1. **Indexing**: Redis OM Spring creates a search index with: |
| 215 | + - Full-text index on `description` field |
| 216 | + - Vector index on `embedding` field (384D, COSINE distance) |
| 217 | + - Tag/numeric indexes on filterable fields |
| 218 | + |
| 219 | +2. **Query Execution**: Uses RedisVL's `HybridQuery` which: |
| 220 | + - Performs FT.AGGREGATE with text and vector scoring |
| 221 | + - Combines scores using the alpha parameter |
| 222 | + - Applies filters using Redis query syntax |
| 223 | + - Returns results sorted by hybrid score |
| 224 | + |
| 225 | +3. **Result Ranking**: Documents are scored as: |
| 226 | + ``` |
| 227 | + hybrid_score = (1-α) × BM25_score + α × (1 - cosine_distance) |
| 228 | + ``` |
| 229 | + |
| 230 | +## Real-World Applications |
| 231 | + |
| 232 | +Hybrid search is ideal for: |
| 233 | + |
| 234 | +- **E-commerce**: "Find running shoes similar to these" + "for marathon training" |
| 235 | +- **Document Search**: "Contract documents about pricing" (semantic + keyword) |
| 236 | +- **Content Discovery**: "Articles like this one" + "about machine learning" |
| 237 | +- **Job Matching**: "Software engineer" + similar to candidate profile |
| 238 | +- **Customer Support**: "How to reset password" (handles variations semantically) |
| 239 | + |
| 240 | +## Technology Stack |
| 241 | + |
| 242 | +- **Redis Stack 7.4+**: Provides RediSearch with hybrid query support |
| 243 | +- **Redis OM Spring**: Object mapping and search capabilities |
| 244 | +- **RedisVL for Java**: Hybrid query implementation |
| 245 | +- **Spring Boot 3.x**: Application framework |
| 246 | +- **Lombok**: Reduces boilerplate code |
| 247 | + |
| 248 | +## Learn More |
| 249 | + |
| 250 | +- [Redis OM Spring Documentation](https://redis.io/docs/clients/om-clients/stack-spring/) |
| 251 | +- [RediSearch Hybrid Queries](https://redis.io/docs/interact/search-and-query/advanced-concepts/hybrid-queries/) |
| 252 | +- [Vector Similarity Search](https://redis.io/docs/interact/search-and-query/advanced-concepts/vectors/) |
| 253 | + |
| 254 | +## License |
| 255 | + |
| 256 | +This demo is part of Redis OM Spring and follows the same license. |
0 commit comments