diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index ed73f5b5..8cbd8c91 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -854,3 +854,5 @@ export_post_1: |- client.export(request); compact_index_1: |- client.index("INDEX_NAME").compact(); +rename_an_index_1: |- + client.updateIndex("indexA", null, "indexB"); diff --git a/build.gradle b/build.gradle index d0ea5bec..c75e8861 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,8 @@ dependencies { testImplementation 'com.squareup.okhttp3:okhttp:5.3.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.20.1' + testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0' + // https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind compileOnly group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.20.1' diff --git a/data.ms/VERSION b/data.ms/VERSION new file mode 100644 index 00000000..bc584045 --- /dev/null +++ b/data.ms/VERSION @@ -0,0 +1 @@ +1.26.0 \ No newline at end of file diff --git a/data.ms/auth/data.mdb b/data.ms/auth/data.mdb new file mode 100644 index 00000000..75c5e139 Binary files /dev/null and b/data.ms/auth/data.mdb differ diff --git a/data.ms/auth/lock.mdb b/data.ms/auth/lock.mdb new file mode 100644 index 00000000..66998cbb Binary files /dev/null and b/data.ms/auth/lock.mdb differ diff --git a/data.ms/instance-uid b/data.ms/instance-uid new file mode 100644 index 00000000..31fed96f --- /dev/null +++ b/data.ms/instance-uid @@ -0,0 +1 @@ +e67555e1-d1ac-4ecb-adae-00d5687bbea3 \ No newline at end of file diff --git a/data.ms/tasks/data.mdb b/data.ms/tasks/data.mdb new file mode 100644 index 00000000..4fb8ee69 Binary files /dev/null and b/data.ms/tasks/data.mdb differ diff --git a/data.ms/tasks/lock.mdb b/data.ms/tasks/lock.mdb new file mode 100644 index 00000000..f9ead877 Binary files /dev/null and b/data.ms/tasks/lock.mdb differ diff --git a/src/main/java/com/meilisearch/sdk/Client.java b/src/main/java/com/meilisearch/sdk/Client.java index 3a871eb8..888a52dd 100644 --- a/src/main/java/com/meilisearch/sdk/Client.java +++ b/src/main/java/com/meilisearch/sdk/Client.java @@ -10,11 +10,7 @@ import com.meilisearch.sdk.model.*; import com.meilisearch.sdk.model.batch.req.BatchesQuery; import com.meilisearch.sdk.model.batch.res.Batch; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.TimeZone; -import java.util.UUID; +import java.util.*; /** Meilisearch client */ public class Client { @@ -169,6 +165,15 @@ public TaskInfo updateIndex(String uid, String primaryKey) throws MeilisearchExc return this.indexesHandler.updatePrimaryKey(uid, primaryKey); } + /** Update an index: either update primary key or rename the index by passing indexUid. */ + public TaskInfo updateIndex(String uid, String primaryKey, String indexUid) + throws MeilisearchException { + if (indexUid != null) { + return this.indexesHandler.updateIndexUid(uid, indexUid); + } + return this.indexesHandler.updatePrimaryKey(uid, primaryKey); + } + /** * Deletes single index by its unique identifier * diff --git a/src/main/java/com/meilisearch/sdk/IndexesHandler.java b/src/main/java/com/meilisearch/sdk/IndexesHandler.java index 9a9996f5..fdaa0b0f 100644 --- a/src/main/java/com/meilisearch/sdk/IndexesHandler.java +++ b/src/main/java/com/meilisearch/sdk/IndexesHandler.java @@ -121,6 +121,20 @@ TaskInfo updatePrimaryKey(String uid, String primaryKey) throws MeilisearchExcep return httpClient.patch(indexesPath().addSubroute(uid).getURL(), index, TaskInfo.class); } + /** + * Rename an index by changing its uid. + * + * @param uid Unique identifier of the index to rename + * @param indexUid New unique identifier for the index + * @return Meilisearch API response as TaskInfo + * @throws MeilisearchException if an error occurs + */ + TaskInfo updateIndexUid(String uid, String indexUid) throws MeilisearchException { + HashMap body = new HashMap<>(); + body.put("indexUid", indexUid); + return httpClient.patch(indexesPath().addSubroute(uid).getURL(), body, TaskInfo.class); + } + /** * Deletes an index in the Meilisearch instance * diff --git a/src/main/java/com/meilisearch/sdk/model/SearchResult.java b/src/main/java/com/meilisearch/sdk/model/SearchResult.java index ef8c424a..0640d4d2 100644 --- a/src/main/java/com/meilisearch/sdk/model/SearchResult.java +++ b/src/main/java/com/meilisearch/sdk/model/SearchResult.java @@ -18,6 +18,7 @@ public class SearchResult implements Searchable { Object facetDistribution; HashMap facetStats; int processingTimeMs; + ArrayList queryVector; String query; int offset; int limit; diff --git a/src/main/java/com/meilisearch/sdk/model/SearchResultPaginated.java b/src/main/java/com/meilisearch/sdk/model/SearchResultPaginated.java index 239077f5..474ba742 100644 --- a/src/main/java/com/meilisearch/sdk/model/SearchResultPaginated.java +++ b/src/main/java/com/meilisearch/sdk/model/SearchResultPaginated.java @@ -23,6 +23,7 @@ public class SearchResultPaginated implements Searchable { Object facetDistribution; HashMap facetStats; int processingTimeMs; + ArrayList queryVector; String query; public SearchResultPaginated() {} diff --git a/src/main/java/com/meilisearch/sdk/model/SwapIndexesParams.java b/src/main/java/com/meilisearch/sdk/model/SwapIndexesParams.java index 53229791..bc8c5c2b 100644 --- a/src/main/java/com/meilisearch/sdk/model/SwapIndexesParams.java +++ b/src/main/java/com/meilisearch/sdk/model/SwapIndexesParams.java @@ -10,6 +10,7 @@ @Accessors(chain = true) public class SwapIndexesParams { protected String[] indexes; + protected Boolean rename; public SwapIndexesParams() {} } diff --git a/src/test/java/com/meilisearch/sdk/IndexRenameTest.java b/src/test/java/com/meilisearch/sdk/IndexRenameTest.java new file mode 100644 index 00000000..edd7d52d --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/IndexRenameTest.java @@ -0,0 +1,83 @@ +package com.meilisearch.sdk; + +import com.meilisearch.sdk.model.TaskInfo; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +class IndexRenameTest { + + private MockWebServer mockServer; + private Config config; + private IndexesHandler handler; + + @BeforeEach + void setUp() throws IOException { + mockServer = new MockWebServer(); + mockServer.start(); + + String baseUrl = mockServer.url("").toString(); + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + config = new Config(baseUrl, "masterKey"); + handler = new IndexesHandler(config); + } + + @AfterEach + void tearDown() throws IOException { + mockServer.shutdown(); + } + + @Test + void testRenameIndex() throws Exception { + String responseJson = "{\"taskUid\":123,\"indexUid\":\"indexB\",\"status\":\"enqueued\",\"type\":\"indexUpdate\",\"enqueuedAt\":\"2024-01-01T00:00:00Z\"}"; + mockServer.enqueue(new MockResponse() + .setResponseCode(202) + .setBody(responseJson) + .addHeader("Content-Type", "application/json")); + + TaskInfo result = handler.updateIndexUid("indexA", "indexB"); + + assertThat(result, is(notNullValue())); + assertThat(result.getTaskUid(), is(equalTo(123))); + + RecordedRequest request = mockServer.takeRequest(); + assertThat(request.getMethod(), equalTo("PATCH")); + assertThat(request.getPath(), equalTo("/indexes/indexA")); + assertThat(request.getHeader("Authorization"), equalTo("Bearer masterKey")); + + String requestBody = request.getBody().readUtf8(); + assertThat(requestBody, containsString("\"indexUid\":\"indexB\"")); + } + + @Test + void testRenameIndexWithDifferentNames() throws Exception { + String responseJson = "{\"taskUid\":456,\"indexUid\":\"newIndex\",\"status\":\"enqueued\",\"type\":\"indexUpdate\",\"enqueuedAt\":\"2024-01-02T00:00:00Z\"}"; + mockServer.enqueue(new MockResponse() + .setResponseCode(202) + .setBody(responseJson) + .addHeader("Content-Type", "application/json")); + + TaskInfo result = handler.updateIndexUid("oldIndex", "newIndex"); + + assertThat(result, is(notNullValue())); + assertThat(result.getTaskUid(), is(equalTo(456))); + + RecordedRequest request = mockServer.takeRequest(); + assertThat(request.getMethod(), equalTo("PATCH")); + assertThat(request.getPath(), equalTo("/indexes/oldIndex")); + assertThat(request.getHeader("Authorization"), equalTo("Bearer masterKey")); + + String requestBody = request.getBody().readUtf8(); + assertThat(requestBody, containsString("\"indexUid\":\"newIndex\"")); + } +} diff --git a/src/test/java/com/meilisearch/sdk/SearchResultQueryVectorTest.java b/src/test/java/com/meilisearch/sdk/SearchResultQueryVectorTest.java new file mode 100644 index 00000000..b1590a2b --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/SearchResultQueryVectorTest.java @@ -0,0 +1,32 @@ +package com.meilisearch.sdk; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +import com.meilisearch.sdk.json.GsonJsonHandler; +import com.meilisearch.sdk.model.SearchResult; +import org.junit.jupiter.api.Test; + +class SearchResultQueryVectorTest { + + @Test + void testQueryVectorIsMapped() throws Exception { + String json = + "{" + + "\"hits\": []," + + "\"processingTimeMs\": 1," + + "\"query\": \"hello\"," + + "\"queryVector\": [0.1, 0.2, 0.3]" + + "}"; + + GsonJsonHandler handler = new GsonJsonHandler(); + SearchResult result = handler.decode(json, SearchResult.class); + + assertThat(result, is(notNullValue())); + assertThat(result.getQueryVector(), is(notNullValue())); + assertThat(result.getQueryVector().size(), is(3)); + assertThat(result.getQueryVector().get(0), equalTo(0.1F)); + assertThat(result.getQueryVector().get(1), equalTo(0.2F)); + assertThat(result.getQueryVector().get(2), equalTo(0.3F)); + } +} diff --git a/src/test/java/com/meilisearch/sdk/SwapIndexesRenameTest.java b/src/test/java/com/meilisearch/sdk/SwapIndexesRenameTest.java new file mode 100644 index 00000000..9c3b3317 --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/SwapIndexesRenameTest.java @@ -0,0 +1,138 @@ +package com.meilisearch.sdk; + +import com.meilisearch.sdk.model.SwapIndexesParams; +import com.meilisearch.sdk.model.TaskInfo; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +public class SwapIndexesRenameTest { + + private MockWebServer mockServer; + private Config config; + private Client client; + + @BeforeEach + void setUp() throws IOException { + mockServer = new MockWebServer(); + mockServer.start(); + + String baseUrl = mockServer.url("").toString(); + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + config = new Config(baseUrl, "masterKey"); + client = new Client(config); + } + + @AfterEach + void tearDown() throws IOException { + mockServer.shutdown(); + } + + @Test + void testSwapIndexesWithRename() throws Exception { + String responseJson = "{\"taskUid\":789,\"status\":\"enqueued\",\"type\":\"indexSwap\",\"enqueuedAt\":\"2024-01-01T00:00:00Z\"}"; + mockServer.enqueue(new MockResponse() + .setResponseCode(202) + .setBody(responseJson) + .addHeader("Content-Type", "application/json")); + + SwapIndexesParams[] params = { + new SwapIndexesParams() + .setIndexes(new String[]{"indexA", "indexB"}) + .setRename(true) + }; + + TaskInfo result = client.swapIndexes(params); + + assertThat(result, is(notNullValue())); + assertThat(result.getTaskUid(), is(equalTo(789))); + + RecordedRequest request = mockServer.takeRequest(); + assertThat(request.getMethod(), equalTo("POST")); + assertThat(request.getPath(), equalTo("/swap-indexes")); + assertThat(request.getHeader("Authorization"), equalTo("Bearer masterKey")); + + String requestBody = request.getBody().readUtf8(); + assertThat(requestBody, containsString("\"rename\":true")); + assertThat(requestBody, containsString("\"indexes\"")); + assertThat(requestBody, containsString("\"indexA\"")); + assertThat(requestBody, containsString("\"indexB\"")); + } + + @Test + void testSwapIndexesWithoutRename() throws Exception { + String responseJson = "{\"taskUid\":790,\"status\":\"enqueued\",\"type\":\"indexSwap\",\"enqueuedAt\":\"2024-01-02T00:00:00Z\"}"; + mockServer.enqueue(new MockResponse() + .setResponseCode(202) + .setBody(responseJson) + .addHeader("Content-Type", "application/json")); + + SwapIndexesParams[] params = { + new SwapIndexesParams() + .setIndexes(new String[]{"indexC", "indexD"}) + .setRename(false) + }; + + TaskInfo result = client.swapIndexes(params); + + assertThat(result, is(notNullValue())); + assertThat(result.getTaskUid(), is(equalTo(790))); + + RecordedRequest request = mockServer.takeRequest(); + assertThat(request.getMethod(), equalTo("POST")); + assertThat(request.getPath(), equalTo("/swap-indexes")); + assertThat(request.getHeader("Authorization"), equalTo("Bearer masterKey")); + + String requestBody = request.getBody().readUtf8(); + assertThat(requestBody, containsString("\"rename\":false")); + assertThat(requestBody, containsString("\"indexC\"")); + assertThat(requestBody, containsString("\"indexD\"")); + } + + @Test + void testSwapMultipleIndexPairs() throws Exception { + String responseJson = "{\"taskUid\":791,\"status\":\"enqueued\",\"type\":\"indexSwap\",\"enqueuedAt\":\"2024-01-03T00:00:00Z\"}"; + mockServer.enqueue(new MockResponse() + .setResponseCode(202) + .setBody(responseJson) + .addHeader("Content-Type", "application/json")); + + SwapIndexesParams[] params = { + new SwapIndexesParams() + .setIndexes(new String[]{"indexA", "indexB"}) + .setRename(true), + new SwapIndexesParams() + .setIndexes(new String[]{"indexC", "indexD"}) + .setRename(false) + }; + + TaskInfo result = client.swapIndexes(params); + + assertThat(result, is(notNullValue())); + assertThat(result.getTaskUid(), is(equalTo(791))); + + RecordedRequest request = mockServer.takeRequest(); + String requestBody = request.getBody().readUtf8(); + + assertThat(requestBody, containsString("\"indexA\"")); + assertThat(requestBody, containsString("\"indexB\"")); + assertThat(requestBody, containsString("\"indexC\"")); + assertThat(requestBody, containsString("\"indexD\"")); + assertThat(requestBody, containsString("\"rename\":true")); + assertThat(requestBody, containsString("\"rename\":false")); + + String trimmedBody = requestBody.trim(); + assertThat(trimmedBody, startsWith("[")); + assertThat(trimmedBody, endsWith("]")); + } +}