|
14 | 14 | use Symfony\AI\Fixtures\Movies; |
15 | 15 | use Symfony\AI\Platform\Bridge\OpenAi\PlatformFactory; |
16 | 16 | use Symfony\AI\Store\Bridge\Postgres\HybridStore; |
| 17 | +use Symfony\AI\Store\Bridge\Postgres\ReciprocalRankFusion; |
| 18 | +use Symfony\AI\Store\Bridge\Postgres\TextSearch\Bm25TextSearchStrategy; |
| 19 | +use Symfony\AI\Store\Bridge\Postgres\TextSearch\PostgresTextSearchStrategy; |
17 | 20 | use Symfony\AI\Store\Document\Loader\InMemoryLoader; |
18 | 21 | use Symfony\AI\Store\Document\Metadata; |
19 | 22 | use Symfony\AI\Store\Document\TextDocument; |
|
25 | 28 | require_once dirname(__DIR__).'/bootstrap.php'; |
26 | 29 |
|
27 | 30 | echo "=== PostgreSQL Hybrid Search Demo ===\n\n"; |
28 | | -echo "This example demonstrates how to configure the semantic ratio to balance\n"; |
29 | | -echo "between semantic (vector) search and PostgreSQL Full-Text Search.\n\n"; |
| 31 | +echo "Demonstrates HybridStore with configurable search strategies:\n"; |
| 32 | +echo "- Native PostgreSQL FTS vs BM25\n"; |
| 33 | +echo "- Semantic ratio adjustment\n"; |
| 34 | +echo "- Custom RRF scoring\n\n"; |
30 | 35 |
|
31 | | -// Initialize the hybrid store with balanced search (50/50) |
32 | 36 | $connection = DriverManager::getConnection((new DsnParser())->parse(env('POSTGRES_URI'))); |
33 | 37 | $pdo = $connection->getNativeConnection(); |
34 | 38 |
|
35 | 39 | if (!$pdo instanceof PDO) { |
36 | 40 | throw new RuntimeException('Unable to get native PDO connection from Doctrine DBAL.'); |
37 | 41 | } |
38 | 42 |
|
| 43 | +echo "=== Using BM25 Text Search Strategy ===\n\n"; |
| 44 | + |
39 | 45 | $store = new HybridStore( |
40 | 46 | connection: $pdo, |
41 | 47 | tableName: 'hybrid_movies', |
42 | | - semanticRatio: 0.5, // Balanced hybrid search by default |
| 48 | + textSearchStrategy: new Bm25TextSearchStrategy('en'), |
| 49 | + rrf: new ReciprocalRankFusion(k: 60, normalized: true), |
| 50 | + semanticRatio: 0.5, |
43 | 51 | ); |
44 | 52 |
|
45 | 53 | // Create embeddings and documents |
|
119 | 127 | // Cleanup |
120 | 128 | $store->drop(); |
121 | 129 |
|
122 | | -echo "=== Summary ===\n"; |
123 | | -echo "- semanticRatio = 0.0: Best for exact keyword matches (PostgreSQL FTS)\n"; |
124 | | -echo "- semanticRatio = 0.5: Balanced approach using RRF (Reciprocal Rank Fusion)\n"; |
125 | | -echo "- semanticRatio = 1.0: Best for conceptual similarity searches (pgvector)\n"; |
126 | | -echo "\nYou can set the default ratio when instantiating the HybridStore,\n"; |
127 | | -echo "and override it per query using the 'semanticRatio' option.\n"; |
| 130 | +echo "=== Comparing with Native PostgreSQL FTS ===\n\n"; |
| 131 | + |
| 132 | +$storeFts = new HybridStore( |
| 133 | + connection: $pdo, |
| 134 | + tableName: 'hybrid_movies_fts', |
| 135 | + textSearchStrategy: new PostgresTextSearchStrategy(), |
| 136 | + semanticRatio: 0.5, |
| 137 | +); |
| 138 | + |
| 139 | +$storeFts->setup(); |
| 140 | +$indexer = new Indexer(new InMemoryLoader($documents), $vectorizer, $storeFts, logger: logger()); |
| 141 | +$indexer->index($documents); |
| 142 | + |
| 143 | +$resultsFts = $storeFts->query($queryEmbedding, [ |
| 144 | + 'semanticRatio' => 0.5, |
| 145 | + 'q' => 'technology', |
| 146 | + 'limit' => 3, |
| 147 | +]); |
| 148 | + |
| 149 | +echo "Top 3 results (Native FTS):\n"; |
| 150 | +foreach ($resultsFts as $i => $result) { |
| 151 | + $metadata = $result->metadata->getArrayCopy(); |
| 152 | + echo sprintf( |
| 153 | + " %d. %s (Score: %.4f)\n", |
| 154 | + $i + 1, |
| 155 | + $metadata['title'] ?? 'Unknown', |
| 156 | + $result->score ?? 0.0 |
| 157 | + ); |
| 158 | +} |
| 159 | + |
| 160 | +$storeFts->drop(); |
| 161 | + |
| 162 | +echo "\n=== Summary ===\n"; |
| 163 | +echo "- semanticRatio = 0.0: Pure keyword matching\n"; |
| 164 | +echo "- semanticRatio = 0.5: Balanced hybrid (RRF)\n"; |
| 165 | +echo "- semanticRatio = 1.0: Pure semantic search\n"; |
| 166 | +echo "\nText Search Strategies:\n"; |
| 167 | +echo "- PostgresTextSearchStrategy: Native FTS (ts_rank_cd)\n"; |
| 168 | +echo "- Bm25TextSearchStrategy: BM25 ranking (requires pg_bm25 extension)\n"; |
0 commit comments