Installation
composer require bneumann/opensearch-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Bneumann\OpenSearchBundle\OpenSearchBundle::class => ['all' => true],
];
Basic Configuration
Define a client and index in config/packages/opensearch.yaml:
opensearch:
clients:
default:
hosts: ['https://localhost:9200']
username: '%env(OPENSEARCH_USER)%'
password: '%env(OPENSEARCH_PASS)%'
indexes:
products:
index_name: 'products_%kernel.environment%'
First Use Case: Indexing an Entity
Annotate your entity with @OpenSearch\Index:
use Bneumann\OpenSearchBundle\Annotation\Index;
#[Index(index: 'products')]
class Product {}
Sync the index via CLI:
php bin/console opensearch:sync
config/packages/opensearch.yaml (clients, indexes, mappings).src/Entity/ for @Index and @Field annotations.php bin/console opensearch: for index management.src/Repository/ for custom search queries.Entity Mapping Use annotations to define OpenSearch mappings:
#[Index(index: 'articles')]
class Article {
#[Field(type: 'text')]
private string $title;
#[Field(type: 'date')]
private \DateTimeInterface $publishedAt;
}
Serializer for complex transformations via opensearch.indexes.<name>.transformer: serializer.Search Queries
Create a custom repository extending OpenSearchRepository:
use Bneumann\OpenSearchBundle\Repository\OpenSearchRepository;
class ArticleRepository extends OpenSearchRepository {
public function findByTitle(string $title): array {
return $this->createQueryBuilder()
->boolQuery()
->must()
->match('title', $title)
->getResults();
}
}
createQueryBuilder() for fluent query construction.Blue/Green Reindexing
Configure in opensearch.yaml:
indexes:
products:
index_name: 'products_%kernel.environment%'
blue_green:
enabled: true
green_index: 'products_green'
php bin/console opensearch:reindex products
Event-Driven Extensibility
Listen to lifecycle events (e.g., IndexingEvent):
use Bneumann\OpenSearchBundle\Event\IndexingEvent;
$eventDispatcher->addListener(IndexingEvent::PRE_INDEX, function (IndexingEvent $event) {
$event->getDocument()->set('custom_field', 'value');
});
opensearch.yaml:
doctrine_sync:
enabled: true
events: [persist, update, remove]
TransformerInterface for non-standard data:
use Bneumann\OpenSearchBundle\Transformer\TransformerInterface;
class CustomTransformer implements TransformerInterface {
public function transform($entity): array {
return ['custom' => 'data'];
}
}
Register in config:
indexes:
products:
transformer: App\Transformer\CustomTransformer
indexes:
dynamic_products:
index_name: 'products_{id}'
template: true
template_body:
index_patterns: ['products_*']
settings: { ... }
SSL Verification
clients:
default:
ssl_verification: false
Index Naming Conflicts
index_name in config matches your @Index annotation or use index_name in the annotation to override:
#[Index(index: 'custom_products')]
class Product {}
%kernel.environment% in index names to avoid dev/prod collisions.Doctrine Sync Lag
doctrine_sync:
batch_size: 50
Field Mapping Overrides
ignore_annotations: true in config if needed:
indexes:
products:
mappings:
ignore_annotations: true
properties:
title: { type: 'keyword' }
Enable Logging
Add to config/packages/dev/opensearch.yaml:
logging: true
Logs appear in var/log/dev.log.
Check Index Status Use the CLI to verify index health:
php bin/console opensearch:status
Query Debugging Enable query logging in the repository:
$queryBuilder->setDebug(true);
Custom Clients
Implement ClientInterface for non-standard OpenSearch setups (e.g., AWS VPC endpoints):
use Bneumann\OpenSearchBundle\Client\ClientInterface;
class AwsVpcClient implements ClientInterface {
public function __construct(private string $endpoint) {}
public function getClient() { /* ... */ }
}
Register in config:
clients:
aws_vpc:
class: App\Client\AwsVpcClient
endpoint: '%env(AWS_OPENSEARCH_ENDPOINT)%'
Pre/Post Indexing Hooks
Extend IndexingEventSubscriber:
use Bneumann\OpenSearchBundle\Event\IndexingEvent;
class CustomIndexingSubscriber implements IndexingEventSubscriber {
public function onPreIndex(IndexingEvent $event) {
$event->getDocument()->set('processed_at', new \DateTime());
}
}
Register as a service with tag opensearch.indexing_subscriber.
Dynamic Index Management
Use the IndexManager service to create/delete indices programmatically:
$indexManager = $container->get('opensearch.index_manager');
$indexManager->create('dynamic_index', ['settings' => { ... }]);
How can I help you explore Laravel packages today?