symfony/ai-neo4j-store
Neo4j Store integration for Symfony AI Store, enabling use of Neo4j as a vector store with support for vector indexes. Includes links to Neo4j documentation and Symfony AI resources for contributing and reporting issues.
Install Dependencies
Add to composer.json:
{
"require": {
"symfony/ai-neo4j-store": "^0.8",
"neo4j/neo4j-php-client": "^5.0",
"symfony/ai": "^1.0"
}
}
Run composer update.
Configure Neo4j Ensure your Neo4j instance (5.12+) has a semantic vector index pre-configured. Example Cypher:
CREATE SEMANTIC INDEX `document_embeddings`
FOR (d:Document)
OPTIONS {
`vector.dimensions`: 1536,
`vector.similarity_function`: 'cosine'
};
Basic Laravel Integration
Bind the store in AppServiceProvider:
use Symfony\Component\AI\Store\Neo4jStore;
use Neo4j\ClientBuilder;
public function register()
{
$this->app->singleton(\Symfony\Component\AI\VectorStoreInterface::class, function ($app) {
$client = ClientBuilder::create()
->withUri(env('NEO4J_URI'))
->withBasicAuth(env('NEO4J_USER'), env('NEO4J_PASSWORD'))
->build();
return new Neo4jStore($client, 'Document');
});
}
First Use Case: Storing Embeddings
use Symfony\Component\AI\VectorStoreInterface;
$store = app(VectorStoreInterface::class);
$store->add(
id: 'doc_123',
vector: [0.1, 0.2, ..., 0.9], // Your embedding array
metadata: ['title' => 'Laravel AI', 'author' => 'Jane Doe']
);
Querying with Filters
$results = $store->similaritySearch(
query: [0.5, 0.6, ..., 0.4], // Query embedding
limit: 5,
filter: ['author' => 'Jane Doe'] // Neo4j property filter
);
Leverage Neo4j’s native graph traversals alongside vector search. Example:
// Find documents similar to query AND connected to a specific author
$results = $store->similaritySearch(
query: $queryEmbedding,
filter: [
'author' => 'Jane Doe',
'category' => 'tutorial'
]
);
// Under the hood, this translates to Cypher with MATCH clauses.
Use Laravel’s collect() + chunking for bulk inserts:
use Illuminate\Support\Collection;
$documents = collect($rawData)->chunk(100);
$documents->each(function ($chunk) use ($store) {
foreach ($chunk as $doc) {
$store->add(
id: $doc['id'],
vector: $doc['embedding'],
metadata: $doc['metadata']
);
}
});
Build a flexible search endpoint:
// routes/web.php
Route::get('/search', function (Request $request, VectorStoreInterface $store) {
$filter = $request->only(['author', 'category', 'year']);
$results = $store->similaritySearch(
query: $request->queryEmbedding,
filter: $filter
);
return response()->json($results);
});
Combine with Symfony AI’s EmbeddingGenerator:
use Symfony\Component\AI\EmbeddingGeneratorInterface;
$generator = app(EmbeddingGeneratorInterface::class);
$store = app(VectorStoreInterface::class);
$text = "Explain Laravel Eloquent";
$embedding = $generator->generate($text);
$results = $store->similaritySearch($embedding, limit: 3);
Extend Neo4j’s schema for complex queries:
// Add a relationship index for traversal
CREATE INDEX `doc_author_index` FOR ()-[r:WRITTEN_BY]->(author:Author)
Create a custom query builder for fluent syntax:
class Neo4jVectorQueryBuilder
{
public function whereAuthor(string $author): self
{
$this->filter['author'] = $author;
return $this;
}
public function whereCategory(string $category): self
{
$this->filter['category'] = $category;
return $this;
}
public function build(): array
{
return $this->filter;
}
}
// Usage:
$query = (new Neo4jVectorQueryBuilder())
->whereAuthor('Jane Doe')
->whereCategory('tutorial')
->build();
$results = $store->similaritySearch($embedding, filter: $query);
Cache frequent queries with Laravel’s cache:
$cacheKey = md5(serialize($queryEmbedding) . json_encode($filter));
$results = cache()->remember($cacheKey, now()->addHours(1), function () use ($store, $queryEmbedding, $filter) {
return $store->similaritySearch($queryEmbedding, filter: $filter);
});
Use Laravel events to sync data:
// In a service
event(new DocumentEmbeddingGenerated($documentId, $embedding));
// Listener
public function handle(DocumentEmbeddingGenerated $event, VectorStoreInterface $store)
{
$store->add(
id: $event->documentId,
vector: $event->embedding,
metadata: ['updated_at' => now()]
);
}
// app/Console/Commands/SetupNeo4jIndex.php
public function handle()
{
$client = ClientBuilder::create()->build();
$client->run('CREATE SEMANTIC INDEX IF NOT EXISTS `document_embeddings` FOR (d:Document) OPTIONS {`vector.dimensions`: 1536}');
}
filter: ['user.name' => 'John']filter: ['name' => 'John'] (for User node property)vector.dimensions: 768 (index) vs. embedding length 1536.add():
if (count($vector) !== config('neo4j.vector_dimensions')) {
throw new \InvalidArgumentException('Vector dimension mismatch');
}
similaritySearch) may timeout.$client = ClientBuilder::create()
->withConnectionTimeout(30)
->withMaxConnectionLifetime(60)
->build();
Metadata node with relationships.Add this to your AppServiceProvider:
$client = ClientBuilder::create()
->withLogging(function ($query, $params) {
\Log::debug("Neo4j Query: {$query}", $params);
})
->build();
Use the Neo4j Browser to test queries manually before integrating:
// Test a similarity search
CALL db.index.vector.queryNodes('document_embeddings', [0.1, 0.2, ...], 5)
YIELD node, score
RETURN node.title, score
Check index usage in Neo4j:
SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options;
Wrap the Neo4j store to add pre/post-processing:
class CachedNe
How can I help you explore Laravel packages today?