Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,9 @@ update_sortable_attributes_1: |-
reset_sortable_attributes_1: |-
client.index('books').reset_sortable_attributes()
get_index_stats_1: |-
client.index('movies').get_stats()
client.index('movies').get_stats(show_internal_database_sizes=True, size_format='human')
get_indexes_stats_1: |-
client.get_all_stats()
client.get_all_stats(show_internal_database_sizes=True, size_format='human')
get_health_1: |-
client.health()
get_version_1: |-
Expand Down
30 changes: 28 additions & 2 deletions meilisearch/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
MeilisearchError,
)
from meilisearch.index import Index
from meilisearch.models.index import SizeFormat
from meilisearch.models.key import Key, KeysResults
from meilisearch.models.task import Batch, BatchResults, Task, TaskInfo, TaskResults
from meilisearch.models.webhook import Webhook, WebhooksResults
Expand Down Expand Up @@ -322,12 +323,26 @@ def update_documents_by_function(
body=dict(queries),
)

def get_all_stats(self) -> Dict[str, Any]:
def get_all_stats(
self,
*,
show_internal_database_sizes: Optional[bool] = None,
size_format: Optional[Union[SizeFormat, str]] = None,
) -> Dict[str, Any]:
"""Get all stats of Meilisearch

Get information about database size and all indexes
https://www.meilisearch.com/docs/reference/api/stats

Parameters
----------
show_internal_database_sizes (optional):
When true, index stat objects contain an additional internalDatabaseSizes key
with the size of each internal database. Defaults to false.
size_format (optional):
When set to "human", database sizes are returned as strings with units (e.g. "1.5 GiB").
When set to "raw" or omitted, sizes are returned as numbers in bytes.

Returns
-------
stats:
Expand All @@ -338,7 +353,18 @@ def get_all_stats(self) -> Dict[str, Any]:
MeilisearchApiError
An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
return self.http.get(self.config.paths.stat)
params: Dict[str, Any] = {}
if show_internal_database_sizes is not None:
params["showInternalDatabaseSizes"] = str(show_internal_database_sizes).lower()
if size_format is not None:
params["sizeFormat"] = (
size_format.value if isinstance(size_format, SizeFormat) else size_format
)

path = self.config.paths.stat
if params:
path = f"{path}?{parse.urlencode(params)}"
return self.http.get(path)

def health(self) -> Dict[str, str]:
"""Get health of the Meilisearch server.
Expand Down
30 changes: 28 additions & 2 deletions meilisearch/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
Pagination,
PrefixSearch,
ProximityPrecision,
SizeFormat,
TypoTolerance,
)
from meilisearch.models.task import Task, TaskInfo, TaskResults
Expand Down Expand Up @@ -313,12 +314,26 @@ def wait_for_task(
"""
return self.task_handler.wait_for_task(uid, timeout_in_ms, interval_in_ms)

def get_stats(self) -> IndexStats:
def get_stats(
self,
*,
show_internal_database_sizes: Optional[bool] = None,
size_format: Optional[Union[SizeFormat, str]] = None,
) -> IndexStats:
"""Get stats of the index.

Get information about the number of documents, field frequencies, ...
https://www.meilisearch.com/docs/reference/api/stats

Parameters
----------
show_internal_database_sizes (optional):
When true, the response contains an additional internalDatabaseSizes key
with the size of each internal database. Defaults to false.
size_format (optional):
When set to "human", database sizes are returned as strings with units (e.g. "1.5 GiB").
When set to "raw" or omitted, sizes are returned as numbers in bytes.

Returns
-------
stats:
Expand All @@ -329,7 +344,18 @@ def get_stats(self) -> IndexStats:
MeilisearchApiError
An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
stats = self.http.get(f"{self.config.paths.index}/{self.uid}/{self.config.paths.stat}")
params: Dict[str, Any] = {}
if show_internal_database_sizes is not None:
params["showInternalDatabaseSizes"] = str(show_internal_database_sizes).lower()
if size_format is not None:
params["sizeFormat"] = (
size_format.value if isinstance(size_format, SizeFormat) else size_format
)

path = f"{self.config.paths.index}/{self.uid}/{self.config.paths.stat}"
if params:
path = f"{path}?{parse.urlencode(params)}"
stats = self.http.get(path)
return IndexStats(**stats)

@version_error_hint_message
Expand Down
6 changes: 6 additions & 0 deletions meilisearch/models/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ def __iter__(self) -> Iterator:
return iter(self.__dict__.items())


class SizeFormat(str, Enum):
RAW = "raw"
HUMAN = "human"


class IndexStats(CamelBase):
model_config = ConfigDict(arbitrary_types_allowed=True)

number_of_documents: int
is_indexing: bool
field_distribution: FieldDistribution
internal_database_sizes: Optional[Dict[str, Any]] = None

@field_validator("field_distribution", mode="before")
@classmethod
Expand Down
53 changes: 53 additions & 0 deletions tests/client/test_client_stats_meilisearch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import re

import pytest

from meilisearch.models.index import SizeFormat

HUMAN_SIZE_PATTERN = re.compile(r"^\d+(\.\d+)?\s+(B|KiB|MiB|GiB|TiB)$")


@pytest.mark.usefixtures("indexes_sample")
def test_get_all_stats(client):
Expand All @@ -12,3 +18,50 @@ def test_get_all_stats(client):
assert "indexes" in response
assert "indexUID" in response["indexes"]
assert "indexUID2" in response["indexes"]


@pytest.mark.usefixtures("indexes_sample")
def test_get_all_stats_with_internal_database_sizes(client):
"""Tests getting all stats with showInternalDatabaseSizes parameter."""
response = client.get_all_stats(show_internal_database_sizes=True)
assert isinstance(response, dict)
assert isinstance(response["databaseSize"], int)
assert any("internalDatabaseSizes" in index_stats for index_stats in response["indexes"].values())
for index_stats in response["indexes"].values():
if "internalDatabaseSizes" in index_stats:
assert isinstance(index_stats["internalDatabaseSizes"], dict)
assert len(index_stats["internalDatabaseSizes"]) > 0
assert all(
isinstance(value, int) for value in index_stats["internalDatabaseSizes"].values()
)


@pytest.mark.usefixtures("indexes_sample")
def test_get_all_stats_with_size_format(client):
"""Tests getting all stats with sizeFormat parameter."""
response = client.get_all_stats(
show_internal_database_sizes=True,
size_format=SizeFormat.HUMAN,
)
assert isinstance(response, dict)
assert isinstance(response["databaseSize"], str)
assert HUMAN_SIZE_PATTERN.match(response["databaseSize"])
for index_stats in response["indexes"].values():
if "internalDatabaseSizes" in index_stats:
assert all(
isinstance(value, str) and HUMAN_SIZE_PATTERN.match(value)
for value in index_stats["internalDatabaseSizes"].values()
)


@pytest.mark.usefixtures("indexes_sample")
def test_get_all_stats_with_all_params(client):
"""Tests getting all stats with both query parameters."""
response = client.get_all_stats(
show_internal_database_sizes=True,
size_format="human",
)
assert isinstance(response, dict)
assert isinstance(response["databaseSize"], str)
assert "indexes" in response
assert any("internalDatabaseSizes" in index_stats for index_stats in response["indexes"].values())
42 changes: 41 additions & 1 deletion tests/index/test_index_stats_meilisearch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from meilisearch.models.index import IndexStats
import re

from meilisearch.models.index import IndexStats, SizeFormat

HUMAN_SIZE_PATTERN = re.compile(r"^\d+(\.\d+)?\s+(B|KiB|MiB|GiB|TiB)$")


def test_get_stats(empty_index):
Expand All @@ -15,3 +19,39 @@ def test_get_stats_default(index_with_documents):
assert response.number_of_documents == 31
assert hasattr(response.field_distribution, "genre")
assert response.field_distribution.genre == 11


def test_get_stats_with_internal_database_sizes(index_with_documents):
"""Tests getting stats with showInternalDatabaseSizes parameter."""
response = index_with_documents().get_stats(show_internal_database_sizes=True)
assert isinstance(response, IndexStats)
assert response.internal_database_sizes is not None
assert isinstance(response.internal_database_sizes, dict)
assert len(response.internal_database_sizes) > 0
assert all(isinstance(value, int) for value in response.internal_database_sizes.values())


def test_get_stats_with_size_format(index_with_documents):
"""Tests getting stats with sizeFormat parameter."""
response = index_with_documents().get_stats(
show_internal_database_sizes=True,
size_format=SizeFormat.HUMAN,
)
assert isinstance(response, IndexStats)
assert response.internal_database_sizes is not None
assert all(
isinstance(value, str) and HUMAN_SIZE_PATTERN.match(value)
for value in response.internal_database_sizes.values()
)


def test_get_stats_with_all_params(index_with_documents):
"""Tests getting stats with both query parameters."""
response = index_with_documents().get_stats(
show_internal_database_sizes=True,
size_format="human",
)
assert isinstance(response, IndexStats)
assert response.number_of_documents == 31
assert response.internal_database_sizes is not None
assert all(isinstance(value, str) for value in response.internal_database_sizes.values())