Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions src/Contracts/IndexStats.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts;

final class IndexStats
{
/**
* @param non-negative-int $numberOfDocuments
* @param non-negative-int $rawDocumentDbSize
* @param non-negative-int $avgDocumentSize
* @param non-negative-int $numberOfEmbeddings
* @param non-negative-int $numberOfEmbeddedDocuments
* @param array<non-empty-string, non-negative-int> $fieldDistribution
*/
public function __construct(
private readonly int $numberOfDocuments,
private readonly int $rawDocumentDbSize,
private readonly int $avgDocumentSize,
private readonly bool $isIndexing,
private readonly int $numberOfEmbeddings,
private readonly int $numberOfEmbeddedDocuments,
private readonly array $fieldDistribution,
) {
}

/**
* @return non-negative-int
*/
public function getNumberOfDocuments(): int
{
return $this->numberOfDocuments;
}

/**
* @return non-negative-int
*/
public function getRawDocumentDbSize(): int
{
return $this->rawDocumentDbSize;
}

/**
* @return non-negative-int
*/
public function getAvgDocumentSize(): int
{
return $this->avgDocumentSize;
}

public function isIndexing(): bool
{
return $this->isIndexing;
}

/**
* @return non-negative-int
*/
public function getNumberOfEmbeddings(): int
{
return $this->numberOfEmbeddings;
}

/**
* @return non-negative-int
*/
public function getNumberOfEmbeddedDocuments(): int
{
return $this->numberOfEmbeddedDocuments;
}

/**
* @return array<non-empty-string, non-negative-int>
*/
public function getFieldDistribution(): array
{
return $this->fieldDistribution;
}

/**
* @param array{
* numberOfDocuments: non-negative-int,
* rawDocumentDbSize: non-negative-int,
* avgDocumentSize: non-negative-int,
* isIndexing: bool,
* numberOfEmbeddings: non-negative-int,
* numberOfEmbeddedDocuments: non-negative-int,
* fieldDistribution: array<non-empty-string, non-negative-int>
* } $data
*/
public static function fromArray(array $data): self
{
return new self(
$data['numberOfDocuments'],
$data['rawDocumentDbSize'],
$data['avgDocumentSize'],
$data['isIndexing'],
$data['numberOfEmbeddings'],
$data['numberOfEmbeddedDocuments'],
$data['fieldDistribution'],
);
}
}
68 changes: 68 additions & 0 deletions src/Contracts/Stats.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Meilisearch\Contracts;

final class Stats
{
/**
* @param non-negative-int $databaseSize
* @param non-negative-int $usedDatabaseSize
* @param array<non-empty-string, IndexStats> $indexes
*/
public function __construct(
private readonly int $databaseSize,
private readonly int $usedDatabaseSize,
private readonly ?\DateTimeImmutable $lastUpdate,
private readonly array $indexes,
) {
}

/**
* @return non-negative-int
*/
public function getDatabaseSize(): int
{
return $this->databaseSize;
}

/**
* @return non-negative-int
*/
public function getUsedDatabaseSize(): int
{
return $this->usedDatabaseSize;
}

public function getLastUpdate(): ?\DateTimeImmutable
{
return $this->lastUpdate;
}

/**
* @return array<non-empty-string, IndexStats>
*/
public function getIndexes(): array
{
return $this->indexes;
}

/**
* @param array{
* databaseSize: non-negative-int,
* usedDatabaseSize: non-negative-int,
* lastUpdate: non-empty-string|null,
* indexes: array<non-empty-string, mixed>
* } $data
*/
public static function fromArray(array $data): self
{
return new self(
$data['databaseSize'],
$data['usedDatabaseSize'],
null !== $data['lastUpdate'] ? new \DateTimeImmutable($data['lastUpdate']) : null,
array_map(static fn (array $v) => IndexStats::fromArray($v), $data['indexes']),
);
}
}
12 changes: 10 additions & 2 deletions src/Endpoints/Delegates/HandlesSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

namespace Meilisearch\Endpoints\Delegates;

use Meilisearch\Contracts\Stats as StatsContract;
use Meilisearch\Contracts\Task;
use Meilisearch\Endpoints\Health;
use Meilisearch\Endpoints\Stats;
use Meilisearch\Endpoints\TenantToken;
use Meilisearch\Endpoints\Version;
use Meilisearch\Exceptions\LogicException;

trait HandlesSystem
{
Expand Down Expand Up @@ -38,9 +40,15 @@ public function version(): array
return $this->version->show();
}

public function stats(): array
public function stats(): StatsContract
{
return $this->stats->show();
$stats = $this->stats->show();

if (!\is_array($stats)) {
throw new LogicException('Stats did not respond with valid data.');
}

return StatsContract::fromArray($stats);
}

public function generateTenantToken(string $apiKeyUid, $searchRules, array $options = []): string
Expand Down
72 changes: 72 additions & 0 deletions tests/Contracts/StatsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Tests\Contracts;

use Meilisearch\Contracts\IndexStats;
use Meilisearch\Contracts\Stats;
use PHPUnit\Framework\TestCase;

final class StatsTest extends TestCase
{
public function testConstruct(): void
{
$stats = new Stats(
databaseSize: 1146880,
usedDatabaseSize: 925696,
lastUpdate: $date = new \DateTimeImmutable('2025-11-15 10:03:15.000000'),
indexes: $indexes = [
'stats_a902635d92481c0925e3a801bbc60c3e' => new IndexStats(
numberOfDocuments: 2,
rawDocumentDbSize: 4096,
avgDocumentSize: 2040,
isIndexing: false,
numberOfEmbeddings: 0,
numberOfEmbeddedDocuments: 0,
fieldDistribution: ['objectID' => 2, 'type' => 1],
),
],
);

self::assertSame(1146880, $stats->getDatabaseSize());
self::assertSame(925696, $stats->getUsedDatabaseSize());
self::assertSame($date, $stats->getLastUpdate());
self::assertSame($indexes, $stats->getIndexes());
}

public function testFromArray(): void
{
$stats = Stats::fromArray([
'databaseSize' => 1146880,
'usedDatabaseSize' => 925696,
'lastUpdate' => '2025-11-15T10:03:15.000000000Z',
'indexes' => [
'stats_a902635d92481c0925e3a801bbc60c3e' => [
'numberOfDocuments' => 2,
'rawDocumentDbSize' => 4096,
'avgDocumentSize' => 2040,
'isIndexing' => false,
'numberOfEmbeddings' => 0,
'numberOfEmbeddedDocuments' => 0,
'fieldDistribution' => ['objectID' => 2, 'type' => 1],
],
],
]);

self::assertSame(1146880, $stats->getDatabaseSize());
self::assertSame(925696, $stats->getUsedDatabaseSize());
self::assertEquals(new \DateTimeImmutable('2025-11-15 10:03:15.000000'), $stats->getLastUpdate());
self::assertEquals([
'stats_a902635d92481c0925e3a801bbc60c3e' => new IndexStats(
numberOfDocuments: 2,
rawDocumentDbSize: 4096,
avgDocumentSize: 2040,
isIndexing: false,
numberOfEmbeddings: 0,
numberOfEmbeddedDocuments: 0,
fieldDistribution: ['objectID' => 2, 'type' => 1],
),
], $stats->getIndexes());
}
}
25 changes: 19 additions & 6 deletions tests/Endpoints/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,28 +210,41 @@ public function testVersion(): void

public function testStats(): void
{
$index = $this->createEmptyIndex($this->safeIndexName('stats'));
$index->addDocuments([
['objectID' => 1, 'type' => 'Library'],
['objectID' => 2],
])->wait();

$response = $this->client->stats();

self::assertArrayHasKey('databaseSize', $response);
self::assertArrayHasKey('lastUpdate', $response);
self::assertArrayHasKey('indexes', $response);
self::assertGreaterThanOrEqual(0, $response->getDatabaseSize());
self::assertGreaterThanOrEqual(0, $response->getUsedDatabaseSize());

$statsIndex = $response->getIndexes()[$index->getUid()];

self::assertSame(2, $statsIndex->getNumberOfDocuments());
self::assertSame(['objectID' => 2, 'type' => 1], $statsIndex->getFieldDistribution());
}

public function testBadClientUrl(): void
{
$this->expectException(\Exception::class);
$client = new Client('http://127.0.0.1.com:1234', 'some-key');

$this->expectException(\Exception::class);

$client->createIndex('index');
}

public function testHeaderWithoutApiKey(): void
{
$client = new Client($this->host);

$response = $client->health();

self::assertSame('available', $response['status']);

$this->expectException(ApiException::class);
$response = $client->stats();

$client->stats();
}
}