|
| 1 | +/** |
| 2 | + * Advanced Redis Vector Store Filtering Examples |
| 3 | + * |
| 4 | + * This example demonstrates all the advanced filtering capabilities of RedisVectorStore: |
| 5 | + * - Tag filters for categorical data |
| 6 | + * - Numeric filters for ranges and comparisons |
| 7 | + * - Text filters for full-text search |
| 8 | + * - Geo filters for location-based queries |
| 9 | + * - Timestamp filters for date/time queries |
| 10 | + * - Complex combinations with AND/OR logic |
| 11 | + * - Custom filters with raw RediSearch query syntax |
| 12 | + * |
| 13 | + * Note: Timestamps are stored as numeric fields (Unix epoch timestamps). |
| 14 | + * Date objects are automatically converted during serialization and returned |
| 15 | + * as numbers during deserialization. |
| 16 | + */ |
| 17 | + |
| 18 | +import { createClient } from "redis"; |
| 19 | +import { OpenAIEmbeddings } from "@langchain/openai"; |
| 20 | +import { |
| 21 | + RedisVectorStore, |
| 22 | + Tag, |
| 23 | + Num, |
| 24 | + Text, |
| 25 | + Geo, |
| 26 | + Timestamp, |
| 27 | + Custom, |
| 28 | +} from "@langchain/redis"; |
| 29 | +import { Document } from "@langchain/core/documents"; |
| 30 | + |
| 31 | +// Connect to Redis |
| 32 | +const client = createClient({ |
| 33 | + url: process.env.REDIS_URL ?? "redis://localhost:6379", |
| 34 | +}); |
| 35 | +await client.connect(); |
| 36 | + |
| 37 | +// Sample documents with rich metadata |
| 38 | +const docs = [ |
| 39 | + new Document({ |
| 40 | + metadata: { |
| 41 | + category: "electronics", |
| 42 | + price: 299.99, |
| 43 | + title: "Wireless Bluetooth Headphones", |
| 44 | + location: [-122.4194, 37.7749], // San Francisco |
| 45 | + created_at: new Date("2023-01-15"), |
| 46 | + brand: "TechCorp", |
| 47 | + rating: 4.5, |
| 48 | + }, |
| 49 | + pageContent: |
| 50 | + "High-quality wireless Bluetooth headphones with noise cancellation", |
| 51 | + }), |
| 52 | + new Document({ |
| 53 | + metadata: { |
| 54 | + category: "books", |
| 55 | + price: 24.99, |
| 56 | + title: "JavaScript Programming Guide", |
| 57 | + location: [-74.006, 40.7128], // New York |
| 58 | + created_at: new Date("2023-03-20"), |
| 59 | + author: "John Smith", |
| 60 | + pages: 450, |
| 61 | + }, |
| 62 | + pageContent: |
| 63 | + "Comprehensive guide to modern JavaScript programming techniques", |
| 64 | + }), |
| 65 | + new Document({ |
| 66 | + metadata: { |
| 67 | + category: "electronics", |
| 68 | + price: 899.99, |
| 69 | + title: "4K Smart TV", |
| 70 | + location: [-118.2437, 34.0522], // Los Angeles |
| 71 | + created_at: new Date("2023-02-10"), |
| 72 | + brand: "ViewTech", |
| 73 | + screen_size: 55, |
| 74 | + }, |
| 75 | + pageContent: |
| 76 | + "Ultra HD 4K Smart TV with streaming capabilities and voice control", |
| 77 | + }), |
| 78 | + new Document({ |
| 79 | + metadata: { |
| 80 | + category: "books", |
| 81 | + price: 19.99, |
| 82 | + title: "Machine Learning Basics", |
| 83 | + location: [-122.4194, 37.7749], // San Francisco |
| 84 | + created_at: new Date("2023-04-05"), |
| 85 | + author: "Jane Doe", |
| 86 | + pages: 320, |
| 87 | + }, |
| 88 | + pageContent: |
| 89 | + "Introduction to machine learning concepts and practical applications", |
| 90 | + }), |
| 91 | +]; |
| 92 | + |
| 93 | +// Create vector store with metadata schema for proper indexing |
| 94 | +const vectorStore = await RedisVectorStore.fromDocuments( |
| 95 | + docs, |
| 96 | + new OpenAIEmbeddings(), |
| 97 | + { |
| 98 | + redisClient: client, |
| 99 | + indexName: "advanced_products", |
| 100 | + customSchema: [ |
| 101 | + { name: "category", type: "tag" }, |
| 102 | + { name: "price", type: "numeric", options: { sortable: true } }, |
| 103 | + { name: "title", type: "text", options: { weight: 2.0 } }, |
| 104 | + { name: "location", type: "geo" }, |
| 105 | + // Timestamps are stored as numeric fields (Unix epoch timestamps) |
| 106 | + { name: "created_at", type: "numeric", options: { sortable: true } }, |
| 107 | + { name: "brand", type: "tag" }, |
| 108 | + { name: "author", type: "tag" }, |
| 109 | + { name: "rating", type: "numeric" }, |
| 110 | + { name: "pages", type: "numeric" }, |
| 111 | + { name: "screen_size", type: "numeric" }, |
| 112 | + ], |
| 113 | + } |
| 114 | +); |
| 115 | + |
| 116 | +console.log("=== Advanced Redis Vector Store Filtering Examples ===\n"); |
| 117 | + |
| 118 | +// Example 1: Simple tag filtering |
| 119 | +console.log("1. Simple tag filtering - Electronics only:"); |
| 120 | +const electronicsFilter = Tag("category").eq("electronics"); |
| 121 | +const electronicsResults = await vectorStore.similaritySearch( |
| 122 | + "high quality device", |
| 123 | + 5, |
| 124 | + electronicsFilter |
| 125 | +); |
| 126 | +console.log(`Found ${electronicsResults.length} electronics items`); |
| 127 | +electronicsResults.forEach((doc) => |
| 128 | + console.log(`- ${doc.metadata.title} ($${doc.metadata.price})`) |
| 129 | +); |
| 130 | +console.log(); |
| 131 | + |
| 132 | +// Example 2: Numeric range filtering |
| 133 | +console.log("2. Numeric range filtering - Products between $20-$500:"); |
| 134 | +const priceFilter = Num("price").between(20, 500); |
| 135 | +const priceResults = await vectorStore.similaritySearch( |
| 136 | + "quality product", |
| 137 | + 5, |
| 138 | + priceFilter |
| 139 | +); |
| 140 | +console.log(`Found ${priceResults.length} products in price range`); |
| 141 | +priceResults.forEach((doc) => |
| 142 | + console.log(`- ${doc.metadata.title} ($${doc.metadata.price})`) |
| 143 | +); |
| 144 | +console.log(); |
| 145 | + |
| 146 | +// Example 3: Text search filtering |
| 147 | +console.log("3. Text search filtering - Titles containing 'programming':"); |
| 148 | +const textFilter = Text("title").wildcard("*programming*"); |
| 149 | +const textResults = await vectorStore.similaritySearch( |
| 150 | + "learning guide", |
| 151 | + 5, |
| 152 | + textFilter |
| 153 | +); |
| 154 | +console.log(`Found ${textResults.length} programming-related items`); |
| 155 | +textResults.forEach((doc) => |
| 156 | + console.log(`- ${doc.metadata.title} by ${doc.metadata.author || "N/A"}`) |
| 157 | +); |
| 158 | +console.log(); |
| 159 | + |
| 160 | +// Example 4: Geographic filtering |
| 161 | +console.log("4. Geographic filtering - Items within 50km of San Francisco:"); |
| 162 | +const geoFilter = Geo("location").within(-122.4194, 37.7749, 50, "km"); |
| 163 | +const geoResults = await vectorStore.similaritySearch( |
| 164 | + "local products", |
| 165 | + 5, |
| 166 | + geoFilter |
| 167 | +); |
| 168 | +console.log(`Found ${geoResults.length} items near San Francisco`); |
| 169 | +geoResults.forEach((doc) => |
| 170 | + console.log(`- ${doc.metadata.title} (${doc.metadata.location})`) |
| 171 | +); |
| 172 | +console.log(); |
| 173 | + |
| 174 | +// Example 5: Timestamp filtering |
| 175 | +console.log("5. Timestamp filtering - Items created after March 1, 2023:"); |
| 176 | +const timestampFilter = Timestamp("created_at").gt(new Date("2023-03-01")); |
| 177 | +const timestampResults = await vectorStore.similaritySearch( |
| 178 | + "recent items", |
| 179 | + 5, |
| 180 | + timestampFilter |
| 181 | +); |
| 182 | +console.log(`Found ${timestampResults.length} recent items`); |
| 183 | +timestampResults.forEach((doc) => { |
| 184 | + // created_at is stored as a Unix epoch timestamp (number) |
| 185 | + // Convert it back to a Date for display |
| 186 | + const createdDate = new Date((doc.metadata.created_at as number) * 1000); |
| 187 | + console.log(`- ${doc.metadata.title} (${createdDate.toDateString()})`); |
| 188 | +}); |
| 189 | +console.log(); |
| 190 | + |
| 191 | +// Example 6: Complex combined filtering |
| 192 | +console.log("6. Complex filtering - Electronics under $400 in California:"); |
| 193 | +const complexFilter = Tag("category") |
| 194 | + .eq("electronics") |
| 195 | + .and(Num("price").lt(400)) |
| 196 | + .and(Geo("location").within(-119.4179, 36.7783, 500, "km")); // California center |
| 197 | + |
| 198 | +const complexResults = await vectorStore.similaritySearch( |
| 199 | + "affordable electronics", |
| 200 | + 5, |
| 201 | + complexFilter |
| 202 | +); |
| 203 | +console.log( |
| 204 | + `Found ${complexResults.length} affordable electronics in California` |
| 205 | +); |
| 206 | +complexResults.forEach((doc) => |
| 207 | + console.log( |
| 208 | + `- ${doc.metadata.title} ($${doc.metadata.price}) at ${doc.metadata.location}` |
| 209 | + ) |
| 210 | +); |
| 211 | +console.log(); |
| 212 | + |
| 213 | +// Example 7: OR filtering |
| 214 | +console.log("7. OR filtering - Books OR items under $30:"); |
| 215 | +const orFilter = Tag("category").eq("books").or(Num("price").lt(30)); |
| 216 | +const orResults = await vectorStore.similaritySearch( |
| 217 | + "affordable items", |
| 218 | + 5, |
| 219 | + orFilter |
| 220 | +); |
| 221 | +console.log(`Found ${orResults.length} books or cheap items`); |
| 222 | +orResults.forEach((doc) => |
| 223 | + console.log( |
| 224 | + `- ${doc.metadata.title} (${doc.metadata.category}, $${doc.metadata.price})` |
| 225 | + ) |
| 226 | +); |
| 227 | +console.log(); |
| 228 | + |
| 229 | +// Example 8: Multiple tag values |
| 230 | +console.log("8. Multiple tag values - TechCorp OR ViewTech brands:"); |
| 231 | +const multiTagFilter = Tag("brand").eq(["TechCorp", "ViewTech"]); |
| 232 | +const multiTagResults = await vectorStore.similaritySearch( |
| 233 | + "branded products", |
| 234 | + 5, |
| 235 | + multiTagFilter |
| 236 | +); |
| 237 | +console.log(`Found ${multiTagResults.length} items from specified brands`); |
| 238 | +multiTagResults.forEach((doc) => |
| 239 | + console.log(`- ${doc.metadata.title} by ${doc.metadata.brand}`) |
| 240 | +); |
| 241 | +console.log(); |
| 242 | + |
| 243 | +// Example 9: Negation filtering |
| 244 | +console.log("9. Negation filtering - NOT electronics:"); |
| 245 | +const negationFilter = Tag("category").ne("electronics"); |
| 246 | +const negationResults = await vectorStore.similaritySearch( |
| 247 | + "non-electronic items", |
| 248 | + 5, |
| 249 | + negationFilter |
| 250 | +); |
| 251 | +console.log(`Found ${negationResults.length} non-electronic items`); |
| 252 | +negationResults.forEach((doc) => |
| 253 | + console.log(`- ${doc.metadata.title} (${doc.metadata.category})`) |
| 254 | +); |
| 255 | +console.log(); |
| 256 | + |
| 257 | +// Example 10: Custom filter with raw RediSearch syntax |
| 258 | +console.log("10. Custom filter - Raw RediSearch query syntax:"); |
| 259 | +const customFilter = Custom("(@category:{electronics} @price:[0 400])"); |
| 260 | +const customResults = await vectorStore.similaritySearch( |
| 261 | + "affordable tech", |
| 262 | + 5, |
| 263 | + customFilter |
| 264 | +); |
| 265 | +console.log(`Found ${customResults.length} affordable electronics`); |
| 266 | +customResults.forEach((doc) => |
| 267 | + console.log(`- ${doc.metadata.title} ($${doc.metadata.price})`) |
| 268 | +); |
| 269 | +console.log(); |
| 270 | + |
| 271 | +// Cleanup |
| 272 | +await vectorStore.delete({ deleteAll: true }); |
| 273 | +await client.disconnect(); |
| 274 | + |
| 275 | +console.log("\n=== Advanced filtering examples completed! ==="); |
0 commit comments