symfony/ai-manticore-search-store
ManticoreSearch Store integrates ManticoreSearch as a vector store for Symfony AI Store, enabling KNN/vector similarity search backed by Manticore’s engine. Includes links to Manticore KNN docs plus Symfony AI contribution and issue resources.
Install Dependencies:
composer require symfony/ai manticoresearch/manticoresearch symfony/ai-manticore-search-store
Configure ManticoreSearch:
CREATE TABLE vectors (
id INT PRIMARY KEY,
embedding VECTOR(768) DISTANCE_L2,
metadata JSON
) ENGINE = Manticore;
Bind the Store in Laravel:
Add to config/app.php:
'providers' => [
// ...
Symfony\Component\AI\Bridge\Symfony\Store\ManticoreSearchStore::class,
],
First Use Case: Inject and use the store in a Laravel service:
use Symfony\Component\AI\Store\StoreInterface;
class VectorService {
public function __construct(private StoreInterface $store) {}
public function addVector(array $embedding, array $metadata) {
$this->store->add($embedding, ['metadata' => $metadata]);
}
public function findNearest(array $query, int $limit = 5) {
return $this->store->findNearest($query, $limit);
}
}
Vector Indexing:
// Add a single vector
$this->store->add($embeddingArray, ['id' => 123, 'type' => 'document']);
// Bulk add (if supported via custom extension)
$this->store->addMany([$embedding1, $embedding2], [$metadata1, $metadata2]);
KNN Queries with Filtering:
// Basic nearest neighbors
$results = $this->store->findNearest($queryEmbedding, 3);
// With metadata filtering (using query abstraction)
$query = new Query($queryEmbedding, 3);
$query->where('type', '=', 'document');
$results = $this->store->findNearest($query);
Vector Removal:
// Remove by ID
$this->store->remove(123);
// Bulk removal (if IDs are known)
$this->store->removeMany([123, 456]);
Laravel Service Binding:
Bind the store to Laravel’s container in AppServiceProvider:
public function register() {
$this->app->bind(
StoreInterface::class,
fn() => new ManticoreSearchStore(
new Manticore\Client('localhost', 9308),
'vectors'
)
);
}
Embedding Generation: Use Symfony AI’s embedder to generate vectors before storing:
use Symfony\Component\AI\Embedder\EmbedderInterface;
$embedding = $embeddingService->embed('Your text here');
$this->store->add($embedding, ['source' => 'user_input']);
Hybrid Search: Combine ManticoreSearch’s vector queries with Laravel’s Eloquent:
// Example: Fetch documents with metadata matching SQL conditions
$documents = DB::table('documents')
->where('category', 'tech')
->get();
$embeddings = array_map(fn($doc) => $doc->embedding, $documents);
$results = $this->store->findNearest($queryEmbedding, 5, $embeddings);
Batch Processing: For large datasets, use Laravel’s queues to process vectors asynchronously:
VectorBatchJob::dispatch($batchOfEmbeddings)->onQueue('vector-indexing');
Schema Mismatches:
768 for text-embedding-ada-002) match the ManticoreSearch table definition.if (count($embedding) !== 768) {
throw new \InvalidArgumentException('Embedding dimension mismatch');
}
Connection Issues:
manticoresearch/manticoresearch package version to match your server version.Filtering Limitations:
Query abstraction may not support all ManticoreSearch filter syntax. Complex filters might require raw SQL.executeQuery() for advanced filtering:
$results = $this->store->executeQuery(
'SELECT * FROM vectors WHERE metadata->>\'$.type\' = \'document\' ORDER BY embedding <-> ? LIMIT 5',
[$queryEmbedding]
);
Performance Bottlenecks:
ALTER TABLE vectors SET INDEXING THREADS = 4).Laravel-Symfony DI Conflicts:
StoreInterface may clash with Laravel’s autowiring if not properly bound.AppServiceProvider (as shown above).Enable ManticoreSearch Logging:
Add to manticore.conf:
log_level = 3
log_file = /var/log/manticore/search.log
Tail logs during queries:
tail -f /var/log/manticore/search.log
Query Profiling:
Use ManticoreSearch’s EXPLAIN to analyze query performance:
$explanation = $this->store->executeQuery('EXPLAIN SELECT * FROM vectors ORDER BY embedding <-> ? LIMIT 5', [$queryEmbedding]);
Laravel Debugging: Log store operations in a middleware:
public function handle($request, Closure $next) {
if ($request->is('vector/*')) {
\Log::info('Vector operation', ['query' => $request->query()]);
}
return $next($request);
}
Custom Query Builder:
Extend the Query class to support additional ManticoreSearch syntax:
class ExtendedQuery extends Query {
public function whereJsonPath(string $path, string $operator, $value) {
$this->query .= sprintf(" AND JSON_CONTAINS(metadata, '%s', '%s')", $path, $value);
}
}
Bulk Operations: Add missing bulk methods to the store:
class ManticoreSearchStore extends AbstractStore {
public function addMany(array $embeddings, array $metadatas) {
foreach ($embeddings as $i => $embedding) {
$this->add($embedding, $metadatas[$i]);
}
}
}
Hybrid Search: Create a decorator to combine vector and SQL results:
class HybridVectorStore implements StoreInterface {
public function __construct(
private StoreInterface $vectorStore,
private Connection $db
) {}
public function findNearest($query, int $limit = 5, array $filter = []) {
$vectorResults = $this->vectorStore->findNearest($query, $limit);
$sqlResults = $this->db->table('documents')
->where($filter)
->limit($limit)
->get();
return array_merge($vectorResults, $sqlResults);
}
}
Caching Layer: Cache frequent queries using Laravel’s cache:
public function findNearest($query, int $limit = 5) {
$cacheKey = md5(serialize($query));
return Cache::remember($cacheKey, now()->addMinutes(5), function() use ($query, $limit) {
return $this->store->findNearest($query, $limit);
});
}
Distance Metrics:
ManticoreSearch supports L2 (default), IP, or DOT_PRODUCT. Specify in the schema:
CREATE TABLE vectors (embedding VECTOR(768) DISTANCE_IP)
Connection Pooling: Reuse the ManticoreSearch client instance to avoid connection overhead:
$client = new Manticore\Client('localhost', 9308);
$store = new ManticoreSearchStore($client, 'vectors');
Environment Variables:
Store ManticoreSearch credentials in .env:
MANTOCORE_HOST=localhost
MANTOCORE
How can I help you explore Laravel packages today?