From 722d9aa98c7cf32483b765adeabfc4edfd694a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiberiu=20Sab=C4=83u?= Date: Tue, 27 Jan 2026 17:45:25 +0100 Subject: [PATCH] Allow skipCreation when adding/replacing documents --- .code-samples.meilisearch.yaml | 14 ++- .../java/com/meilisearch/sdk/Documents.java | 56 ++++++++- src/main/java/com/meilisearch/sdk/Index.java | 54 ++++++++- .../integration/DocumentsTest.java | 113 ++++++++++++++++++ 4 files changed, 230 insertions(+), 7 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index bd219bd6..66b4c799 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -40,14 +40,22 @@ 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\"" - + "}]" + + "}]", + null, + null, + null, + false ); add_or_update_documents_1: |- - client.index("movies").updateDocuments("[{ + client.index("movies").updateDocuments("[{" + "\"id\": 287947," + "\"title\": \"Shazam ⚡️\"," + "\"genres\": \"comedy\"" - + "}]" + + "}]", + null, + null, + null, + false ); 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..d7ce2de5 100644 --- a/src/main/java/com/meilisearch/sdk/Documents.java +++ b/src/main/java/com/meilisearch/sdk/Documents.java @@ -161,7 +161,7 @@ String getRawDocuments(String uid, DocumentsQuery param) throws MeilisearchExcep */ TaskInfo addDocuments(String uid, String document, String primaryKey, String csvDelimiter) throws MeilisearchException { - return addDocuments(uid, document, primaryKey, csvDelimiter, null); + return addDocuments(uid, document, primaryKey, csvDelimiter, null, null); } /** @@ -182,6 +182,29 @@ TaskInfo addDocuments( String csvDelimiter, String customMetadata) throws MeilisearchException { + return addDocuments(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 If true, skips document creation and only updates existing documents + * @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 { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -192,6 +215,9 @@ TaskInfo addDocuments( if (customMetadata != null) { urlb.addParameter("customMetadata", customMetadata); } + if (skipCreation != null) { + urlb.addParameter("skipCreation", skipCreation.toString()); + } return httpClient.post(urlb.getURL(), document, TaskInfo.class); } @@ -206,7 +232,7 @@ TaskInfo addDocuments( */ TaskInfo updateDocuments(String uid, String document, String primaryKey, String csvDelimiter) throws MeilisearchException { - return updateDocuments(uid, document, primaryKey, csvDelimiter, null); + return updateDocuments(uid, document, primaryKey, csvDelimiter, null, null); } /** @@ -227,6 +253,29 @@ TaskInfo updateDocuments( String csvDelimiter, String customMetadata) throws MeilisearchException { + return updateDocuments(uid, document, primaryKey, csvDelimiter, customMetadata, null); + } + + /** + * Replaces a document at the specified index uid + * + * @param uid Partial index identifier for the document + * @param document String containing the document to replace the existing document + * @param primaryKey PrimaryKey of the document + * @param csvDelimiter CSV delimiter of the document + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation If true, skips document creation and only updates existing documents + * @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 { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -237,6 +286,9 @@ TaskInfo updateDocuments( if (customMetadata != null) { urlb.addParameter("customMetadata", customMetadata); } + if (skipCreation != null) { + urlb.addParameter("skipCreation", skipCreation.toString()); + } 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 12356944..18ed8d03 100644 --- a/src/main/java/com/meilisearch/sdk/Index.java +++ b/src/main/java/com/meilisearch/sdk/Index.java @@ -221,7 +221,32 @@ public TaskInfo addDocuments( String document, String primaryKey, String csvDelimiter, String customMetadata) throws MeilisearchException { return this.documents.addDocuments( - this.uid, document, primaryKey, csvDelimiter, customMetadata); + this.uid, document, primaryKey, csvDelimiter, customMetadata, null); + } + + /** + * 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 If true, skips document creation and only updates existing documents + * @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); } /** @@ -335,7 +360,32 @@ public TaskInfo updateDocuments( String document, String primaryKey, String csvDelimiter, String customMetadata) throws MeilisearchException { return this.documents.updateDocuments( - this.uid, document, primaryKey, csvDelimiter, customMetadata); + this.uid, document, primaryKey, csvDelimiter, customMetadata, null); + } + + /** + * 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 added + * @param customMetadata Custom metadata to attach to the task + * @param skipCreation If true, skips document creation and only updates existing documents + * @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); } /** diff --git a/src/test/java/com/meilisearch/integration/DocumentsTest.java b/src/test/java/com/meilisearch/integration/DocumentsTest.java index 68ff37d0..491e99af 100644 --- a/src/test/java/com/meilisearch/integration/DocumentsTest.java +++ b/src/test/java/com/meilisearch/integration/DocumentsTest.java @@ -865,4 +865,117 @@ public void testDeleteAllDocumentsWithCustomMetadata() throws Exception { Results result = index.getDocuments(Movie.class); assertThat(result.getResults(), is(arrayWithSize(0))); } + + /** Test addDocuments with skipCreation set to true */ + @Test + public void testAddDocumentsWithSkipCreationTrue() throws Exception { + String indexUid = "AddDocumentsWithSkipCreationTrue"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + + // First, add a document + String firstDocument = this.gson.toJson(testData.getData().get(0)); + TaskInfo addTask = index.addDocuments("[" + firstDocument + "]"); + index.waitForTask(addTask.getTaskUid()); + + // Verify one document exists + Results result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(1))); + assertThat(result.getResults()[0].getTitle(), is(equalTo("Ad Astra"))); + + // Try to add a new document with skipCreation=true, it should not be added + String newDocument = this.gson.toJson(testData.getData().get(1)); + TaskInfo skipTask = index.addDocuments("[" + newDocument + "]", null, null, null, true); + index.waitForTask(skipTask.getTaskUid()); + + // Verify still only one document exists (new one was not created) + result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(1))); + + // Now update existing document with skipCreation=true, it should work + String updatedDocument = "[{\"id\":\"419704\",\"title\":\"Ad Astra Updated\"}]"; + TaskInfo updateTask = index.addDocuments(updatedDocument, null, null, null, true); + index.waitForTask(updateTask.getTaskUid()); + + // Verify document was updated + Movie movie = index.getDocument("419704", Movie.class); + assertThat(movie.getTitle(), is(equalTo("Ad Astra Updated"))); + } + + /** Test addDocuments with skipCreation set to false */ + @Test + public void testAddDocumentsWithSkipCreationFalse() throws Exception { + String indexUid = "AddDocumentsWithSkipCreationFalse"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + + // Add documents with skipCreation=false (should create new documents) + TaskInfo task = index.addDocuments(testData.getRaw(), null, null, null, false); + index.waitForTask(task.getTaskUid()); + + // Verify all documents were created + Results result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(testData.getData().size()))); + } + + /** Test updateDocuments with skipCreation set to true */ + @Test + public void testUpdateDocumentsWithSkipCreationTrue() throws Exception { + String indexUid = "UpdateDocumentsWithSkipCreationTrue"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + + // First, add documents + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + // Verify documents exist + Results result = index.getDocuments(Movie.class); + int initialCount = result.getResults().length; + assertThat(initialCount, is(equalTo(testData.getData().size()))); + + // Try to update with a new document with skipCreation=true (should not be added) + String newDocument = "[{\"id\":\"999999\",\"title\":\"New Movie\"}]"; + TaskInfo skipTask = index.updateDocuments(newDocument, null, null, null, true); + index.waitForTask(skipTask.getTaskUid()); + + // Verify document count hasn't changed (new one was not created) + result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(initialCount))); + + // Now update existing document with skipCreation=true, it should work + String updatedDocument = "[{\"id\":\"419704\",\"title\":\"Updated Title\"}]"; + TaskInfo updateTask = index.updateDocuments(updatedDocument, null, null, null, true); + index.waitForTask(updateTask.getTaskUid()); + + // Verify document was updated + Movie movie = index.getDocument("419704", Movie.class); + assertThat(movie.getTitle(), is(equalTo("Updated Title"))); + } + + /** Test updateDocuments with skipCreation set to false */ + @Test + public void testUpdateDocumentsWithSkipCreationFalse() throws Exception { + String indexUid = "UpdateDocumentsWithSkipCreationFalse"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + + // First, add one document + String firstDocument = this.gson.toJson(testData.getData().get(0)); + TaskInfo addTask = index.addDocuments("[" + firstDocument + "]"); + index.waitForTask(addTask.getTaskUid()); + + // Update with new document and skipCreation=false (should add the new document) + String newDocument = "[{\"id\":\"999999\",\"title\":\"New Movie\"}]"; + TaskInfo task = index.updateDocuments(newDocument, null, null, null, false); + index.waitForTask(task.getTaskUid()); + + // Verify new document was created + Results result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(2))); + } }