Update elasticsearch-php package to version 8

This commit is contained in:
DariusIII
2026-03-27 14:14:22 +01:00
parent adcf55f6ac
commit c9a9dacf6e
14 changed files with 795 additions and 353 deletions
+20 -12
View File
@@ -4,9 +4,11 @@ declare(strict_types=1);
namespace App\Console\Commands;
use Elasticsearch\ClientBuilder;
use App\Services\Search\Support\ElasticsearchClientFactory;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client as ElasticsearchClient;
use Illuminate\Console\Command;
use Manticoresearch\Client;
use Manticoresearch\Client as ManticoreClient;
use Manticoresearch\Exceptions\ResponseException;
class CreateMediaIndexes extends Command
@@ -54,7 +56,7 @@ class CreateMediaIndexes extends Command
$host = config('search.drivers.manticore.host', '127.0.0.1');
$port = config('search.drivers.manticore.port', 9308);
$client = new Client([
$client = new ManticoreClient([
'host' => $host,
'port' => $port,
]);
@@ -147,7 +149,7 @@ class CreateMediaIndexes extends Command
*
* @param array<string, mixed> $schema
*/
private function createManticoreIndex(Client $client, string $indexName, array $schema, bool $dropExisting): bool
private function createManticoreIndex(ManticoreClient $client, string $indexName, array $schema, bool $dropExisting): bool
{
try {
// Check if index exists
@@ -266,20 +268,22 @@ class CreateMediaIndexes extends Command
];
try {
// Create Elasticsearch client directly
$esConfig = config('search.drivers.elasticsearch');
$esClient = ClientBuilder::create()
->setHosts($esConfig['hosts'] ?? [['host' => 'localhost', 'port' => 9200]])
->build();
/** @var ElasticsearchClient $esClient */
$esClient = ElasticsearchClientFactory::make($esConfig ?? []);
// Create movies index
$moviesIndex = config('search.drivers.elasticsearch.indexes.movies', 'movies');
if ($dropExisting && $esClient->indices()->exists(['index' => $moviesIndex])) {
if ($dropExisting && (ElasticsearchResponseHelper::boolResponse($esClient, function (ElasticsearchClient $client) use ($moviesIndex) {
return $client->indices()->exists(['index' => $moviesIndex]);
}))) {
$this->warn("Dropping existing index: {$moviesIndex}");
$esClient->indices()->delete(['index' => $moviesIndex]);
}
if (! $esClient->indices()->exists(['index' => $moviesIndex])) {
if (! (ElasticsearchResponseHelper::boolResponse($esClient, function (ElasticsearchClient $client) use ($moviesIndex) {
return $client->indices()->exists(['index' => $moviesIndex]);
}))) {
$this->info("Creating index: {$moviesIndex}");
$esClient->indices()->create([
'index' => $moviesIndex,
@@ -292,12 +296,16 @@ class CreateMediaIndexes extends Command
// Create tvshows index
$tvshowsIndex = config('search.drivers.elasticsearch.indexes.tvshows', 'tvshows');
if ($dropExisting && $esClient->indices()->exists(['index' => $tvshowsIndex])) {
if ($dropExisting && (ElasticsearchResponseHelper::boolResponse($esClient, function (ElasticsearchClient $client) use ($tvshowsIndex) {
return $client->indices()->exists(['index' => $tvshowsIndex]);
}))) {
$this->warn("Dropping existing index: {$tvshowsIndex}");
$esClient->indices()->delete(['index' => $tvshowsIndex]);
}
if (! $esClient->indices()->exists(['index' => $tvshowsIndex])) {
if (! (ElasticsearchResponseHelper::boolResponse($esClient, function (ElasticsearchClient $client) use ($tvshowsIndex) {
return $client->indices()->exists(['index' => $tvshowsIndex]);
}))) {
$this->info("Creating index: {$tvshowsIndex}");
$esClient->indices()->create([
'index' => $tvshowsIndex,
+5 -1
View File
@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace App\Console\Commands;
use App\Facades\Elasticsearch;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client as ElasticsearchClient;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
@@ -68,7 +70,9 @@ class NntmuxCheckIndex extends Command
{
try {
// Check if index exists
$exists = Elasticsearch::indices()->exists(['index' => $index]);
/** @var ElasticsearchClient $client */
$client = app('elasticsearch');
$exists = ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => $index]));
if (! $exists) {
$this->error("ElasticSearch index '{$index}' does not exist.");
@@ -5,6 +5,8 @@ declare(strict_types=1);
namespace App\Console\Commands;
use App\Facades\Elasticsearch;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client;
use Illuminate\Console\Command;
class NntmuxCreateESIndexes extends Command
@@ -28,7 +30,10 @@ class NntmuxCreateESIndexes extends Command
*/
public function handle(): void
{
if (Elasticsearch::indices()->exists(['index' => 'releases'])) {
/** @var Client $client */
$client = app('elasticsearch');
if (ElasticsearchResponseHelper::boolResponse($client, fn (Client $elasticClient) => $elasticClient->indices()->exists(['index' => 'releases']))) {
Elasticsearch::indices()->delete(['index' => 'releases']);
}
$releases_index = [
@@ -110,7 +115,7 @@ class NntmuxCreateESIndexes extends Command
$response = Elasticsearch::indices()->create($releases_index);
$this->info('Index releases created successfully');
if (Elasticsearch::indices()->exists(['index' => 'predb'])) {
if (ElasticsearchResponseHelper::boolResponse($client, fn (Client $elasticClient) => $elasticClient->indices()->exists(['index' => 'predb']))) {
Elasticsearch::indices()->delete(['index' => 'predb']);
}
$predb_index = [
@@ -8,6 +8,8 @@ use App\Facades\Elasticsearch;
use App\Facades\Search;
use App\Models\Predb;
use App\Models\Release;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client as ElasticsearchClient;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
@@ -290,7 +292,9 @@ class NntmuxOffsetPopulate extends Command
} else {
// For ElasticSearch, just clear the data instead of recreating the index
try {
$exists = Elasticsearch::indices()->exists(['index' => $index]);
/** @var ElasticsearchClient $client */
$client = app('elasticsearch');
$exists = ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => $index]));
if ($exists) {
// Get current document count
+9 -4
View File
@@ -7,6 +7,8 @@ namespace App\Console\Commands;
use App\Facades\Elasticsearch;
use App\Facades\Search;
use App\Models\UsenetGroup;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client as ElasticsearchClient;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
@@ -94,7 +96,10 @@ class NntmuxResetDb extends Command
unset($value);
if (config('search.default') === 'elasticsearch') {
if (Elasticsearch::indices()->exists(['index' => 'releases'])) {
/** @var ElasticsearchClient $client */
$client = app('elasticsearch');
if (ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => 'releases']))) {
Elasticsearch::indices()->delete(['index' => 'releases']);
}
$releases_index = [
@@ -138,7 +143,7 @@ class NntmuxResetDb extends Command
Elasticsearch::indices()->create($releases_index);
if (Elasticsearch::indices()->exists(['index' => 'predb'])) {
if (ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => 'predb']))) {
Elasticsearch::indices()->delete(['index' => 'predb']);
}
$predb_index = [
@@ -173,7 +178,7 @@ class NntmuxResetDb extends Command
Elasticsearch::indices()->create($predb_index);
// Delete and recreate movies index
if (Elasticsearch::indices()->exists(['index' => 'movies'])) {
if (ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => 'movies']))) {
Elasticsearch::indices()->delete(['index' => 'movies']);
}
$movies_index = [
@@ -214,7 +219,7 @@ class NntmuxResetDb extends Command
Elasticsearch::indices()->create($movies_index);
// Delete and recreate tvshows index
if (Elasticsearch::indices()->exists(['index' => 'tvshows'])) {
if (ElasticsearchResponseHelper::boolResponse($client, fn (ElasticsearchClient $elasticClient) => $elasticClient->indices()->exists(['index' => 'tvshows']))) {
Elasticsearch::indices()->delete(['index' => 'tvshows']);
}
$tvshows_index = [
@@ -6,6 +6,8 @@ namespace App\Console\Commands;
use App\Facades\Elasticsearch;
use App\Models\Release;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client as ElasticsearchClient;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -50,6 +52,8 @@ class UpdateReleasesIndexSchemaES extends Command
'movieinfo_id' => ['type' => 'integer'],
];
private ?ElasticsearchClient $client = null;
/**
* Execute the console command.
*/
@@ -108,6 +112,19 @@ class UpdateReleasesIndexSchemaES extends Command
return $result;
}
private function elasticsearchClient(): ElasticsearchClient
{
if ($this->client instanceof ElasticsearchClient) {
return $this->client;
}
/** @var ElasticsearchClient $client */
$client = app('elasticsearch');
$this->client = $client;
return $this->client;
}
/**
* Show current index schema information
*/
@@ -119,7 +136,7 @@ class UpdateReleasesIndexSchemaES extends Command
try {
$indexName = config('search.drivers.elasticsearch.indexes.releases', 'releases');
if (! Elasticsearch::indices()->exists(['index' => $indexName])) {
if (! ElasticsearchResponseHelper::boolResponse($this->elasticsearchClient(), fn (ElasticsearchClient $client) => $client->indices()->exists(['index' => $indexName]))) {
$this->warn("Index '{$indexName}' does not exist.");
$this->info('Run `php artisan nntmux:create-es-indexes` to create the index first.');
@@ -206,7 +223,7 @@ class UpdateReleasesIndexSchemaES extends Command
try {
$indexName = config('search.drivers.elasticsearch.indexes.releases', 'releases');
if (! Elasticsearch::indices()->exists(['index' => $indexName])) {
if (! ElasticsearchResponseHelper::boolResponse($this->elasticsearchClient(), fn (ElasticsearchClient $client) => $client->indices()->exists(['index' => $indexName]))) {
$this->error("Index '{$indexName}' does not exist. Create it first with: php artisan nntmux:create-es-indexes");
return Command::FAILURE;
@@ -287,7 +304,7 @@ class UpdateReleasesIndexSchemaES extends Command
try {
// Delete existing index if it exists
if (Elasticsearch::indices()->exists(['index' => $indexName])) {
if (ElasticsearchResponseHelper::boolResponse($this->elasticsearchClient(), fn (ElasticsearchClient $client) => $client->indices()->exists(['index' => $indexName]))) {
$this->info("Deleting existing '{$indexName}' index...");
Elasticsearch::indices()->delete(['index' => $indexName]);
}
@@ -411,7 +428,7 @@ class UpdateReleasesIndexSchemaES extends Command
$indexName = config('search.drivers.elasticsearch.indexes.releases', 'releases');
// Check if index exists
if (! Elasticsearch::indices()->exists(['index' => $indexName])) {
if (! ElasticsearchResponseHelper::boolResponse($this->elasticsearchClient(), fn (ElasticsearchClient $client) => $client->indices()->exists(['index' => $indexName]))) {
$this->error("Index '{$indexName}' does not exist. Create it first.");
return Command::FAILURE;
+7 -7
View File
@@ -4,18 +4,18 @@ declare(strict_types=1);
namespace App\Facades;
use Elasticsearch\Client;
use Elastic\Elasticsearch\Client;
use Illuminate\Support\Facades\Facade;
/**
* Elasticsearch Facade - provides static access to Elasticsearch client.
*
* @method static \Elasticsearch\Namespaces\IndicesNamespace indices()
* @method static \Elasticsearch\Namespaces\ClusterNamespace cluster()
* @method static array search(array $params)
* @method static array bulk(array $params)
* @method static array get(array $params)
* @method static array deleteByQuery(array $params)
* @method static \Elastic\Elasticsearch\Endpoints\Indices indices()
* @method static \Elastic\Elasticsearch\Endpoints\Cluster cluster()
* @method static \Elastic\Elasticsearch\Response\Elasticsearch search(array $params)
* @method static \Elastic\Elasticsearch\Response\Elasticsearch bulk(array $params)
* @method static \Elastic\Elasticsearch\Response\Elasticsearch get(array $params)
* @method static \Elastic\Elasticsearch\Response\Elasticsearch deleteByQuery(array $params)
*
* @see Client
*/
+3 -6
View File
@@ -10,8 +10,8 @@ use App\Services\Search\Drivers\ElasticSearchDriver;
use App\Services\Search\Drivers\ManticoreSearchDriver;
use App\Services\Search\MediaSearchService;
use App\Services\Search\SearchService;
use Elasticsearch\Client;
use Elasticsearch\ClientBuilder;
use App\Services\Search\Support\ElasticsearchClientFactory;
use Elastic\Elasticsearch\Client;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;
@@ -63,11 +63,8 @@ class SearchServiceProvider extends ServiceProvider
$this->app->singleton('elasticsearch', function (): Client {
$config = config('search.drivers.elasticsearch', []);
$clientBuilder = ClientBuilder::create();
$clientBuilder->setHosts($config['hosts'] ?? []);
$clientBuilder->setRetries($config['retries'] ?? 2);
return $clientBuilder->build();
return ElasticsearchClientFactory::make($config);
});
// Register aliases (using prefixed names to avoid conflicts with other packages)
@@ -8,11 +8,10 @@ use App\Models\MovieInfo;
use App\Models\Release;
use App\Models\Video;
use App\Services\Search\Contracts\SearchDriverInterface;
use Elasticsearch\Client;
use Elasticsearch\ClientBuilder;
use Elasticsearch\Common\Exceptions\ElasticsearchException;
use Elasticsearch\Common\Exceptions\Missing404Exception;
use GuzzleHttp\Ring\Client\CurlHandler;
use App\Services\Search\Support\ElasticsearchClientFactory;
use App\Services\Search\Support\ElasticsearchResponseHelper;
use Elastic\Elasticsearch\Client;
use Elastic\Elasticsearch\Exception\ElasticsearchException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
@@ -91,38 +90,17 @@ class ElasticSearchDriver implements SearchDriverInterface
{
if (self::$client === null) {
try {
if (! extension_loaded('curl')) {
throw new RuntimeException('cURL extension is not loaded');
}
if (empty($this->config)) {
throw new RuntimeException('Elasticsearch configuration not found');
}
$clientBuilder = ClientBuilder::create();
$hosts = $this->buildHostsArray($this->config['hosts'] ?? []);
if (empty($hosts)) {
throw new RuntimeException('No Elasticsearch hosts configured');
}
if (config('app.debug')) {
Log::debug('Elasticsearch client initializing', [
'hosts' => $hosts,
'hosts' => ElasticsearchClientFactory::buildHosts($this->config['hosts'] ?? []),
]);
}
$clientBuilder->setHosts($hosts);
$clientBuilder->setHandler(new CurlHandler);
$clientBuilder->setConnectionParams([
'timeout' => $this->config['timeout'] ?? self::DEFAULT_TIMEOUT,
'connect_timeout' => $this->config['connect_timeout'] ?? self::DEFAULT_CONNECT_TIMEOUT,
]);
// Enable retries for better resilience
$clientBuilder->setRetries($this->config['retries'] ?? 2);
self::$client = $clientBuilder->build();
self::$client = ElasticsearchClientFactory::make($this->config);
if (config('app.debug')) {
Log::debug('Elasticsearch client initialized successfully');
@@ -139,43 +117,10 @@ class ElasticSearchDriver implements SearchDriverInterface
return self::$client;
}
/**
* Build hosts array from configuration.
*
* @param array<string, mixed> $configHosts Configuration hosts array
* @return array<string, mixed> Formatted hosts array for Elasticsearch client
*/
private function buildHostsArray(array $configHosts): array
{
$hosts = [];
foreach ($configHosts as $host) {
$hostConfig = [
'host' => $host['host'] ?? 'localhost',
'port' => $host['port'] ?? 9200,
];
if (! empty($host['scheme'])) {
$hostConfig['scheme'] = $host['scheme'];
}
if (! empty($host['user']) && ! empty($host['pass'])) {
$hostConfig['user'] = $host['user'];
$hostConfig['pass'] = $host['pass'];
}
$hosts[] = $hostConfig;
}
return $hosts;
}
/**
* Check if Elasticsearch is available.
*
* Caches the availability status to avoid frequent ping requests.
*
* @return list<array<string, mixed>>
*/
private function isElasticsearchAvailable(): bool
{
@@ -190,7 +135,7 @@ class ElasticSearchDriver implements SearchDriverInterface
try {
$client = $this->getClient();
$result = $client->ping();
$result = ElasticsearchResponseHelper::boolResponse($client, fn (Client $elasticClient) => $elasticClient->ping());
if (config('app.debug')) {
Log::debug('Elasticsearch ping result', ['available' => $result]);
@@ -1268,16 +1213,23 @@ class ElasticSearchDriver implements SearchDriverInterface
'id' => $id,
]);
} catch (Missing404Exception $e) {
// Document already deleted, not an error
if (config('app.debug')) {
Log::debug('ElasticSearch deleteRelease: document not found', ['release_id' => $id]);
}
} catch (ElasticsearchException $e) {
Log::error('ElasticSearch deleteRelease error: '.$e->getMessage(), [
'release_id' => $id,
]);
} catch (\Throwable $e) {
if (ElasticsearchResponseHelper::isNotFound($e)) {
if (config('app.debug')) {
Log::debug('ElasticSearch deleteRelease: document not found', ['release_id' => $id]);
}
return;
}
if ($e instanceof ElasticsearchException) {
Log::error('ElasticSearch deleteRelease error: '.$e->getMessage(), [
'release_id' => $id,
]);
return;
}
Log::error('ElasticSearch deleteRelease unexpected error: '.$e->getMessage(), [
'release_id' => $id,
]);
@@ -1306,15 +1258,23 @@ class ElasticSearchDriver implements SearchDriverInterface
'id' => $id,
]);
} catch (Missing404Exception $e) {
if (config('app.debug')) {
Log::debug('ElasticSearch deletePreDb: document not found', ['predb_id' => $id]);
}
} catch (ElasticsearchException $e) {
Log::error('ElasticSearch deletePreDb error: '.$e->getMessage(), [
'predb_id' => $id,
]);
} catch (\Throwable $e) {
if (ElasticsearchResponseHelper::isNotFound($e)) {
if (config('app.debug')) {
Log::debug('ElasticSearch deletePreDb: document not found', ['predb_id' => $id]);
}
return;
}
if ($e instanceof ElasticsearchException) {
Log::error('ElasticSearch deletePreDb error: '.$e->getMessage(), [
'predb_id' => $id,
]);
return;
}
Log::error('ElasticSearch deletePreDb unexpected error: '.$e->getMessage(), [
'predb_id' => $id,
]);
@@ -1474,7 +1434,7 @@ class ElasticSearchDriver implements SearchDriverInterface
try {
$client = $this->getClient();
return $client->indices()->exists(['index' => $index]);
return ElasticsearchResponseHelper::boolResponse($client, fn (Client $elasticClient) => $elasticClient->indices()->exists(['index' => $index]));
} catch (\Throwable $e) {
Log::error('ElasticSearch indexExists error: '.$e->getMessage(), ['index' => $index]);
@@ -1501,7 +1461,7 @@ class ElasticSearchDriver implements SearchDriverInterface
$client = $this->getClient();
// Check if index exists
if (! $client->indices()->exists(['index' => $index])) {
if (! ElasticsearchResponseHelper::boolResponse($client, fn (Client $elasticClient) => $elasticClient->indices()->exists(['index' => $index]))) {
Log::info("ElasticSearch truncateIndex: index {$index} does not exist, skipping");
continue;
@@ -1577,7 +1537,7 @@ class ElasticSearchDriver implements SearchDriverInterface
try {
$client = $this->getClient();
return $client->cluster()->health();
return ElasticsearchResponseHelper::asArray($client->cluster()->health());
} catch (\Throwable $e) {
Log::error('ElasticSearch getClusterHealth error: '.$e->getMessage());
@@ -1600,7 +1560,7 @@ class ElasticSearchDriver implements SearchDriverInterface
try {
$client = $this->getClient();
return $client->indices()->stats(['index' => $index]);
return ElasticsearchResponseHelper::asArray($client->indices()->stats(['index' => $index]));
} catch (\Throwable $e) {
Log::error('ElasticSearch getIndexStats error: '.$e->getMessage(), ['index' => $index]);
@@ -1965,12 +1925,16 @@ class ElasticSearchDriver implements SearchDriverInterface
'index' => $this->getMoviesIndex(),
'id' => $id,
]);
} catch (Missing404Exception $e) {
// Document doesn't exist, that's fine
} catch (ElasticsearchException $e) {
Log::error('ElasticSearch deleteMovie error: '.$e->getMessage(), [
'id' => $id,
]);
} catch (\Throwable $e) {
if (ElasticsearchResponseHelper::isNotFound($e)) {
return;
}
if ($e instanceof ElasticsearchException) {
Log::error('ElasticSearch deleteMovie error: '.$e->getMessage(), [
'id' => $id,
]);
}
}
}
@@ -2247,12 +2211,16 @@ class ElasticSearchDriver implements SearchDriverInterface
'index' => $this->getTvShowsIndex(),
'id' => $id,
]);
} catch (Missing404Exception $e) {
// Document doesn't exist, that's fine
} catch (ElasticsearchException $e) {
Log::error('ElasticSearch deleteTvShow error: '.$e->getMessage(), [
'id' => $id,
]);
} catch (\Throwable $e) {
if (ElasticsearchResponseHelper::isNotFound($e)) {
return;
}
if ($e instanceof ElasticsearchException) {
Log::error('ElasticSearch deleteTvShow error: '.$e->getMessage(), [
'id' => $id,
]);
}
}
}
@@ -2513,7 +2481,6 @@ class ElasticSearchDriver implements SearchDriverInterface
*
* @param array<string, mixed> $categoryIds Array of category IDs to filter by
* @param int $limit Maximum number of results
* @return list
* @return array<string, mixed>
*/
public function searchReleasesByCategory(array $categoryIds, int $limit = 1000): array
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace App\Services\Search\Support;
use Elastic\Elasticsearch\Client;
use Elastic\Elasticsearch\ClientBuilder;
use GuzzleHttp\Client as GuzzleClient;
use RuntimeException;
final class ElasticsearchClientFactory
{
/**
* @param array<string, mixed> $config
*/
public static function make(array $config): Client
{
$hosts = self::buildHosts($config['hosts'] ?? []);
if ($hosts === []) {
throw new RuntimeException('No Elasticsearch hosts configured');
}
$builder = ClientBuilder::create()
->setHosts($hosts)
->setRetries((int) ($config['retries'] ?? 2));
$builder->setHttpClient(new GuzzleClient(self::buildHttpClientOptions($config)));
return $builder->build();
}
/**
* @param array<int, array<string, mixed>|string> $configHosts
* @return list<string>
*/
public static function buildHosts(array $configHosts): array
{
$hosts = [];
foreach ($configHosts as $host) {
if (is_string($host) && trim($host) !== '') {
$hosts[] = $host;
continue;
}
if (! is_array($host)) {
continue;
}
$hostname = trim((string) ($host['host'] ?? ''));
if ($hostname === '') {
continue;
}
$scheme = trim((string) ($host['scheme'] ?? 'http')) ?: 'http';
$port = $host['port'] ?? null;
$portSuffix = $port !== null && $port !== '' ? ':'.(string) $port : '';
$credentials = '';
if (! empty($host['user']) && ! empty($host['pass'])) {
$credentials = rawurlencode((string) $host['user']).':'.rawurlencode((string) $host['pass']).'@';
}
$hosts[] = sprintf('%s://%s%s%s', $scheme, $credentials, $hostname, $portSuffix);
}
return $hosts;
}
/**
* @param array<string, mixed> $config
* @return array<string, int|float|string|bool>
*/
public static function buildHttpClientOptions(array $config): array
{
$options = [];
$timeout = $config['timeout'] ?? null;
if (is_numeric($timeout) && (float) $timeout > 0) {
$options['timeout'] = (float) $timeout;
}
$connectTimeout = $config['connect_timeout'] ?? null;
if (is_numeric($connectTimeout) && (float) $connectTimeout > 0) {
$options['connect_timeout'] = (float) $connectTimeout;
}
return $options;
}
}
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Services\Search\Support;
use Elastic\Elasticsearch\Client;
use Elastic\Elasticsearch\Exception\ClientResponseException;
use Elastic\Elasticsearch\Response\Elasticsearch as ElasticsearchResponse;
use Throwable;
final class ElasticsearchResponseHelper
{
/**
* @param array<string, mixed>|ElasticsearchResponse $response
* @return array<string, mixed>
*/
public static function asArray(array|ElasticsearchResponse $response): array
{
return is_array($response) ? $response : $response->asArray();
}
/**
* Execute a HEAD-style Elasticsearch call without throwing on 404.
*
* @param callable(Client): ElasticsearchResponse $callback
*/
public static function boolResponse(Client $client, callable $callback): bool
{
$previousSetting = $client->getResponseException();
$client->setResponseException(false);
try {
return $callback($client)->asBool();
} finally {
$client->setResponseException($previousSetting);
}
}
public static function isNotFound(Throwable $throwable): bool
{
return $throwable instanceof ClientResponseException && $throwable->getCode() === 404;
}
}