symfony/ai-open-search-store
OpenSearch vector store integration for Symfony AI Store. Index and query embeddings using OpenSearch knn_vector fields and k‑NN/approximate k‑NN search. Links to OpenSearch docs and contribution resources in the main Symfony AI repo.
Install Dependencies:
composer require symfony/ai-open-search-store symfony/ai symfony/http-client
Ensure your Laravel app can use Symfony components (e.g., via symfony/process or a facade).
Set Up OpenSearch:
knn_vector field:
curl -X PUT "http://localhost:9200/vector_index" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"embedding": { "type": "knn_vector", "dimension": 768 }
}
}
}'
Configure the Store: Create a Laravel service provider to bind the OpenSearch store:
// app/Providers/OpenSearchStoreServiceProvider.php
use Symfony\Component\AI\Store\OpenSearchStore;
use OpenSearch\Client;
public function register()
{
$this->app->singleton(\Symfony\Component\AI\Store\StoreInterface::class, function ($app) {
$client = new Client([
'hosts' => ['http://localhost:9200'],
]);
return new OpenSearchStore($client, 'vector_index');
});
}
First Use Case: Store and query embeddings in a Laravel controller:
use Symfony\Component\AI\Store\StoreInterface;
public function storeEmbedding(StoreInterface $store)
{
$embedding = [0.1, 0.2, ..., 0.768]; // Example 768-dim vector
$store->add('doc_id_1', ['embedding' => $embedding]);
}
public function findSimilar(StoreInterface $store)
{
$queryVector = [0.1, 0.2, ..., 0.768];
$results = $store->nearest($queryVector, limit: 5);
return $results;
}
add, nearest, remove).Embedding Workflows:
$store->add('document_123', [
'embedding' => $documentEmbedding,
'metadata' => ['title' => 'Example', 'content' => '...']
]);
$nearest = $store->nearest($queryEmbedding, limit: 3);
// Returns array of IDs with similarity scores
Hybrid Search: Combine keyword and vector search by extending the store:
public function hybridSearch(StoreInterface $store, string $keyword, array $vector)
{
$results = $store->nearest($vector, limit: 100);
$filtered = array_filter($results, function ($id) use ($keyword) {
// Fetch metadata from OpenSearch and filter by keyword
return str_contains($this->getMetadata($id)['title'], $keyword);
});
return array_slice($filtered, 0, 5);
}
Batch Operations: Use OpenSearch’s bulk API for efficiency:
public function bulkAdd(StoreInterface $store, array $embeddings)
{
$client = $store->getClient();
$bulkBody = [];
foreach ($embeddings as $id => $data) {
$bulkBody[] = ['index' => ['_index' => 'vector_index', '_id' => $id]];
$bulkBody[] = $data;
}
$client->bulk(['body' => $bulkBody]);
}
RAG Pipeline:
$context = $store->nearest($llmEmbedding, limit: 3);
$prompt = "Answer using these documents: " . implode("\n", $context);
Recommendation Engine:
$recommendations = $store->nearest($userEmbedding, limit: 10);
Semantic Search:
$queryEmbedding = $this->generateEmbedding($userQuery);
$results = $store->nearest($queryEmbedding);
Laravel Service Container: Bind the store as a singleton for global access:
$this->app->singleton(StoreInterface::class, function ($app) {
return new OpenSearchStore(
new Client(['hosts' => config('opensearch.hosts')]),
config('opensearch.index')
);
});
Configuration: Use Laravel config for OpenSearch settings:
// config/opensearch.php
return [
'hosts' => ['http://opensearch:9200'],
'index' => 'vector_index',
'dimension' => 768,
];
Error Handling: Wrap store operations in try-catch blocks to handle OpenSearch errors:
try {
$results = $store->nearest($vector);
} catch (\OpenSearch\Common\Exceptions\ClientException $e) {
\Log::error("OpenSearch query failed: " . $e->getMessage());
return [];
}
Testing: Use Laravel’s testing helpers to mock the store:
$store = Mockery::mock(StoreInterface::class);
$store->shouldReceive('nearest')
->once()
->andReturn(['id1', 'id2']);
OpenSearch Cluster Requirements:
knn_vector fields will fail.dimension or method (e.g., HNSW) in the mapping will cause errors.
// Wrong: dimension mismatch
"embedding": { "type": "knn_vector", "dimension": 384 } // but your vectors are 768D
Approximate vs. Exact Search:
knn_vector uses approximate search by default (faster but less precise). For exact search, set method: brute_force in the mapping (not recommended for high-dimensional vectors).engine: nmslib or hnsw for a balance of speed and accuracy.Rate Limiting:
search.max_buckets if needed.Laravel-Symfony Namespace Collisions:
// config/autoload.php
'aliases' => [
'Symfony\Component\AI' => 'SymfonyAI',
],
Data Migration:
Query DSL Issues:
_validate/query API to debug k-NN queries:
curl -X POST "http://localhost:9200/vector_index/_validate/query" -H 'Content-Type: application/json' -d'
{
"query": {
"knn": {
"embedding": {
"vector": [0.1, 0.2, ...],
"k": 5
}
}
}
}'
Mapping Errors:
curl -X GET "http://localhost:9200/vector_index/_mapping"
Performance Bottlenecks:
profile: true:
$params = [
'body'
How can I help you explore Laravel packages today?