From ec78f3c29638196ba7b970788ccffb155462c852 Mon Sep 17 00:00:00 2001 From: Fayupable <90789180+Fayupable@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:30:53 +0300 Subject: [PATCH 1/4] Add dynamic search rules API support Implement list, get, upsert (PATCH), and delete endpoints for the experimental dynamic search rules feature introduced in Meilisearch v1.50.0, plus integration tests and code samples. --- .code-samples.meilisearch.yaml | 23 ++++ src/main/java/com/meilisearch/sdk/Client.java | 48 +++++++++ .../sdk/DynamicSearchRulesHandler.java | 44 ++++++++ .../sdk/model/DynamicSearchRule.java | 37 +++++++ .../sdk/model/DynamicSearchRulesQuery.java | 27 +++++ .../integration/DynamicSearchRulesTest.java | 100 ++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java create mode 100644 src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java create mode 100644 src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java create mode 100644 src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 2c7cdd86..f8941745 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -664,3 +664,26 @@ post_render_template_1: |- .setInput(input); client.renderTemplate(request); + + +list_dynamic_search_rules_1: |- + DynamicSearchRulesQuery query = new DynamicSearchRulesQuery(); + Results rules = client.listDynamicSearchRules(query); +get_dynamic_search_rule_1: |- + DynamicSearchRule rule = client.getDynamicSearchRule("black-friday"); +patch_dynamic_search_rule_1: |- + List> conditions = List.of( + Map.of("scope", "query", "isEmpty", true), + Map.of("scope", "time", "start", "2025-11-28T00:00:00Z", "end", "2025-11-28T23:59:59Z")); + + List> actions = List.of( + Map.of( + "selector", Map.of("indexUid", "products", "id", "123"), + "action", Map.of("type", "pin", "position", 1))); + + DynamicSearchRule rule = new DynamicSearchRule( + "black-friday", "Black Friday 2025 rules", 10, true, conditions, actions); + + TaskInfo task = client.updateDynamicSearchRule("black-friday", rule); +delete_dynamic_search_rule_1: |- + TaskInfo task = client.deleteDynamicSearchRule("black-friday"); \ No newline at end of file diff --git a/src/main/java/com/meilisearch/sdk/Client.java b/src/main/java/com/meilisearch/sdk/Client.java index 3f4bfed5..d8d4cc68 100644 --- a/src/main/java/com/meilisearch/sdk/Client.java +++ b/src/main/java/com/meilisearch/sdk/Client.java @@ -21,6 +21,7 @@ public class Client { private KeysHandler keysHandler; private JsonHandler jsonHandler; private WebHooksHandler webHooksHandler; + private DynamicSearchRulesHandler dynamicSearchRulesHandler; /** * Calls instance for Meilisearch client @@ -35,6 +36,7 @@ public Client(Config config) { this.keysHandler = new KeysHandler(config); this.jsonHandler = config.jsonHandler; this.webHooksHandler = new WebHooksHandler(config); + this.dynamicSearchRulesHandler = new DynamicSearchRulesHandler(config); } /** @@ -624,4 +626,50 @@ private Boolean isValidUUID(String apiKeyUid) { } return true; } + /** + * Get a list of all dynamic search rules. + * + * @param query Query parameters for pagination and filtering (offset, limit, filter). + * @return Results containing a list of DynamicSearchRule instances. + * @throws MeilisearchException If an error occurs. + */ + public Results listDynamicSearchRules(DynamicSearchRulesQuery query) + throws MeilisearchException { + return this.dynamicSearchRulesHandler.listDynamicSearchRules(query); + } + + /** + * Get a single dynamic search rule by its uid. + * + * @param uid Unique identifier of the dynamic search rule. + * @return A single DynamicSearchRule instance. + * @throws MeilisearchException If an error occurs. + */ + public DynamicSearchRule getDynamicSearchRule(String uid) throws MeilisearchException { + return this.dynamicSearchRulesHandler.getDynamicSearchRule(uid); + } + + /** + * Create a new dynamic search rule, or update it if a rule with the given uid already exists. + * + * @param uid Unique identifier of the dynamic search rule. + * @param dynamicSearchRule Request body containing the rule configuration. + * @return TaskInfo instance for the enqueued upsert operation. + * @throws MeilisearchException If an error occurs. + */ + public TaskInfo updateDynamicSearchRule(String uid, DynamicSearchRule dynamicSearchRule) + throws MeilisearchException { + return this.dynamicSearchRulesHandler.updateDynamicSearchRule(uid, dynamicSearchRule); + } + + /** + * Delete a dynamic search rule. + * + * @param uid Unique identifier of the dynamic search rule. + * @return TaskInfo instance for the enqueued delete operation. + * @throws MeilisearchException If an error occurs. + */ + public TaskInfo deleteDynamicSearchRule(String uid) throws MeilisearchException { + return this.dynamicSearchRulesHandler.deleteDynamicSearchRule(uid); + } } diff --git a/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java b/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java new file mode 100644 index 00000000..39eb6eb5 --- /dev/null +++ b/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java @@ -0,0 +1,44 @@ +package com.meilisearch.sdk; + +import com.meilisearch.sdk.exceptions.MeilisearchException; +import com.meilisearch.sdk.http.URLBuilder; +import com.meilisearch.sdk.model.DynamicSearchRule; +import com.meilisearch.sdk.model.DynamicSearchRulesQuery; +import com.meilisearch.sdk.model.Results; +import com.meilisearch.sdk.model.TaskInfo; + +class DynamicSearchRulesHandler { + private final HttpClient httpClient; + + protected DynamicSearchRulesHandler(Config config) { + httpClient = config.httpClient; + } + + Results listDynamicSearchRules(DynamicSearchRulesQuery query) + throws MeilisearchException { + return httpClient.post( + dynamicSearchRulesPath().getURL(), query, Results.class, DynamicSearchRule.class); +} + + DynamicSearchRule getDynamicSearchRule(String uid) throws MeilisearchException { + return httpClient.get( + dynamicSearchRulesPath().addSubroute(uid).getURL(), DynamicSearchRule.class); + } + + TaskInfo updateDynamicSearchRule(String uid, DynamicSearchRule dynamicSearchRule) + throws MeilisearchException { + return httpClient.patch( + dynamicSearchRulesPath().addSubroute(uid).getURL(), + dynamicSearchRule, + TaskInfo.class); + } + + TaskInfo deleteDynamicSearchRule(String uid) throws MeilisearchException { + return httpClient.delete( + dynamicSearchRulesPath().addSubroute(uid).getURL(), TaskInfo.class); + } + + private URLBuilder dynamicSearchRulesPath() { + return new URLBuilder("/dynamic-search-rules"); + } +} \ No newline at end of file diff --git a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java new file mode 100644 index 00000000..cae92994 --- /dev/null +++ b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java @@ -0,0 +1,37 @@ +package com.meilisearch.sdk.model; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +/** + * Meilisearch Dynamic Search Rule. + * + * @see API + * specification + */ +@Getter +public class DynamicSearchRule implements Serializable { + protected String uid; + protected String description; + protected int priority; + protected boolean active; + protected List> conditions; + protected List> actions; + + public DynamicSearchRule( + String uid, + String description, + int priority, + boolean active, + List> conditions, + List> actions) { + this.uid = uid; + this.description = description; + this.priority = priority; + this.active = active; + this.conditions = conditions; + this.actions = actions; + } +} \ No newline at end of file diff --git a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java new file mode 100644 index 00000000..8b86160c --- /dev/null +++ b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java @@ -0,0 +1,27 @@ +package com.meilisearch.sdk.model; + +import java.io.Serializable; +import java.util.Map; + +public class DynamicSearchRulesQuery implements Serializable { + protected Integer offset; + protected Integer limit; + protected Map filter; + + public DynamicSearchRulesQuery() {} + + public DynamicSearchRulesQuery setOffset(int offset) { + this.offset = offset; + return this; + } + + public DynamicSearchRulesQuery setLimit(int limit) { + this.limit = limit; + return this; + } + + public DynamicSearchRulesQuery setFilter(Map filter) { + this.filter = filter; + return this; + } +} \ No newline at end of file diff --git a/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java b/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java new file mode 100644 index 00000000..b125bebd --- /dev/null +++ b/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java @@ -0,0 +1,100 @@ +package com.meilisearch.integration; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.meilisearch.integration.classes.AbstractIT; +import com.meilisearch.sdk.exceptions.MeilisearchException; +import com.meilisearch.sdk.model.DynamicSearchRule; +import com.meilisearch.sdk.model.DynamicSearchRulesQuery; +import com.meilisearch.sdk.model.Results; +import com.meilisearch.sdk.model.TaskInfo; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag("integration") +public class DynamicSearchRulesTest extends AbstractIT { + + @BeforeEach + public void initialize() throws MeilisearchException { + this.setUp(); + client.experimentalFeatures(Collections.singletonMap("dynamicSearchRules", true)); + } + + @AfterAll + static void cleanMeilisearch() { + cleanup(); + } + + private DynamicSearchRule buildTestRule(String uid) { + Map queryCondition = + Map.of("scope", "query", "isEmpty", true); + Map action = + Map.of( + "selector", Map.of("indexUid", "products"), + "action", Map.of("type", "pin", "position", 1)); + + return new DynamicSearchRule( + uid, "Test rule", 5, true, List.of(queryCondition), List.of(action)); + } + + @Test + public void testUpdateDynamicSearchRule() throws MeilisearchException { + DynamicSearchRule rule = buildTestRule("test-rule"); + + TaskInfo task = client.updateDynamicSearchRule("test-rule", rule); + client.waitForTask(task.getTaskUid()); + + DynamicSearchRule fetched = client.getDynamicSearchRule("test-rule"); + assertThat(fetched.getUid(), equalTo("test-rule")); + assertThat(fetched.getDescription(), equalTo("Test rule")); + assertThat(fetched.getPriority(), equalTo(5)); + assertThat(fetched.isActive(), equalTo(true)); + } + + @Test + public void testGetDynamicSearchRule() throws MeilisearchException { + DynamicSearchRule rule = buildTestRule("test-rule-get"); + TaskInfo task = client.updateDynamicSearchRule("test-rule-get", rule); + client.waitForTask(task.getTaskUid()); + + DynamicSearchRule fetched = client.getDynamicSearchRule("test-rule-get"); + assertThat(fetched, notNullValue()); + assertThat(fetched.getUid(), equalTo("test-rule-get")); + } + + @Test + public void testListDynamicSearchRules() throws MeilisearchException { + TaskInfo task1 = client.updateDynamicSearchRule("rule-one", buildTestRule("rule-one")); + client.waitForTask(task1.getTaskUid()); + TaskInfo task2 = client.updateDynamicSearchRule("rule-two", buildTestRule("rule-two")); + client.waitForTask(task2.getTaskUid()); + + Results results = + client.listDynamicSearchRules(new DynamicSearchRulesQuery()); + + assertThat(results.getResults().size() >= 2, equalTo(true)); + } + + @Test + public void testDeleteDynamicSearchRule() throws MeilisearchException { + DynamicSearchRule rule = buildTestRule("test-rule-delete"); + TaskInfo createTask = client.updateDynamicSearchRule("test-rule-delete", rule); + client.waitForTask(createTask.getTaskUid()); + + TaskInfo deleteTask = client.deleteDynamicSearchRule("test-rule-delete"); + client.waitForTask(deleteTask.getTaskUid()); + + MeilisearchException exception = + org.junit.jupiter.api.Assertions.assertThrows( + MeilisearchException.class, + () -> client.getDynamicSearchRule("test-rule-delete")); + assertThat(exception, notNullValue()); + } +} \ No newline at end of file From ec41e91fc5de0411d9dec54130c3999da7ff7679 Mon Sep 17 00:00:00 2001 From: Fayupable <90789180+Fayupable@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:55:46 +0300 Subject: [PATCH 2/4] Add no-arg constructor and setters to DynamicSearchRule --- .../java/com/meilisearch/sdk/model/DynamicSearchRule.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java index cae92994..93797ab5 100644 --- a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java +++ b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import lombok.Getter; +import lombok.Setter; /** * Meilisearch Dynamic Search Rule. @@ -12,6 +13,7 @@ * specification */ @Getter +@Setter public class DynamicSearchRule implements Serializable { protected String uid; protected String description; @@ -20,6 +22,8 @@ public class DynamicSearchRule implements Serializable { protected List> conditions; protected List> actions; + public DynamicSearchRule() {} + public DynamicSearchRule( String uid, String description, From c41b8c994c4acb3467f2613df38be628d3d0ea47 Mon Sep 17 00:00:00 2001 From: Fayupable <90789180+Fayupable@users.noreply.github.com> Date: Wed, 22 Jul 2026 10:51:29 +0300 Subject: [PATCH 3/4] fix: format code and ensure newline at end of file in DynamicSearchRule classes --- .../meilisearch/sdk/DynamicSearchRulesHandler.java | 12 ++++++------ .../com/meilisearch/sdk/model/DynamicSearchRule.java | 2 +- .../sdk/model/DynamicSearchRulesQuery.java | 2 +- .../integration/DynamicSearchRulesTest.java | 7 +++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java b/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java index 39eb6eb5..edead4f9 100644 --- a/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java +++ b/src/main/java/com/meilisearch/sdk/DynamicSearchRulesHandler.java @@ -14,11 +14,11 @@ protected DynamicSearchRulesHandler(Config config) { httpClient = config.httpClient; } - Results listDynamicSearchRules(DynamicSearchRulesQuery query) - throws MeilisearchException { - return httpClient.post( - dynamicSearchRulesPath().getURL(), query, Results.class, DynamicSearchRule.class); -} + Results listDynamicSearchRules(DynamicSearchRulesQuery query) + throws MeilisearchException { + return httpClient.post( + dynamicSearchRulesPath().getURL(), query, Results.class, DynamicSearchRule.class); + } DynamicSearchRule getDynamicSearchRule(String uid) throws MeilisearchException { return httpClient.get( @@ -41,4 +41,4 @@ TaskInfo deleteDynamicSearchRule(String uid) throws MeilisearchException { private URLBuilder dynamicSearchRulesPath() { return new URLBuilder("/dynamic-search-rules"); } -} \ No newline at end of file +} diff --git a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java index 93797ab5..71f94838 100644 --- a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java +++ b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRule.java @@ -38,4 +38,4 @@ public DynamicSearchRule( this.conditions = conditions; this.actions = actions; } -} \ No newline at end of file +} diff --git a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java index 8b86160c..ecb439cf 100644 --- a/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java +++ b/src/main/java/com/meilisearch/sdk/model/DynamicSearchRulesQuery.java @@ -24,4 +24,4 @@ public DynamicSearchRulesQuery setFilter(Map filter) { this.filter = filter; return this; } -} \ No newline at end of file +} diff --git a/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java b/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java index b125bebd..6cf67cdc 100644 --- a/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java +++ b/src/test/java/com/meilisearch/integration/DynamicSearchRulesTest.java @@ -33,8 +33,7 @@ static void cleanMeilisearch() { } private DynamicSearchRule buildTestRule(String uid) { - Map queryCondition = - Map.of("scope", "query", "isEmpty", true); + Map queryCondition = Map.of("scope", "query", "isEmpty", true); Map action = Map.of( "selector", Map.of("indexUid", "products"), @@ -79,7 +78,7 @@ public void testListDynamicSearchRules() throws MeilisearchException { Results results = client.listDynamicSearchRules(new DynamicSearchRulesQuery()); - assertThat(results.getResults().size() >= 2, equalTo(true)); + assertThat(results.getResults().length >= 2, equalTo(true)); } @Test @@ -97,4 +96,4 @@ public void testDeleteDynamicSearchRule() throws MeilisearchException { () -> client.getDynamicSearchRule("test-rule-delete")); assertThat(exception, notNullValue()); } -} \ No newline at end of file +} From d534011beecdab9faa66c6155afc81661f8974f2 Mon Sep 17 00:00:00 2001 From: Fayupable <90789180+Fayupable@users.noreply.github.com> Date: Wed, 22 Jul 2026 11:12:55 +0300 Subject: [PATCH 4/4] fix: ensure newline at end of file in .code-samples.meilisearch.yaml --- .code-samples.meilisearch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index f8941745..51c069dd 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -686,4 +686,4 @@ patch_dynamic_search_rule_1: |- TaskInfo task = client.updateDynamicSearchRule("black-friday", rule); delete_dynamic_search_rule_1: |- - TaskInfo task = client.deleteDynamicSearchRule("black-friday"); \ No newline at end of file + TaskInfo task = client.deleteDynamicSearchRule("black-friday");