jolicode / elastically
基于Elastica的具有偏见的框架,用于启动PHP和Elasticsearch实现。
Requires
- php: >=8.0
- ext-json: *
- phpdocumentor/reflection-docblock: ^4.4|^5.0
- ruflin/elastica: ^7.0
- symfony/deprecation-contracts: ^2.4 || ^3.0
- symfony/property-access: ^5.4 || ^6.0 || ^7.0
- symfony/property-info: ^5.4 || ^6.0 || ^7.0
- symfony/serializer: ^5.4 || ^6.0 || ^7.0
- symfony/yaml: ^5.4 || ^6.0 || ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.17.0
- jane-php/json-schema: ^7.4
- jane-php/json-schema-runtime: ^7.4
- phpstan/phpstan: ^1.9
- symfony/browser-kit: ^5.4 || ^6.0
- symfony/framework-bundle: ^5.4.17 || ^6.0 || ^7.0
- symfony/http-client: ^5.4 || ^6.0 || ^7.0
- symfony/http-foundation: ^5.4 || ^6.0 || ^7.0
- symfony/messenger: ^5.4 || ^6.0 || ^7.0
- symfony/phpunit-bridge: ^6.2.3 || ^7.0
Conflicts
- nikic/php-parser: <4.7
This package is auto-updated.
Last update: 2024-09-13 08:12:39 UTC
README
基于Elastica的具有偏见的框架,用于启动PHP和Elasticsearch / OpenSearch实现。
主要功能
- DTO是第一类公民,您发送PHP对象作为文档,并在搜索结果中返回对象,就像ODM一样;
- 所有索引都是自动版本化和别名的;
- 映射通过YAML文件、PHP或通过
MappingProviderInterface
自定义完成; - 分析从映射中分离出来,以便于重用;
- 100%兼容ruflin/elastica;
- 具有ReIndex的映射迁移功能;
- 兼容Symphony HttpClient传输(可选);
- 支持Symphony(可选)
- 请参阅专门的章节;
- 已测试与Symfony 5.4到7;
- 支持Symphony Messenger Handler(带或不带spool);
重要
需要PHP 8.0+和Elasticsearch 7+。
与Elasticsearch 8+兼容,但尚未由Elastica官方支持。请谨慎使用。
与OpenSearch 1和2兼容。
安装
composer require jolicode/elastically
演示
提示
如果您正在使用Symfony,您可以转到章节;
快速示例:在Elastica之上库的工作原理
// Your own DTO, or one generated by Jane (see below) class Beer { public string $foo; public string $bar; } use JoliCode\Elastically\Factory; use JoliCode\Elastically\Model\Document; // Factory object with Elastica options + new Elastically options in the same array $factory = new Factory([ // Where to find the mappings Factory::CONFIG_MAPPINGS_DIRECTORY => __DIR__.'/mappings', // What objects to find in each index Factory::CONFIG_INDEX_CLASS_MAPPING => [ 'beers' => Beer::class, ], ]); // Class to perform request, same as the Elastica Client $client = $factory->buildClient(); // Class to build Indexes $indexBuilder = $factory->buildIndexBuilder(); // Create the Index in Elasticsearch $index = $indexBuilder->createIndex('beers'); // Set the proper aliases $indexBuilder->markAsLive($index, 'beers'); // Class to index DTO(s) in an Index $indexer = $factory->buildIndexer(); $dto = new Beer(); $dto->bar = 'American Pale Ale'; $dto->foo = 'Hops from Alsace, France'; // Add a document to the queue $indexer->scheduleIndex('beers', new Document('123', $dto)); $indexer->flush(); // Set parameters on the Bulk $indexer->setBulkRequestParams([ 'pipeline' => 'covfefe', 'refresh' => 'wait_for' ]); // Force index refresh if needed $indexer->refresh('beers'); // Get the Document (new!) $results = $client->getIndex('beers')->getDocument('123'); // Get the DTO (new!) $results = $client->getIndex('beers')->getModel('123'); // Perform a search $results = $client->getIndex('beers')->search('alsace'); // Get the Elastic Document $results->getDocuments()[0]; // Get the Elastica compatible Result $results->getResults()[0]; // Get the DTO 🎉 (new!) $results->getResults()[0]->getModel(); // Create a new version of the Index "beers" $index = $indexBuilder->createIndex('beers'); // Slow down the Refresh Interval of the new Index to speed up indexation $indexBuilder->slowDownRefresh($index); $indexBuilder->speedUpRefresh($index); // Set proper aliases $indexBuilder->markAsLive($index, 'beers'); // Clean the old indices (close the previous one and delete the older) $indexBuilder->purgeOldIndices('beers'); // Mapping change? Just call migrate and enjoy a full reindex (use the Task API internally to avoid timeout) $newIndex = $indexBuilder->migrate($index); $indexBuilder->speedUpRefresh($newIndex); $indexBuilder->markAsLive($newIndex, 'beers');
注意
scheduleIndex
在这里使用"beers"
索引,因为索引已经在之前创建。如果您正在创建一个新的索引并将文档索引到其中,则应直接传递Index
对象。
mappings/beers_mapping.yaml
# Anything you want, no validation settings: number_of_replicas: 1 number_of_shards: 1 refresh_interval: 60s mappings: dynamic: false properties: foo: type: text analyzer: english fields: keyword: type: keyword
配置
此库在Elastica之上添加了自定义配置
Factory::CONFIG_MAPPINGS_DIRECTORY
(必需,默认配置)
此目录是Elastically将要查找YAML的地方。
创建一个foobar
索引时,期望有一个foobar_mapping.yaml
文件。
如果存在analyzers.yaml
文件,则所有索引都会获得它。
Factory::CONFIG_INDEX_CLASS_MAPPING
(必需)
索引名称到类完全限定名的数组。
[ 'indexName' => My\AwesomeDTO::class, ]
Factory::CONFIG_MAPPINGS_PROVIDER
MappingProviderInterface
的一个实例。
如果没有定义此选项,则工厂将回退到YamlProvider
并使用Factory::CONFIG_MAPPINGS_DIRECTORY
选项。
Elastically中有两个可用的提供程序:YamlProvider
和PhpProvider
。
Factory::CONFIG_SERIALIZER
(可选)
用于索引时的兼容SerializerInterface
的对象。
默认为带有Object Normalizer的Symfony Serializer。
一个更快的替代方案是使用Jane生成纯PHP Normalizer,见下文。我们还建议自定义处理日期等问题。
Factory::CONFIG_DENORMALIZER
(可选)
一个与DenormalizerInterface
兼容的对象,将在搜索结果中用于构建您的对象。
如果此选项未定义,工厂将回退到Factory::CONFIG_SERIALIZER
选项。
Factory::CONFIG_SERIALIZER_CONTEXT_BUILDER
(可选)
一个ContextBuilderInterface
实例,用于从类名构建序列化上下文。
如果未定义,Elastically将使用具有Factory::CONFIG_SERIALIZER_CONTEXT_PER_CLASS
配置的StaticContextBuilder
。
Factory::CONFIG_SERIALIZER_CONTEXT_PER_CLASS
(可选)
允许指定规范化和解规范化时的序列化器上下文。
[ Beer::class => ['attributes' => ['title']], ];
默认为[]
。
Factory::CONFIG_BULK_SIZE
(可选)
在运行大量文档的索引时,此设置允许您调整文档阈值。
默认为100。
Factory::CONFIG_INDEX_PREFIX
(可选)
向通过Elastically创建的所有索引和别名添加前缀。
默认为null
。
在Symfony中的使用
配置
您需要在bundles.php
中添加捆绑包
// config/bundles.php return [ // ... JoliCode\Elastically\Bridge\Symfony\ElasticallyBundle::class => ['all' => true], ];
然后配置捆绑包
# config/packages/elastically.yaml elastically: connections: default: client: host: '%env(ELASTICSEARCH_HOST)%' # If you want to use the Symfony HttpClient (you MUST create this service) #transport: 'JoliCode\Elastically\Transport\HttpClientTransport' # Path to the mapping directory (in YAML) mapping_directory: '%kernel.project_dir%/config/elasticsearch' # Size of the bulk sent to Elasticsearch (default to 100) bulk_size: 100 # Mapping between an index name and a FQCN index_class_mapping: my-foobar-index: App\Dto\Foobar # Configuration for the serializer serializer: # Fill a static context context_mapping: foo: bar # If you want to add a prefix for your index in elasticsearch (you can still call it by its base name everywhere!) # prefix: '%kernel.environment%'
最后,在您需要它的代码中注入这些服务(自动可注入)之一
JoliCode\Elastically\Client (elastically.default.client)
JoliCode\Elastically\IndexBuilder (elastically.default.index_builder)
JoliCode\Elastically\Indexer (elastically.default.indexer)
高级配置
多个连接和自动注入
如果您定义了多个连接,您可以定义一个默认连接。这对于自动注入非常有用
elastically: default_connection: default connections: default: # ... another: # ...
要使用其他连接的类,您可以使用自动可注入类型。要发现它们,运行
bin/console debug:autowiring elastically
使用自定义序列化器上下文构建器
elastically: default_connection: default connections: default: serializer: context_builder_service: App\Elastically\Serializer\ContextBuilder # Do not define "context_mapping" option anymore
使用自定义映射提供者
elastically: default_connection: default connections: default: mapping_provider_service: App\Elastically\MappingProvider # Do not define "index_class_mapping" option anymore
使用HttpClient作为传输
您还可以使用Symfony HttpClient进行所有Elastica通信
JoliCode\Elastically\Transport\HttpClientTransport: ~ JoliCode\Elastically\Client: arguments: $config: host: '%env(ELASTICSEARCH_HOST)%' transport: 'JoliCode\Elastically\Transport\HttpClientTransport' # ...
参考
您可以通过以下命令获取默认配置参考
bin/console config:dump elastically
使用Messenger进行异步索引
Elastically随带默认的消息和处理器用于Symfony Messenger。
在您的配置中注册此消息
framework: messenger: transports: async: "%env(MESSENGER_TRANSPORT_DSN)%" routing: # async is whatever name you gave your transport above 'JoliCode\Elastically\Messenger\IndexationRequest': async services: JoliCode\Elastically\Messenger\IndexationRequestHandler: ~
IndexationRequestHandler
服务依赖于JoliCode\Elastically\Messenger\DocumentExchangerInterface
的实现,该实现不由此库提供。您必须提供一个实现此接口的服务,以便可以将您的数据库或任何其他事实来源连接。
然后从您的代码中调用
use JoliCode\Elastically\Messenger\IndexationRequest; use JoliCode\Elastically\Messenger\IndexationRequestHandler; $bus->dispatch(new IndexationRequest(Product::class, '1234567890')); // Third argument is the operation, so for a "delete" add this argument: // new IndexationRequest(Product::class, 'ref9999', IndexationRequestHandler::OP_DELETE);
然后消费这些消息
php bin/console messenger:consume async
将索引请求分组到spool中
在同一个Symfony请求期间发送多个IndexationRequest
并不总是合适的,它将触发多个批量操作。Elastically提供了一个Kernel监听器,将所有IndexationRequest
分组到单个MultipleIndexationRequest
消息中。
要使用此机制,我们发送IndexationRequest
到内存传输,以便在真正的异步传输中消费和分组
messenger: transports: async: "%env(MESSENGER_TRANSPORT_DSN)%" queuing: 'in-memory:///' routing: 'JoliCode\Elastically\Messenger\MultipleIndexationRequest': async 'JoliCode\Elastically\Messenger\IndexationRequest': queuing
您还需要注册订阅者
services: JoliCode\Elastically\Messenger\IndexationRequestSpoolSubscriber: arguments: - '@messenger.transport.queuing' # should be the name of the memory transport - '@messenger.default_bus' tags: - { name: kernel.event_subscriber }
使用Jane构建PHP DTO和快速规范化器
安装JanePHP json-schema工具来构建自己的DTO和规范化器。您需要做的只是设置Jane-completed序列化器到Factory
$factory = new Factory([ Factory::CONFIG_SERIALIZER => $serializer, ]);
注意
Elastically不兼容于Jane < 6。
待办事项
- 代码中的某些“待办”事项
- 可选的Doctrine连接器
- 更好的记录器 - 可能通过处理器?扩展_log预计将被弃用 :(
- 额外的命令来监控、更新映射、重新索引等常见任务
- 可选的Symfony集成
- 网络调试工具栏!
- 用于常见任务的脚本/命令
- 当映射更改时自动重新索引,处理别名等
- 集群/索引的微监控
- 健康检查方法
赞助商
由JoliCode赞助的开源时间。