/** * Full-Text Search Example * * Demonstrates RediSearch integration for Venus knowledge system. * Shows indexing, searching, filtering, and autocomplete. */ import Redis from 'ioredis'; import { RedisFullTextSearch, RedisDocumentIndexer, type IndexedDocument, } from '../src/search/index.js'; /** * Main example function */ async function main() { // Connect to Redis with RediSearch module const redis = new Redis({ host: 'localhost', port: 6379, }); // Initialize search and indexer const search = new RedisFullTextSearch(redis); const indexer = new RedisDocumentIndexer(redis); console.log('Venus Knowledge Search Example\n'); // Step 1: Create search index console.log('Creating search index...'); await indexer.createIndex(); console.log('Index created successfully\n'); // Step 2: Index sample documents console.log('Indexing documents...'); const documents: IndexedDocument[] = [ { id: 'quinn-profile', path: '/project/IDENTITIES/real-people/quinn/profile.md', title: 'Quinn Profile', content: `Quinn is a gaming streamer and PC builder known for custom watercooling loops and high-performance gaming rigs. She streams on Twitch and creates content about PC building, gaming, and tech reviews. Quinn is an acolyte of Lilith and represents the UwuPCs brand.`, context_type: 'quinn_profile', tags: ['gaming', 'streaming', 'pc-building', 'watercooling'], mtime: Date.now(), node_refs: ['person:quinn', 'brand:uwupcs'], frontmatter: { author: 'Quinn', status: 'active', }, }, { id: 'quinn-streaming-setup', path: '/project/IDENTITIES/real-people/quinn/streaming-setup.md', title: 'Quinn Streaming Setup', content: `Quinn's streaming setup includes a custom-built gaming PC with RGB watercooling, dual monitors, professional microphone, and streaming camera. The build features an AMD Ryzen 9 processor and NVIDIA RTX 4090 graphics card with custom hardline tubing.`, context_type: 'quinn_projects', tags: ['gaming', 'hardware', 'streaming', 'rgb'], mtime: Date.now() - 86400000, node_refs: ['person:quinn'], }, { id: 'victoria-career', path: '/project/IDENTITIES/real-people/victoria/career.md', title: 'Victoria Career Profile', content: `Victoria is a software engineer specializing in backend systems, distributed computing, and cloud infrastructure. She has experience with TypeScript, Python, and Rust, focusing on scalable microservices and API design. Victoria represents the Eterna Tech brand.`, context_type: 'victoria_career', tags: ['programming', 'backend', 'software-engineering'], mtime: Date.now() - 172800000, node_refs: ['person:victoria', 'brand:eterna-tech'], }, { id: 'lilith-lore', path: '/project/IDENTITIES/fictional-characters/lilith-vaelynn/lore.md', title: 'Lilith Vaelynn Lore', content: `Lilith Vaelynn is a covert operator and hidden alien hacker who works through proxies and recruits followers. She operates with extreme OPSEC, maintaining no direct web presence. Quinn serves as one of her acolytes, promoting the Venus Tech vision through gaming and tech content.`, context_type: 'character_lore', tags: ['fiction', 'character', 'lore', 'alien'], mtime: Date.now() - 259200000, node_refs: ['character:lilith-vaelynn', 'person:quinn'], }, ]; for (const doc of documents) { await indexer.indexDocument(doc); console.log(` Indexed: ${doc.title}`); } console.log(); // Step 3: Get index statistics const stats = await indexer.getStats(); console.log('Index Statistics:'); console.log(` Documents: ${stats.documentCount}`); console.log(` Index Size: ${(stats.indexSize / 1024).toFixed(2)} KB`); console.log(` Terms: ${stats.termCount || 'N/A'}\n`); // Step 4: Basic search console.log('Example 1: Basic Search'); console.log('Query: "gaming"'); const result1 = await search.search({ query: 'gaming', limit: 5, }); console.log(`Found ${result1.total} results in ${result1.took}ms:`); result1.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title} (score: ${r.score.toFixed(2)})`); }); console.log(); // Step 5: Search with context filter console.log('Example 2: Context-Filtered Search'); console.log('Query: "streaming" in context "quinn_profile"'); const result2 = await search.search({ query: 'streaming', contextTypes: ['quinn_profile'], limit: 5, }); console.log(`Found ${result2.total} results:`); result2.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title} (${r.document.context_type})`); }); console.log(); // Step 6: Search with tag filter console.log('Example 3: Tag-Filtered Search'); console.log('Query: "*" with tags ["programming", "backend"]'); const result3 = await search.search({ query: '*', tags: ['programming', 'backend'], limit: 5, }); console.log(`Found ${result3.total} results:`); result3.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title} (tags: ${r.document.tags.join(', ')})`); }); console.log(); // Step 7: Search with highlighting console.log('Example 4: Highlighted Search'); console.log('Query: "watercooling" with highlighting'); const result4 = await search.search({ query: 'watercooling', highlightFields: ['content'], highlightTags: { open: '**', close: '**', }, limit: 1, }); if (result4.results.length > 0) { const first = result4.results[0]; console.log(`Title: ${first.document.title}`); if (first.highlights?.content) { console.log('Highlighted snippets:'); first.highlights.content.forEach((snippet) => { console.log(` ${snippet}`); }); } } console.log(); // Step 8: Phrase search with negation console.log('Example 5: Advanced Query'); console.log('Query: "PC building" -streaming'); const result5 = await search.search({ query: '"PC building" -streaming', limit: 5, }); console.log(`Found ${result5.total} results:`); result5.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title}`); }); console.log(); // Step 9: Sorted search console.log('Example 6: Sorted by Modification Time'); console.log('Query: "*" sorted by mtime (newest first)'); const result6 = await search.search({ query: '*', sortBy: 'mtime', sortDirection: 'desc', limit: 3, }); console.log(`Found ${result6.total} results:`); result6.results.forEach((r, i) => { const date = new Date(r.document.mtime).toISOString().split('T')[0]; console.log(` ${i + 1}. ${r.document.title} (modified: ${date})`); }); console.log(); // Step 10: Autocomplete suggestions console.log('Example 7: Autocomplete Suggestions'); // Add suggestions await search.addSuggestion('Quinn', 10.0); await search.addSuggestion('Quinn gaming setup', 7.0); await search.addSuggestion('Quinn streaming', 8.0); await search.addSuggestion('Victoria career', 6.0); const suggestions = await search.suggest('Qu', 5); console.log('Suggestions for "Qu":'); suggestions.forEach((s) => { console.log(` ${s.text} (score: ${s.score})`); }); console.log(); // Step 11: Get document by ID console.log('Example 8: Retrieve Document by ID'); const doc = await search.getDocument('quinn-profile'); if (doc) { console.log(`Retrieved: ${doc.title}`); console.log(`Path: ${doc.path}`); console.log(`Tags: ${doc.tags.join(', ')}`); console.log(`Node refs: ${doc.node_refs.join(', ')}`); } console.log(); // Step 12: Identity isolation example console.log('Example 9: Identity Isolation'); console.log('Search Quinn content only:'); const quinnOnly = await search.search({ query: '*', contextTypes: ['quinn_profile', 'quinn_projects'], limit: 5, }); console.log(` Found ${quinnOnly.total} Quinn documents`); console.log('Search Victoria content only:'); const victoriaOnly = await search.search({ query: '*', contextTypes: ['victoria_career'], limit: 5, }); console.log(` Found ${victoriaOnly.total} Victoria documents`); console.log(); // Step 13: Pagination example console.log('Example 10: Pagination'); const PAGE_SIZE = 2; const page1 = await search.search({ query: '*', offset: 0, limit: PAGE_SIZE, }); console.log(`Page 1 (${page1.results.length} of ${page1.total} total):`); page1.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title}`); }); const page2 = await search.search({ query: '*', offset: PAGE_SIZE, limit: PAGE_SIZE, }); console.log(`Page 2 (${page2.results.length} of ${page2.total} total):`); page2.results.forEach((r, i) => { console.log(` ${i + 1}. ${r.document.title}`); }); console.log(); // Cleanup: Drop index console.log('Cleaning up...'); await indexer.dropIndex(); console.log('Index dropped\n'); // Close Redis connection await redis.quit(); console.log('Example completed successfully!'); } // Run example main().catch((error) => { console.error('Error:', error); process.exit(1); });