symfony/ai-surreal-db-store
SurrealDB vector store integration for Symfony AI Store. Use SurrealDB’s vector indexing and search (MTREE/HNSW) to store embeddings and perform similarity queries, leveraging SurrealQL vector functions for retrieval in Symfony AI applications.
composer require symfony/ai surreal-db-store surreal-db/surrealdb
config/services.php:
'surrealdb' => [
'dsn' => 'http://user:pass@localhost:8000',
'namespace' => 'test',
'database' => 'test',
],
DEFINE INDEX vector_index ON table USING vector HNSW METRIC cosine DIMENSIONS 1536;
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(\Symfony\AI\Store\VectorStoreInterface::class, function ($app) {
$client = new \Surreal\Client($app['config']['surrealdb.dsn']);
$client->signin(['user', 'pass'], ['namespace', 'database']);
return new \Symfony\AI\Store\SurrealDbStore($client, 'vector_index', 'table');
});
}
$store = app(\Symfony\AI\Store\VectorStoreInterface::class);
$results = $store->find($queryEmbedding, limit: 5);
// Store embeddings
$store->add('doc1', $embeddingArray, ['type' => 'article']);
// Query
$results = $store->find($queryEmbedding, limit: 3, filter: ['type' => 'article']);
// Retrieve metadata
foreach ($results as $result) {
echo $result->getId(); // 'doc1'
echo $result->getMetadata()['type']; // 'article'
}
CRUD Operations:
add() or update() with metadata.
$store->add('user_123', $userEmbedding, ['role' => 'premium']);
remove() by ID or filter.
$store->remove('user_123'); // By ID
$store->removeWhere(['role' => 'premium']); // Bulk remove
INSERT statements (wrap in a transaction).Query Patterns:
$results = $store->find($queryEmbedding, limit: 10);
$results = $store->find($queryEmbedding, filter: [
'category' => 'tech',
'published' => true
]);
// Custom query via SurrealDB client
$client->query('SELECT * FROM table WHERE vector_similarity(embedding, ?) > 0.8 AND tags CONTAINS ?', [$queryEmbedding, 'ai']);
RAG Pipeline Integration:
// Retrieve context for LLM
$context = $store->find($queryEmbedding, limit: 3);
$prompt = "Answer based on: " . implode("\n", $context->getContents());
Service Layer Abstraction:
// app/Services/AI/VectorStoreService.php
class VectorStoreService {
public function __construct(private VectorStoreInterface $store) {}
public function search(string $query, array $filters = []): array {
$embedding = $this->generateEmbedding($query);
return $this->store->find($embedding, filter: $filters);
}
}
Event-Driven Updates:
// Listen to model events and update vectors
Model::updated(function ($model) {
$embedding = $this->generateEmbedding($model->content);
$store->update($model->id, $embedding, $model->metadata);
});
Caching Layer:
// Cache results for 5 minutes
$cacheKey = "vector_search:{$queryHash}";
$results = Cache::remember($cacheKey, 300, function () use ($store, $queryEmbedding) {
return $store->find($queryEmbedding);
});
Index Management:
DEFINE INDEX idx_name ON table USING vector HNSW METRIC cosine DIMENSIONS 1536;
INFO FOR INDEX idx_name;
Schema Design:
documents with embedding and metadata fields).CREATE TABLE documents;
ALTER TABLE documents ADD COLUMN embedding VECTOR DIMENSIONS 1536;
ALTER TABLE documents ADD COLUMN metadata MAP;
Connection Pooling:
$client = new \Surreal\Client($dsn);
$client->signin(['user', 'pass'], ['namespace', 'database']);
// Reuse $client in multiple store operations
Connection Handling:
signin before queries.
$client->signin(['user', 'pass'], ['namespace', 'database']); // Must call this!
booted event.Vector Dimension Mismatch:
DIMENSIONS in the index must match your embeddings.
-- Wrong: Will cause errors
DEFINE INDEX idx ON table USING vector HNSW METRIC cosine DIMENSIONS 384; -- But embeddings are 1536D
Filter Syntax:
CONTAINS, IN, etc., not SQL syntax.
// Wrong (SQL-style)
$store->find($embedding, filter: ['category = "tech"']);
// Correct (SurrealQL)
$store->find($embedding, filter: ['category' => 'tech']);
Rate Limiting:
try {
$results = $store->find($embedding);
} catch (\Surreal\Exception\RateLimitException $e) {
sleep(2 ** $attempts);
retry();
}
Metadata Serialization:
$store->add('id', $embedding, json_encode(['nested' => ['key' => 'value']]));
Query Logging: Enable SurrealDB query logging:
$client->setOption('log', true);
Or use Laravel’s logging:
\Log::debug('SurrealDB Query', ['query' => $client->getLastQuery()]);
Index Verification: Check if an index exists:
SHOW INDEXES ON table;
Performance Profiling: Compare raw SurrealQL vs. Symfony AI wrapper:
// Benchmark raw SurrealQL
$start = microtime(true);
$client->query('SELECT * FROM table WHERE vector_similarity(embedding, ?) > 0.8', [$embedding]);
$time = microtime(true) - $start;
Common Errors:
Invalid vector dimension: Mismatch between index and embedding size.Index not found: Verify index name/case sensitivity.Authentication failed: Check signin credentials/namespace.class CustomSurrealDbStore extends SurrealDbStore {
public function __construct(...) {
parent::__construct(..., 'dot_product');
}
How can I help you explore Laravel packages today?