Skip to content

Commit fc28f56

Browse files
committed
feat(redis): Advanced pre-filter for document metadata
1 parent cacc137 commit fc28f56

File tree

12 files changed

+4854
-880
lines changed

12 files changed

+4854
-880
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
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! ===");

β€Žlibs/providers/langchain-google-genai/package.jsonβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
},
3333
"dependencies": {
3434
"@google/generative-ai": "^0.24.0",
35-
"uuid": "^11.1.0"
35+
"uuid": "^11.1.0",
36+
"uuid": "^10.0.0"
3637
},
3738
"peerDependencies": {
3839
"@langchain/core": "^1.0.0-alpha.6"

β€Žlibs/providers/langchain-redis/package.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"format:check": "prettier --config .prettierrc --check \"src\""
2828
},
2929
"dependencies": {
30+
"uuid": "^10.0.0",
3031
"redis": "^4.6.13"
3132
},
3233
"peerDependencies": {
@@ -44,7 +45,6 @@
4445
"eslint": "^9.34.0",
4546
"prettier": "^2.8.3",
4647
"typescript": "~5.8.3",
47-
"uuid": "^10.0.0",
4848
"vitest": "^3.2.4"
4949
},
5050
"publishConfig": {

0 commit comments

Comments
Β (0)