Skip to content
Open
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
10 changes: 8 additions & 2 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,15 @@ update_search_cutoff_1: |-
reset_search_cutoff_1: |-
client.index("movies").resetSearchCutoffMsSettings();
get_index_stats_1: |-
client.index("movies").getStats();
StatsQuery statsQuery = new StatsQuery()
.setShowInternalDatabaseSizes(true)
.setSizeFormat("human");
client.index("movies").getStats(statsQuery);
get_indexes_stats_1: |-
client.getStats();
StatsQuery statsQuery = new StatsQuery()
.setShowInternalDatabaseSizes(true)
.setSizeFormat("human");
client.getStats(statsQuery);
get_health_1: |-
client.health();
get_version_1: |-
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/meilisearch/sdk/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ public Stats getStats() throws MeilisearchException {
return this.instanceHandler.getStats();
}

/**
* Gets extended information and metrics about indexes and the Meilisearch database
*
* @param params query parameters accepted by the stats route
* @return StatsWithSizeFormat instance from Meilisearch API response
* @throws MeilisearchException if an error occurs
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats#stats-object">API
* specification</a>
*/
public StatsWithSizeFormat getStats(StatsQuery params) throws MeilisearchException {
return this.instanceHandler.getStats(params);
}

/**
* Gets the version of Meilisearch instance
*
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/meilisearch/sdk/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,19 @@ public IndexStats getStats() throws MeilisearchException {
return this.instanceHandler.getIndexStats(this.uid);
}

/**
* Gets extended information and metrics about indexes and the Meilisearch database
*
* @param params query parameters accepted by the stats route
* @return Meilisearch API response
* @throws MeilisearchException if an error occurs
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats#get-stats">API
* specification</a>
*/
public IndexStatsWithSizeFormat getStats(StatsQuery params) throws MeilisearchException {
return this.instanceHandler.getIndexStats(this.uid, params);
}

/**
* Retrieves an index tasks by its uid
*
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/meilisearch/sdk/InstanceHandler.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.meilisearch.sdk;

import com.meilisearch.sdk.exceptions.MeilisearchException;
import com.meilisearch.sdk.http.URLBuilder;
import com.meilisearch.sdk.model.IndexStats;
import com.meilisearch.sdk.model.IndexStatsWithSizeFormat;
import com.meilisearch.sdk.model.Stats;
import com.meilisearch.sdk.model.StatsQuery;
import com.meilisearch.sdk.model.StatsWithSizeFormat;

/** Class providing information on the Meilisearch instance */
public class InstanceHandler {
Expand Down Expand Up @@ -55,6 +59,22 @@ Stats getStats() throws MeilisearchException {
return httpClient.get("/stats", Stats.class);
}

/**
* Gets extended information and metrics about indexes and the Meilisearch database
*
* @param params query parameters accepted by the stats route
* @return Meilisearch API response
* @throws MeilisearchException if an error occurs
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats">API specification</a>
*/
StatsWithSizeFormat getStats(StatsQuery params) throws MeilisearchException {
URLBuilder urlb = new URLBuilder("/stats");
if (params != null) {
urlb.addQuery(params.toQuery());
}
return httpClient.get(urlb.getURL(), StatsWithSizeFormat.class);
}

/**
* Gets extended information and metrics about indexes and the Meilisearch database
*
Expand All @@ -68,6 +88,24 @@ IndexStats getIndexStats(String uid) throws MeilisearchException {
return httpClient.<IndexStats>get(requestQuery, IndexStats.class);
}

/**
* Gets extended information and metrics about an index and the Meilisearch database
*
* @param uid Index identifier to the requested
* @param params query parameters accepted by the stats route
* @return Meilisearch API response
* @throws MeilisearchException if an error occurs
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats">API specification</a>
*/
IndexStatsWithSizeFormat getIndexStats(String uid, StatsQuery params)
throws MeilisearchException {
URLBuilder urlb = new URLBuilder("/indexes").addSubroute(uid).addSubroute("stats");
if (params != null) {
urlb.addQuery(params.toQuery());
}
return httpClient.get(urlb.getURL(), IndexStatsWithSizeFormat.class);
}

/**
* Gets the version of Meilisearch instance
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.meilisearch.sdk.model;

import java.util.Map;
import lombok.Getter;

/**
* Stats data structure of a Meilisearch Index when database sizes may be raw bytes or human-readable
* strings.
*
* @see <a href="https://www.meilisearch.com/docs/reference/api/indexes/get-stats-of-index">API
* specification</a>
*/
@Getter
public class IndexStatsWithSizeFormat {
protected long numberOfDocuments;
protected boolean isIndexing;
protected Map<String, Integer> fieldDistribution;
protected Object rawDocumentDbSize;
protected Object avgDocumentSize;
protected Object maxDocumentSize;
protected Long numberOfEmbeddedDocuments;
protected Long numberOfEmbeddings;
protected Map<String, Object> internalDatabaseSizes;

public IndexStatsWithSizeFormat() {}

public IndexStatsWithSizeFormat(
long numberOfDocuments,
boolean isIndexing,
Map<String, Integer> fieldDistribution,
Object rawDocumentDbSize,
Object avgDocumentSize,
Object maxDocumentSize,
Long numberOfEmbeddedDocuments,
Long numberOfEmbeddings,
Map<String, Object> internalDatabaseSizes) {
this.numberOfDocuments = numberOfDocuments;
this.isIndexing = isIndexing;
this.fieldDistribution = fieldDistribution;
this.rawDocumentDbSize = rawDocumentDbSize;
this.avgDocumentSize = avgDocumentSize;
this.maxDocumentSize = maxDocumentSize;
this.numberOfEmbeddedDocuments = numberOfEmbeddedDocuments;
this.numberOfEmbeddings = numberOfEmbeddings;
this.internalDatabaseSizes = internalDatabaseSizes;
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/meilisearch/sdk/model/StatsQuery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.meilisearch.sdk.model;

import com.meilisearch.sdk.http.URLBuilder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
* Data structure of the query parameters for the stats routes.
*
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats">API specification</a>
*/
@Setter
@Getter
@Accessors(chain = true)
public class StatsQuery {
private Boolean showInternalDatabaseSizes;
private String sizeFormat;

public StatsQuery() {}

public String toQuery() {
URLBuilder urlb =
new URLBuilder()
.addParameter(
"showInternalDatabaseSizes",
this.getShowInternalDatabaseSizes())
.addParameter("sizeFormat", this.getSizeFormat());
return urlb.getURL();
Comment on lines +22 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Omit sizeFormat when it is unset.

Line 28 always passes sizeFormat into URLBuilder, but that helper only skips empty strings. With new StatsQuery() or new StatsQuery().setShowInternalDatabaseSizes(true), this serializes as ?sizeFormat=null, which violates the new optional-parameter contract and breaks the request.

Proposed fix
     public String toQuery() {
-        URLBuilder urlb =
-                new URLBuilder()
-                        .addParameter(
-                                "showInternalDatabaseSizes",
-                                this.getShowInternalDatabaseSizes())
-                        .addParameter("sizeFormat", this.getSizeFormat());
+        URLBuilder urlb =
+                new URLBuilder()
+                        .addParameter(
+                                "showInternalDatabaseSizes",
+                                this.getShowInternalDatabaseSizes());
+        if (this.getSizeFormat() != null && !this.getSizeFormat().isEmpty()) {
+            urlb.addParameter("sizeFormat", this.getSizeFormat());
+        }
         return urlb.getURL();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String toQuery() {
URLBuilder urlb =
new URLBuilder()
.addParameter(
"showInternalDatabaseSizes",
this.getShowInternalDatabaseSizes())
.addParameter("sizeFormat", this.getSizeFormat());
return urlb.getURL();
public String toQuery() {
URLBuilder urlb =
new URLBuilder()
.addParameter(
"showInternalDatabaseSizes",
this.getShowInternalDatabaseSizes());
if (this.getSizeFormat() != null && !this.getSizeFormat().isEmpty()) {
urlb.addParameter("sizeFormat", this.getSizeFormat());
}
return urlb.getURL();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/meilisearch/sdk/model/StatsQuery.java` around lines 22 -
29, The StatsQuery.toQuery() serialization currently always adds sizeFormat,
which produces an unwanted null query value when it is unset. Update toQuery()
so it only calls URLBuilder.addParameter("sizeFormat", ...) when getSizeFormat()
is non-null/non-empty, while keeping showInternalDatabaseSizes behavior
unchanged. Use the StatsQuery and URLBuilder symbols to locate the
optional-parameter handling and align it with the new contract.

}
}
31 changes: 31 additions & 0 deletions src/main/java/com/meilisearch/sdk/model/StatsWithSizeFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.meilisearch.sdk.model;

import java.util.Date;
import java.util.Map;
import lombok.Getter;

/**
* Meilisearch stats data structure when database sizes may be raw bytes or human-readable strings.
*
* @see <a href="https://www.meilisearch.com/docs/reference/api/stats">API specification</a>
*/
@Getter
public class StatsWithSizeFormat {
protected Object databaseSize;
protected Date lastUpdate;
protected Map<String, IndexStatsWithSizeFormat> indexes;
protected Object usedDatabaseSize;

public StatsWithSizeFormat(
Object databaseSize,
Date lastUpdate,
Map<String, IndexStatsWithSizeFormat> indexes,
Object usedDatabaseSize) {
this.databaseSize = databaseSize;
this.lastUpdate = lastUpdate;
this.indexes = indexes;
this.usedDatabaseSize = usedDatabaseSize;
}

public StatsWithSizeFormat() {}
}
121 changes: 121 additions & 0 deletions src/test/java/com/meilisearch/sdk/StatsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.meilisearch.sdk;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import com.meilisearch.sdk.model.IndexStatsWithSizeFormat;
import com.meilisearch.sdk.model.StatsQuery;
import com.meilisearch.sdk.model.StatsWithSizeFormat;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class StatsTest {

private MockWebServer server;
private Client client;

@BeforeEach
void setup() throws Exception {
server = new MockWebServer();
server.start();

client = new Client(new Config(server.url("/").toString(), "masterKey"));
}

@AfterEach
void teardown() throws Exception {
server.shutdown();
}

@Test
void getStatsWithQueryParameters() throws Exception {
String json =
"""
{
"databaseSize": "1.2 KiB",
"usedDatabaseSize": "1 KiB",
"lastUpdate": "2019-11-20T09:40:33.711324Z",
"indexes": {
"movies": {
"numberOfDocuments": 10,
"rawDocumentDbSize": "100 B",
"maxDocumentSize": "16 B",
"avgDocumentSize": "10 B",
"isIndexing": true,
"fieldDistribution": {
"genre": 10
},
"internalDatabaseSizes": {
"documents": "100 B"
}
}
}
}
""";
StatsQuery query =
new StatsQuery().setShowInternalDatabaseSizes(true).setSizeFormat("human");

server.enqueue(new MockResponse().setResponseCode(200).setBody(json));

StatsWithSizeFormat stats = client.getStats(query);

assertThat(
server.takeRequest().getPath(),
is("//stats?showInternalDatabaseSizes=true&sizeFormat=human"));
assertThat(stats.getDatabaseSize(), is("1.2 KiB"));
assertThat(stats.getUsedDatabaseSize(), is("1 KiB"));
assertThat(stats.getIndexes().get("movies").getRawDocumentDbSize(), is("100 B"));
assertThat(
stats.getIndexes().get("movies").getInternalDatabaseSizes().get("documents"),
is("100 B"));
}

@Test
void getIndexStatsWithQueryParameters() throws Exception {
String json =
"""
{
"numberOfDocuments": 10,
"rawDocumentDbSize": "100 B",
"maxDocumentSize": "16 B",
"avgDocumentSize": "10 B",
"numberOfEmbeddings": 2,
"numberOfEmbeddedDocuments": 1,
"isIndexing": false,
"fieldDistribution": {
"genre": 10
},
"internalDatabaseSizes": {
"documents": "100 B"
}
}
""";
StatsQuery query =
new StatsQuery().setShowInternalDatabaseSizes(true).setSizeFormat("human");

server.enqueue(new MockResponse().setResponseCode(200).setBody(json));

IndexStatsWithSizeFormat stats = client.index("movies").getStats(query);

assertThat(
server.takeRequest().getPath(),
is("//indexes/movies/stats?showInternalDatabaseSizes=true&sizeFormat=human"));
assertThat(stats.getRawDocumentDbSize(), is("100 B"));
assertThat(stats.getAvgDocumentSize(), is("10 B"));
assertThat(stats.getMaxDocumentSize(), is("16 B"));
assertThat(stats.getInternalDatabaseSizes().get("documents"), is("100 B"));
assertThat(stats.getNumberOfEmbeddings(), is(2L));
assertThat(stats.getNumberOfEmbeddedDocuments(), is(1L));
}

@Test
void statsQuerySerializesParameters() {
StatsQuery query =
new StatsQuery().setShowInternalDatabaseSizes(true).setSizeFormat("raw");

assertThat(query.toQuery(), is("?showInternalDatabaseSizes=true&sizeFormat=raw"));
}
}