inm39 / mariadb-vector-bundle
Symfony bundle providing Doctrine DBAL type and DQL functions for MariaDB Vector (VECTOR type, VEC_DISTANCE_*, semantic search). Requires MariaDB >= 11.7.
Package info
github.com/IMAMx39/mariadb-vector-bundle
Type:symfony-bundle
pkg:composer/inm39/mariadb-vector-bundle
Requires
- php: >=8.1
- doctrine/dbal: ^3.7 || ^4.0
- doctrine/orm: ^2.16 || ^3.0
- symfony/config: ^6.4 || ^7.0
- symfony/dependency-injection: ^6.4 || ^7.0
- symfony/http-kernel: ^6.4 || ^7.0
Requires (Dev)
- doctrine/doctrine-bundle: ^2.11
- phpunit/phpunit: ^10.5
Suggests
- doctrine/doctrine-bundle: For automatic registration of the DBAL type and DQL functions
Provides
None
Conflicts
None
Replaces
None
README
Symfony bundle for MariaDB Vector (MariaDB ≥ 11.7 / 11.8 LTS): Doctrine vector DBAL type, VEC_* DQL functions and a repository trait for semantic / nearest-neighbour search.
Bring RAG and semantic search to your existing MariaDB — no extra vector database needed.
Features
- ✅
vectorDoctrine DBAL type — mapVECTOR(N)columns to plain PHPfloat[] - ✅ DQL functions:
VEC_DISTANCE,VEC_DISTANCE_COSINE,VEC_DISTANCE_EUCLIDEAN,VEC_FROMTEXT,VEC_TOTEXT - ✅
VectorSearchTraitfor repositories:findNearest()in one line - ✅ Zero configuration — the bundle prepends everything into DoctrineBundle
Requirements
- PHP ≥ 8.1
- Symfony 6.4 / 7.x
- doctrine/orm ≥ 2.16 or 3.x
- MariaDB ≥ 11.7 (11.8 LTS recommended)
Installation
composer require inm39/mariadb-vector-bundle
This bundle has no Symfony Flex recipe, so you need to register it by hand:
// config/bundles.php return [ // ... INM39\MariadbVectorBundle\MariadbVectorBundle::class => ['all' => true], ];
No config/packages/mariadb_vector.yaml is needed — see Configuration below, it's only required if you want to override the default.
Usage
1. Entity
use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: DocumentRepository::class)] class Document { #[ORM\Id, ORM\GeneratedValue, ORM\Column] private ?int $id = null; #[ORM\Column(type: 'text')] private string $content; /** @var list<float> — `length` is the vector dimension */ #[ORM\Column(type: 'vector', length: 768)] private array $embedding = []; // getters/setters... }
2. Migration — add the VECTOR INDEX manually
The schema tool generates the VECTOR(768) column, but the vector index must be added by hand (one per table, column must be NOT NULL):
public function up(Schema $schema): void { $this->addSql('ALTER TABLE document ADD VECTOR INDEX (embedding) DISTANCE=cosine M=8'); }
3. Repository search
use INM39\MariadbVectorBundle\Repository\VectorSearchTrait; class DocumentRepository extends ServiceEntityRepository { use VectorSearchTrait; }
// $queryVector: float[] from your embedding model (Ollama, TEI, OpenAI...) $results = $documentRepository->findNearest('embedding', $queryVector, limit: 5); // With distances: foreach ($documentRepository->findNearestWithDistance('embedding', $queryVector) as $row) { $document = $row[0]; $distance = $row['distance']; }
4. Or raw DQL
$documents = $em->createQuery( 'SELECT d, VEC_DISTANCE_COSINE(d.embedding, VEC_FROMTEXT(:vec)) AS HIDDEN dist FROM App\Entity\Document d ORDER BY dist ASC' ) ->setParameter('vec', json_encode($queryVector)) ->setMaxResults(10) ->getResult();
⚠️ Performance notes (index usage)
MariaDB uses the vector index only when the query is:
ORDER BY VEC_DISTANCE_*(column, vector) ASC LIMIT n
on the bare distance call (or its alias). These patterns fall back to a full table scan:
- Wrapping the distance in an expression (
1 - VEC_DISTANCE_COSINE(...)) → compute the similarity score in an outer query instead. WHERE VEC_DISTANCE(...) < thresholdwithoutORDER BY ... LIMIT.- Using a distance function that doesn't match the index metric (
DISTANCE=cosinevseuclidean).
Configuration
Default config (nothing to do):
# config/packages/mariadb_vector.yaml mariadb_vector: register_dql_functions: true
Set register_dql_functions: false if you use multiple entity managers and prefer registering the DQL functions yourself per-manager.
Generating embeddings
This bundle does not generate embeddings — pair it with any model. Example with Ollama (nomic-embed-text:v1.5):
$response = $httpClient->request('POST', 'http://localhost:11434/api/embeddings', [ 'json' => [ 'model' => 'nomic-embed-text:v1.5', 'prompt' => 'search_document: ' . $content, ], ]); $embedding = $response->toArray()['embedding']; // float[768]
Running tests
composer install vendor/bin/phpunit
License
MIT