jolicode/elastically

基于Elastica的具有偏见的框架,用于启动PHP和Elasticsearch实现。

v1.9.2 2024-08-12 09:56 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中有两个可用的提供程序:YamlProviderPhpProvider

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

由JoliCode赞助的开源时间。