diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index e3f0f8fe..7884f1eb 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -40,14 +40,16 @@ add_or_replace_documents_1: |- + "\"poster\": \"https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg\"," + "\"overview\": \"A boy is given the ability to become an adult superhero in times of need with a single magic word.\"," + "\"release_date\": \"2019-03-23\"" - + "}]" + + "}]", + true ); add_or_update_documents_1: |- client.index("movies").updateDocuments("[{ + "\"id\": 287947," + "\"title\": \"Shazam ⚡️\"," + "\"genres\": \"comedy\"" - + "}]" + + "}]", + true ); delete_all_documents_1: |- client.index("movies").deleteAllDocuments(); diff --git a/src/main/java/com/meilisearch/sdk/Documents.java b/src/main/java/com/meilisearch/sdk/Documents.java index ce671fba..cdaf8dc4 100644 --- a/src/main/java/com/meilisearch/sdk/Documents.java +++ b/src/main/java/com/meilisearch/sdk/Documents.java @@ -182,6 +182,53 @@ TaskInfo addDocuments( String csvDelimiter, String customMetadata) throws MeilisearchException { + return addDocumentsRequest(uid, document, primaryKey, csvDelimiter, customMetadata, null); + } + + /** + * Adds/Replaces a document at the specified index uid + * + * @param uid Partial index identifier for the document + * @param document String containing the document to add + * @param primaryKey PrimaryKey of the document + * @param csvDelimiter CSV delimiter of the document + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation Whether to skip creating documents that do not already exist + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo addDocuments( + String uid, + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + boolean skipCreation) + throws MeilisearchException { + return addDocumentsRequest( + uid, document, primaryKey, csvDelimiter, customMetadata, skipCreation); + } + + /** + * Sends an add/replace request, preserving the server default when skipCreation is null. + * + * @param uid Index identifier + * @param document Serialized documents to add or replace + * @param primaryKey Optional primary key + * @param csvDelimiter Optional CSV delimiter + * @param customMetadata Optional task metadata + * @param skipCreation Whether to skip new documents, or null to omit the parameter + * @return Enqueued task information + * @throws MeilisearchException if the request fails + */ + private TaskInfo addDocumentsRequest( + String uid, + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + Boolean skipCreation) + throws MeilisearchException { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -192,6 +239,7 @@ TaskInfo addDocuments( if (customMetadata != null) { urlb.addParameter("customMetadata", customMetadata); } + urlb.addParameter("skipCreation", skipCreation); return httpClient.post(urlb.getURL(), document, TaskInfo.class); } @@ -227,6 +275,54 @@ TaskInfo updateDocuments( String csvDelimiter, String customMetadata) throws MeilisearchException { + return updateDocumentsRequest( + uid, document, primaryKey, csvDelimiter, customMetadata, null); + } + + /** + * Updates documents at the specified index uid + * + * @param uid Partial index identifier for the document + * @param document String containing the document to update + * @param primaryKey PrimaryKey of the document + * @param csvDelimiter CSV delimiter of the document + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation Whether to skip creating documents that do not already exist + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo updateDocuments( + String uid, + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + boolean skipCreation) + throws MeilisearchException { + return updateDocumentsRequest( + uid, document, primaryKey, csvDelimiter, customMetadata, skipCreation); + } + + /** + * Sends an update request, preserving the server default when skipCreation is null. + * + * @param uid Index identifier + * @param document Serialized documents to update + * @param primaryKey Optional primary key + * @param csvDelimiter Optional CSV delimiter + * @param customMetadata Optional task metadata + * @param skipCreation Whether to skip new documents, or null to omit the parameter + * @return Enqueued task information + * @throws MeilisearchException if the request fails + */ + private TaskInfo updateDocumentsRequest( + String uid, + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + Boolean skipCreation) + throws MeilisearchException { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -237,6 +333,7 @@ TaskInfo updateDocuments( if (customMetadata != null) { urlb.addParameter("customMetadata", customMetadata); } + urlb.addParameter("skipCreation", skipCreation); return httpClient.put(urlb.getURL(), document, TaskInfo.class); } diff --git a/src/main/java/com/meilisearch/sdk/Index.java b/src/main/java/com/meilisearch/sdk/Index.java index 97d397b7..2fdf2d55 100644 --- a/src/main/java/com/meilisearch/sdk/Index.java +++ b/src/main/java/com/meilisearch/sdk/Index.java @@ -172,6 +172,22 @@ public TaskInfo addDocuments(String document) throws MeilisearchException { return this.documents.addDocuments(this.uid, document, null, null); } + /** + * Adds/Replaces documents in the index + * + * @param document Document to add in JSON string format + * @param skipCreation Whether to skip creating documents that do not already exist + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo addDocuments(String document, boolean skipCreation) + throws MeilisearchException { + return this.documents.addDocuments(this.uid, document, null, null, null, skipCreation); + } + /** * Adds/Replaces documents in the index * @@ -224,6 +240,31 @@ public TaskInfo addDocuments( this.uid, document, primaryKey, csvDelimiter, customMetadata); } + /** + * Adds/Replaces documents in the index + * + * @param document Document to add in JSON or CSV string format + * @param primaryKey PrimaryKey of the document to add + * @param csvDelimiter Custom delimiter to use for the document being added + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation Whether to skip creating documents that do not already exist + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo addDocuments( + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + boolean skipCreation) + throws MeilisearchException { + return this.documents.addDocuments( + this.uid, document, primaryKey, csvDelimiter, customMetadata, skipCreation); + } + /** * Adds/Replaces documents in the index in batches * @@ -285,6 +326,22 @@ public TaskInfo updateDocuments(String document) throws MeilisearchException { return this.documents.updateDocuments(this.uid, document, null, null); } + /** + * Updates documents in the index + * + * @param document Document to update in JSON string format + * @param skipCreation Whether to skip creating documents that do not already exist + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo updateDocuments(String document, boolean skipCreation) + throws MeilisearchException { + return this.documents.updateDocuments(this.uid, document, null, null, null, skipCreation); + } + /** * Updates documents in the index * @@ -338,6 +395,31 @@ public TaskInfo updateDocuments( this.uid, document, primaryKey, csvDelimiter, customMetadata); } + /** + * Updates documents in the index + * + * @param document Document to update in JSON or CSV string format + * @param primaryKey PrimaryKey of the document + * @param csvDelimiter Custom delimiter to use for the document being updated + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation Whether to skip creating documents that do not already exist + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo updateDocuments( + String document, + String primaryKey, + String csvDelimiter, + String customMetadata, + boolean skipCreation) + throws MeilisearchException { + return this.documents.updateDocuments( + this.uid, document, primaryKey, csvDelimiter, customMetadata, skipCreation); + } + /** * Updates documents in index in batches * diff --git a/src/test/java/com/meilisearch/sdk/DocumentsSkipCreationTest.java b/src/test/java/com/meilisearch/sdk/DocumentsSkipCreationTest.java new file mode 100644 index 00000000..95c80544 --- /dev/null +++ b/src/test/java/com/meilisearch/sdk/DocumentsSkipCreationTest.java @@ -0,0 +1,73 @@ +package com.meilisearch.sdk; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +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; + +class DocumentsSkipCreationTest { + + private MockWebServer server; + private Index index; + + /** + * Starts an isolated HTTP server so requests can be inspected without a Meilisearch instance. + */ + @BeforeEach + void setup() throws Exception { + server = new MockWebServer(); + server.start(); + + Client client = new Client(new Config(server.url("/").toString(), "masterKey")); + index = client.index("movies"); + } + + /** Releases the server and its connections after each test. */ + @AfterEach + void teardown() throws Exception { + server.shutdown(); + } + + /** Verifies the short add/replace overload sends an explicit true value. */ + @Test + void addDocumentsIncludesSkipCreation() throws Exception { + server.enqueue(new MockResponse().setBody("{\"taskUid\": 1}").setResponseCode(202)); + + index.addDocuments("[]", true); + + RecordedRequest request = server.takeRequest(); + assertThat(request.getMethod(), equalTo("POST")); + assertThat(request.getPath(), equalTo("//indexes/movies/documents?skipCreation=true")); + } + + /** Verifies false is not omitted and existing update parameters are preserved. */ + @Test + void updateDocumentsIncludesSkipCreationAndExistingParameters() throws Exception { + server.enqueue(new MockResponse().setBody("{\"taskUid\": 2}").setResponseCode(202)); + + index.updateDocuments("[]", "id", ";", "import", false); + + RecordedRequest request = server.takeRequest(); + assertThat(request.getMethod(), equalTo("PUT")); + assertThat( + request.getPath(), + equalTo( + "//indexes/movies/documents?primaryKey=id&csvDelimiter=;&customMetadata=import&skipCreation=false")); + } + + /** Verifies legacy overloads leave skipCreation unspecified for backward compatibility. */ + @Test + void existingDocumentMethodsOmitSkipCreation() throws Exception { + server.enqueue(new MockResponse().setBody("{\"taskUid\": 3}").setResponseCode(202)); + + index.addDocuments("[]"); + + RecordedRequest request = server.takeRequest(); + assertThat(request.getPath(), equalTo("//indexes/movies/documents")); + } +}