From 23bcabe9deb6926c801536b7048c7592b7a33daa Mon Sep 17 00:00:00 2001 From: Osi Obomighie <93836683+Osireg17@users.noreply.github.com> Date: Tue, 18 Nov 2025 21:03:57 +0000 Subject: [PATCH] Add support for custom metadata in document operations --- .../java/com/meilisearch/sdk/Documents.java | 120 +++++++++++++++- src/main/java/com/meilisearch/sdk/Index.java | 105 ++++++++++++++ .../java/com/meilisearch/sdk/model/Task.java | 1 + .../integration/DocumentsTest.java | 134 ++++++++++++++++++ 4 files changed, 358 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/meilisearch/sdk/Documents.java b/src/main/java/com/meilisearch/sdk/Documents.java index a56c87d8..ce671fba 100644 --- a/src/main/java/com/meilisearch/sdk/Documents.java +++ b/src/main/java/com/meilisearch/sdk/Documents.java @@ -161,6 +161,27 @@ 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); + } + + /** + * 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 + * @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) + throws MeilisearchException { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -168,6 +189,9 @@ TaskInfo addDocuments(String uid, String document, String primaryKey, String csv if (csvDelimiter != null) { urlb.addParameter("csvDelimiter", csvDelimiter); } + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } return httpClient.post(urlb.getURL(), document, TaskInfo.class); } @@ -182,6 +206,27 @@ TaskInfo addDocuments(String uid, String document, String primaryKey, String csv */ TaskInfo updateDocuments(String uid, String document, String primaryKey, String csvDelimiter) throws MeilisearchException { + return updateDocuments(uid, document, primaryKey, csvDelimiter, 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 + * @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) + throws MeilisearchException { URLBuilder urlb = documentPath(uid); if (primaryKey != null) { urlb.addParameter("primaryKey", primaryKey); @@ -189,6 +234,9 @@ TaskInfo updateDocuments(String uid, String document, String primaryKey, String if (csvDelimiter != null) { urlb.addParameter("csvDelimiter", csvDelimiter); } + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } return httpClient.put(urlb.getURL(), document, TaskInfo.class); } @@ -201,7 +249,25 @@ TaskInfo updateDocuments(String uid, String document, String primaryKey, String * @throws MeilisearchException if the client request causes an error */ TaskInfo deleteDocument(String uid, String identifier) throws MeilisearchException { - return httpClient.delete(documentPath(uid, identifier).getURL(), TaskInfo.class); + return deleteDocument(uid, identifier, null); + } + + /** + * Deletes the document from the specified index uid with the specified identifier + * + * @param uid Partial index identifier for the requested document + * @param identifier ID of the document + * @param customMetadata Custom metadata to attach to the task + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo deleteDocument(String uid, String identifier, String customMetadata) + throws MeilisearchException { + URLBuilder urlb = documentPath(uid, identifier); + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } + return httpClient.delete(urlb.getURL(), TaskInfo.class); } /** @@ -213,7 +279,24 @@ TaskInfo deleteDocument(String uid, String identifier) throws MeilisearchExcepti * @throws MeilisearchException if the client request causes an error */ TaskInfo deleteDocuments(String uid, List identifiers) throws MeilisearchException { + return deleteDocuments(uid, identifiers, null); + } + + /** + * Deletes the documents at the specified index uid with the specified identifiers + * + * @param uid Partial index identifier for the requested documents + * @param identifiers ID of documents to delete + * @param customMetadata Custom metadata to attach to the task + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo deleteDocuments(String uid, List identifiers, String customMetadata) + throws MeilisearchException { URLBuilder urlb = documentPath(uid).addSubroute("delete-batch"); + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } return httpClient.post(urlb.getURL(), identifiers, TaskInfo.class); } @@ -226,6 +309,20 @@ TaskInfo deleteDocuments(String uid, List identifiers) throws Meilisearc * @throws MeilisearchException if the client request causes an error */ TaskInfo deleteDocumentsByFilter(String uid, String filter) throws MeilisearchException { + return deleteDocumentsByFilter(uid, filter, null); + } + + /** + * Deletes the documents matching the given filter + * + * @param uid Partial index identifier for the requested documents + * @param filter filter to match the documents on + * @param customMetadata Custom metadata to attach to the task + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo deleteDocumentsByFilter(String uid, String filter, String customMetadata) + throws MeilisearchException { if (filter == null || filter.isEmpty()) { throw new MeilisearchException( "Null or blank filter not allowed while deleting documents"); @@ -233,6 +330,9 @@ TaskInfo deleteDocumentsByFilter(String uid, String filter) throws MeilisearchEx HashMap filterMap = new HashMap<>(); filterMap.put("filter", filter); URLBuilder urlb = documentPath(uid).addSubroute("delete"); + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } return httpClient.post(urlb.getURL(), filterMap, TaskInfo.class); } @@ -244,7 +344,23 @@ TaskInfo deleteDocumentsByFilter(String uid, String filter) throws MeilisearchEx * @throws MeilisearchException if the client request causes an error */ TaskInfo deleteAllDocuments(String uid) throws MeilisearchException { - return httpClient.delete(documentPath(uid).getURL(), TaskInfo.class); + return deleteAllDocuments(uid, null); + } + + /** + * Deletes all documents at the specified index uid + * + * @param uid Partial index identifier for the requested documents + * @param customMetadata Custom metadata to attach to the task + * @return Meilisearch's TaskInfo API response + * @throws MeilisearchException if the client request causes an error + */ + TaskInfo deleteAllDocuments(String uid, String customMetadata) throws MeilisearchException { + URLBuilder urlb = documentPath(uid); + if (customMetadata != null) { + urlb.addParameter("customMetadata", customMetadata); + } + return httpClient.delete(urlb.getURL(), TaskInfo.class); } /** Creates an URLBuilder for the constant route documents. */ diff --git a/src/main/java/com/meilisearch/sdk/Index.java b/src/main/java/com/meilisearch/sdk/Index.java index 4d1265a1..12356944 100644 --- a/src/main/java/com/meilisearch/sdk/Index.java +++ b/src/main/java/com/meilisearch/sdk/Index.java @@ -204,6 +204,26 @@ public TaskInfo addDocuments(String document, String primaryKey, String csvDelim return this.documents.addDocuments(this.uid, document, primaryKey, csvDelimiter); } + /** + * 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 + * @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) + throws MeilisearchException { + return this.documents.addDocuments( + this.uid, document, primaryKey, csvDelimiter, customMetadata); + } + /** * Adds/Replaces documents in the index in batches * @@ -298,6 +318,26 @@ public TaskInfo updateDocuments(String document, String primaryKey, String csvDe return this.documents.updateDocuments(this.uid, document, primaryKey, csvDelimiter); } + /** + * 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 + * @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) + throws MeilisearchException { + return this.documents.updateDocuments( + this.uid, document, primaryKey, csvDelimiter, customMetadata); + } + /** * Updates documents in index in batches * @@ -359,6 +399,22 @@ public TaskInfo deleteDocument(String identifier) throws MeilisearchException { return this.documents.deleteDocument(this.uid, identifier); } + /** + * Deletes a document from the index + * + * @param identifier Identifier of the document to delete + * @param customMetadata Custom metadata to attach to the task + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo deleteDocument(String identifier, String customMetadata) + throws MeilisearchException { + return this.documents.deleteDocument(this.uid, identifier, customMetadata); + } + /** * Deletes list of documents from the index * @@ -375,6 +431,24 @@ public TaskInfo deleteDocuments(List documentsIdentifiers) throws Meilis return this.documents.deleteDocuments(this.uid, documentsIdentifiers); } + /** + * Deletes list of documents from the index + * + * @param documentsIdentifiers list of identifiers of documents to delete + * @param customMetadata Custom metadata to attach to the task + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + * @see com.meilisearch.sdk.Index#deleteDocumentsByFilter(String) Delete documents using filter + */ + @Deprecated + public TaskInfo deleteDocuments(List documentsIdentifiers, String customMetadata) + throws MeilisearchException { + return this.documents.deleteDocuments(this.uid, documentsIdentifiers, customMetadata); + } + /** * Deletes list of documents from the index using the given filter * @@ -390,6 +464,23 @@ public TaskInfo deleteDocumentsByFilter(String filter) throws MeilisearchExcepti return this.documents.deleteDocumentsByFilter(this.uid, filter); } + /** + * Deletes list of documents from the index using the given filter + * + * @param filter filter to match the documents to delete + * @param customMetadata Custom metadata to attach to the task + * @return TaskInfo Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + * @since 1.2 + */ + public TaskInfo deleteDocumentsByFilter(String filter, String customMetadata) + throws MeilisearchException { + return this.documents.deleteDocumentsByFilter(this.uid, filter, customMetadata); + } + /** * Deletes all documents in the index * @@ -403,6 +494,20 @@ public TaskInfo deleteAllDocuments() throws MeilisearchException { return this.documents.deleteAllDocuments(this.uid); } + /** + * Deletes all documents in the index + * + * @param customMetadata Custom metadata to attach to the task + * @return List of tasks Meilisearch API response + * @throws MeilisearchException if an error occurs + * @see API + * specification + */ + public TaskInfo deleteAllDocuments(String customMetadata) throws MeilisearchException { + return this.documents.deleteAllDocuments(this.uid, customMetadata); + } + /** * Searches documents in the index * diff --git a/src/main/java/com/meilisearch/sdk/model/Task.java b/src/main/java/com/meilisearch/sdk/model/Task.java index fe1940d0..d2995e66 100644 --- a/src/main/java/com/meilisearch/sdk/model/Task.java +++ b/src/main/java/com/meilisearch/sdk/model/Task.java @@ -20,6 +20,7 @@ public class Task { protected Date finishedAt = null; protected TaskError error = null; protected TaskDetails details = null; + protected String customMetadata = null; public Task() {} } diff --git a/src/test/java/com/meilisearch/integration/DocumentsTest.java b/src/test/java/com/meilisearch/integration/DocumentsTest.java index 1a5e28c5..68ff37d0 100644 --- a/src/test/java/com/meilisearch/integration/DocumentsTest.java +++ b/src/test/java/com/meilisearch/integration/DocumentsTest.java @@ -731,4 +731,138 @@ public void testDeleteAllDocuments() throws Exception { movies = index.getDocuments(Movie.class).getResults(); assertThat(movies, is(arrayWithSize(0))); } + + /** Test addDocuments with customMetadata */ + @Test + public void testAddDocumentsWithCustomMetadata() throws Exception { + String indexUid = "AddDocumentsWithCustomMetadata"; + Index index = client.index(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + String singleDocument = this.gson.toJson(testData.getData().get(0)); + TaskInfo task = + index.addDocuments("[" + singleDocument + "]", null, null, "add-movies-metadata"); + + index.waitForTask(task.getTaskUid()); + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentAdditionOrUpdate"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("add-movies-metadata"))); + } + + /** Test updateDocuments with customMetadata */ + @Test + public void testUpdateDocumentsWithCustomMetadata() throws Exception { + String indexUid = "UpdateDocumentsWithCustomMetadata"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + String updateDocument = + "[{\"id\":\"419704\",\"title\":\"Ad Astra Updated\",\"genre\":[\"Drama\"]}]"; + TaskInfo task = index.updateDocuments(updateDocument, null, null, "update-movies-metadata"); + + index.waitForTask(task.getTaskUid()); + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentAdditionOrUpdate"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("update-movies-metadata"))); + } + + /** Test deleteDocument with customMetadata */ + @Test + public void testDeleteDocumentWithCustomMetadata() throws Exception { + String indexUid = "DeleteDocumentWithCustomMetadata"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + TaskInfo task = index.deleteDocument("419704", "delete-single-movie-metadata"); + index.waitForTask(task.getTaskUid()); + + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentDeletion"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("delete-single-movie-metadata"))); + } + + /** Test deleteDocuments (batch) with customMetadata */ + @Test + public void testDeleteDocumentsBatchWithCustomMetadata() throws Exception { + String indexUid = "DeleteDocumentsBatchWithCustomMetadata"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + List identifiers = Arrays.asList("419704", "574982"); + TaskInfo task = index.deleteDocuments(identifiers, "delete-batch-metadata"); + index.waitForTask(task.getTaskUid()); + + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentDeletion"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("delete-batch-metadata"))); + } + + /** Test deleteDocumentsByFilter with customMetadata */ + @Test + public void testDeleteDocumentsByFilterWithCustomMetadata() throws Exception { + String indexUid = "DeleteDocumentsByFilterWithCustomMetadata"; + Index index = createEmptyIndex(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + index.waitForTask( + index.updateFilterableAttributesSettings(new String[] {"id"}).getTaskUid()); + + TaskInfo task = index.deleteDocumentsByFilter("id = 419704", "delete-filter-metadata"); + index.waitForTask(task.getTaskUid()); + + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentDeletion"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("delete-filter-metadata"))); + } + + /** Test deleteAllDocuments with customMetadata */ + @Test + public void testDeleteAllDocumentsWithCustomMetadata() throws Exception { + String indexUid = "DeleteAllDocumentsWithCustomMetadata"; + Index index = client.index(indexUid); + + TestData testData = this.getTestData(MOVIES_INDEX, Movie.class); + TaskInfo addTask = index.addDocuments(testData.getRaw()); + index.waitForTask(addTask.getTaskUid()); + + TaskInfo task = index.deleteAllDocuments("delete-all-metadata"); + index.waitForTask(task.getTaskUid()); + + assertThat(task, is(instanceOf(TaskInfo.class))); + assertThat(task.getType(), is(equalTo("documentDeletion"))); + + // Verify the task was created with custom metadata + com.meilisearch.sdk.model.Task taskDetails = index.getTask(task.getTaskUid()); + assertThat(taskDetails.getCustomMetadata(), is(equalTo("delete-all-metadata"))); + + Results result = index.getDocuments(Movie.class); + assertThat(result.getResults(), is(arrayWithSize(0))); + } }