diff --git a/.ci/doc/templates/print-result-arraylist.tpl.java b/.ci/doc/templates/print-result-arraylist.tpl.java new file mode 100644 index 00000000..ca5a96dc --- /dev/null +++ b/.ci/doc/templates/print-result-arraylist.tpl.java @@ -0,0 +1,30 @@ +import io.kuzzle.sdk.Kuzzle; +import io.kuzzle.sdk.Protocol.WebSocket; +import io.kuzzle.sdk.Options.Protocol.WebSocketOptions; +import io.kuzzle.sdk.Options.KuzzleOptions; +import io.kuzzle.sdk.Options.SubscribeOptions; +import io.kuzzle.sdk.CoreClasses.Responses.Response; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.ArrayList; + +public class SnippetTest { + private static Kuzzle kuzzle; + + public static void main(String[] argv) { + try { + kuzzle = new Kuzzle(new WebSocket("kuzzle")); + kuzzle.connect(); + [snippet-code] + for (Object o : result) { + System.out.println(o); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (kuzzle != null) { + kuzzle.disconnect(); + } + } + } +} \ No newline at end of file diff --git a/doc/3/controllers/document/delete-by-query/index.md b/doc/3/controllers/document/delete-by-query/index.md new file mode 100644 index 00000000..c59a5abc --- /dev/null +++ b/doc/3/controllers/document/delete-by-query/index.md @@ -0,0 +1,46 @@ +--- +code: true +type: page +title: deleteByQuery +description: Delete documents matching query +--- + +# deleteByQuery + +Deletes documents matching the provided search query. + +Kuzzle uses the [ElasticSearch Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl.html) syntax. + +An empty or null query will match all documents in the collection. + +
+ +```java + public CompletableFuture> deleteByQuery( + final String index, + final String collection, + final ConcurrentHashMap searchQuery) throws NotConnectedException, InternalException + + public CompletableFuture> deleteByQuery( + final String index, + final String collection, + final ConcurrentHashMap searchQuery, + final Boolean waitForRefresh) throws NotConnectedException, InternalException +``` + +| Argument | Type | Description | +| ------------------ | -------------------------------------------- | --------------- | +| `index` |
String
| Index name | +| `collection` |
String
| Collection name | +| `searchQuery` |
ConcurrentHashMap
| Query to match | +| `waitForRefresh` |
Boolean
(optional) | If set to `true`, Kuzzle will wait for the persistence layer to finish indexing| + +--- + +## Returns + +Returns an `ArrayList` containing the deleted document ids. + +## Usage + +<<< ./snippets/delete-by-query.js \ No newline at end of file diff --git a/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.java b/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.java new file mode 100644 index 00000000..46b10a9a --- /dev/null +++ b/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.java @@ -0,0 +1,10 @@ + ConcurrentHashMap searchQuery = new ConcurrentHashMap<>(); + ConcurrentHashMap query = new ConcurrentHashMap<>(); + ConcurrentHashMap match = new ConcurrentHashMap<>(); + match.put("capacity", 4); + query.put("match", match); + searchQuery.put("query", query); + ArrayList result = kuzzle + .getDocumentController() + .deleteByQuery("nyc-open-data", "yellow-taxi", searchQuery) + .get(); diff --git a/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.test.yml b/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.test.yml new file mode 100644 index 00000000..5d3fd03d --- /dev/null +++ b/doc/3/controllers/document/delete-by-query/snippets/delete-by-query.test.yml @@ -0,0 +1,22 @@ +name: document#deleteByQuery +description: Delete documents matching query +hooks: + before: | + curl -XDELETE kuzzle:7512/nyc-open-data + curl -XPOST kuzzle:7512/nyc-open-data/_create + curl -XPUT kuzzle:7512/nyc-open-data/yellow-taxi + for i in 1 2 3 4 5; do + curl -H "Content-type: application/json" -d '{"capacity": 4}' kuzzle:7512/nyc-open-data/yellow-taxi/document_$i/_create + done + for i in 1 2 3 4 5; do + curl -H "Content-type: application/json" -d '{"capacity": 7}' kuzzle:7512/nyc-open-data/yellow-taxi/_create + done + curl -XPOST kuzzle:7512/nyc-open-data/_refresh + after: +template: print-result-arraylist +expected: + - "document_1" + - "document_2" + - "document_3" + - "document_4" + - "document_5" \ No newline at end of file diff --git a/src/main/java/io/kuzzle/sdk/API/Controllers/DocumentController.java b/src/main/java/io/kuzzle/sdk/API/Controllers/DocumentController.java index 07a43590..9e404ad6 100644 --- a/src/main/java/io/kuzzle/sdk/API/Controllers/DocumentController.java +++ b/src/main/java/io/kuzzle/sdk/API/Controllers/DocumentController.java @@ -653,6 +653,57 @@ public CompletableFuture>> mCreateOr return this.mCreateOrReplace(index, collection, documents, null); } + /** + * Deletes documents matching the provided search query. + * + * @param index + * @param collection + * @param searchQuery + * @param waitForRefresh + * @return a CompletableFuture + * @throws NotConnectedException + * @throws InternalException + */ + public CompletableFuture> deleteByQuery( + final String index, + final String collection, + final ConcurrentHashMap searchQuery, + final Boolean waitForRefresh) throws NotConnectedException, InternalException { + + final KuzzleMap query = new KuzzleMap(); + + query + .put("index", index) + .put("collection", collection) + .put("controller", "document") + .put("action", "deleteByQuery") + .put("body", new KuzzleMap(searchQuery)) + .put("waitForRefresh", waitForRefresh); + + return kuzzle + .query(query) + .thenApplyAsync( + (response) -> (ArrayList)((ConcurrentHashMap) response.result).get("ids")); + } + + /** + * Deletes documents matching the provided search query. + * + * @param index + * @param collection + * @param searchQuery + * @return a CompletableFuture + * @throws NotConnectedException + * @throws InternalException + */ + public CompletableFuture> deleteByQuery( + final String index, + final String collection, + final ConcurrentHashMap searchQuery) throws NotConnectedException, InternalException { + + return this.deleteByQuery(index, collection, searchQuery, null); + } + /** * Validates data against existing validation rules. * diff --git a/src/test/java/io/kuzzle/test/API/Controllers/DocumentTest/DocumentTest.java b/src/test/java/io/kuzzle/test/API/Controllers/DocumentTest/DocumentTest.java index f1b79737..c1bf6b00 100644 --- a/src/test/java/io/kuzzle/test/API/Controllers/DocumentTest/DocumentTest.java +++ b/src/test/java/io/kuzzle/test/API/Controllers/DocumentTest/DocumentTest.java @@ -26,6 +26,37 @@ public class DocumentTest { private AbstractProtocol networkProtocol = Mockito.mock(WebSocket.class); + @Test + public void getDocumentTest() throws NotConnectedException, InternalException { + + Kuzzle kuzzleMock = spy(new Kuzzle(networkProtocol)); + String index = "nyc-open-data"; + String collection = "yellow-taxi"; + + ArgumentCaptor arg = ArgumentCaptor.forClass(KuzzleMap.class); + + kuzzleMock.getDocumentController().get(index, collection, "some-id"); + Mockito.verify(kuzzleMock, Mockito.times(1)).query((KuzzleMap) arg.capture()); + + assertEquals(((KuzzleMap) arg.getValue()).getString("controller"), "document"); + assertEquals(((KuzzleMap) arg.getValue()).getString("action"), "get"); + assertEquals(((KuzzleMap) arg.getValue()).getString("index"), "nyc-open-data"); + assertEquals(((KuzzleMap) arg.getValue()).getString("_id"), "some-id"); + } + + @Test(expected = NotConnectedException.class) + public void getDocumentShouldThrowWhenNotConnected() throws NotConnectedException, InternalException { + AbstractProtocol fakeNetworkProtocol = Mockito.mock(WebSocket.class); + Mockito.when(fakeNetworkProtocol.getState()).thenAnswer((Answer) invocation -> ProtocolState.CLOSE); + + Kuzzle kuzzleMock = spy(new Kuzzle(fakeNetworkProtocol)); + String index = "nyc-open-data"; + String collection = "yellow-taxi"; + + kuzzleMock.getDocumentController().get(index, collection, "some-id"); + } + + @Test public void createDocumentTestA() throws NotConnectedException, InternalException { @@ -82,7 +113,6 @@ public void createDocumentTestB() throws NotConnectedException, InternalExceptio @Test(expected = NotConnectedException.class) public void createDocumentThrowWhenNotConnected() throws NotConnectedException, InternalException { - AbstractProtocol fakeNetworkProtocol = Mockito.mock(WebSocket.class); Mockito.when(fakeNetworkProtocol.getState()).thenAnswer((Answer) invocation -> ProtocolState.CLOSE); @@ -97,36 +127,6 @@ public void createDocumentThrowWhenNotConnected() throws NotConnectedException, kuzzleMock.getDocumentController().create(index, collection, document); } - @Test - public void getDocumentTest() throws NotConnectedException, InternalException { - - Kuzzle kuzzleMock = spy(new Kuzzle(networkProtocol)); - String index = "nyc-open-data"; - String collection = "yellow-taxi"; - - ArgumentCaptor arg = ArgumentCaptor.forClass(KuzzleMap.class); - - kuzzleMock.getDocumentController().get(index, collection, "some-id"); - Mockito.verify(kuzzleMock, Mockito.times(1)).query((KuzzleMap) arg.capture()); - - assertEquals(((KuzzleMap) arg.getValue()).getString("controller"), "document"); - assertEquals(((KuzzleMap) arg.getValue()).getString("action"), "get"); - assertEquals(((KuzzleMap) arg.getValue()).getString("index"), "nyc-open-data"); - assertEquals(((KuzzleMap) arg.getValue()).getString("_id"), "some-id"); - } - - @Test(expected = NotConnectedException.class) - public void getDocumentShouldThrowWhenNotConnected() throws NotConnectedException, InternalException { - AbstractProtocol fakeNetworkProtocol = Mockito.mock(WebSocket.class); - Mockito.when(fakeNetworkProtocol.getState()).thenAnswer((Answer) invocation -> ProtocolState.CLOSE); - - Kuzzle kuzzleMock = spy(new Kuzzle(fakeNetworkProtocol)); - String index = "nyc-open-data"; - String collection = "yellow-taxi"; - - kuzzleMock.getDocumentController().get(index, collection, "some-id"); - } - @Test public void createOrReplaceDocumentTestA() throws NotConnectedException, InternalException { @@ -258,7 +258,24 @@ public void updateShouldThrowWhenNotConnected() throws NotConnectedException, In ConcurrentHashMap document = new ConcurrentHashMap<>(); document.put("name", "Yoann"); - kuzzleMock.getDocumentController().update(index, collection, id, document); + UpdateOptions options = new UpdateOptions(); + options.setWaitForRefresh(false); + options.setSource(true); + options.setRetryOnConflict(1); + + ArgumentCaptor arg = ArgumentCaptor.forClass(KuzzleMap.class); + + kuzzleMock.getDocumentController().update(index, collection, "some-id", document, options); + Mockito.verify(kuzzleMock, Mockito.times(1)).query((KuzzleMap) arg.capture()); + + assertEquals(((KuzzleMap) arg.getValue()).getString("controller"), "document"); + assertEquals(((KuzzleMap) arg.getValue()).getString("action"), "update"); + assertEquals(((KuzzleMap) arg.getValue()).getString("index"), "nyc-open-data"); + assertEquals(((KuzzleMap) arg.getValue()).getString("_id"), "some-id"); + assertEquals(((KuzzleMap) arg.getValue()).getNumber("retryOnConflict"), 1); + assertEquals(((KuzzleMap) arg.getValue()).getBoolean("waitForRefresh"), false); + assertEquals(((KuzzleMap) arg.getValue()).getBoolean("source"), true); + assertEquals(((ConcurrentHashMap) (((KuzzleMap) arg.getValue()).get("body"))).get("name").toString(), "Yoann"); } @Test @@ -919,6 +936,75 @@ public void mCreateOrReplaceDocumentShouldThrowWhenNotConnected() throws NotConn kuzzleMock.getDocumentController().mCreateOrReplace(index, collection, documents); } + @Test + public void deleteByQueryDocumentTestA() throws NotConnectedException, InternalException { + + Kuzzle kuzzleMock = spy(new Kuzzle(networkProtocol)); + String index = "nyc-open-data"; + String collection = "yellow-taxi"; + + ConcurrentHashMap searchQuery = new ConcurrentHashMap<>(); + ConcurrentHashMap query = new ConcurrentHashMap<>(); + ConcurrentHashMap match = new ConcurrentHashMap<>(); + match.put("Hello", "Clarisse"); + query.put("match", match); + searchQuery.put("query", query); + + ArgumentCaptor arg = ArgumentCaptor.forClass(KuzzleMap.class); + + kuzzleMock.getDocumentController().deleteByQuery(index, collection, searchQuery); + Mockito.verify(kuzzleMock, Mockito.times(1)).query(arg.capture()); + + assertEquals((arg.getValue()).getString("controller"), "document"); + assertEquals((arg.getValue()).getString("action"), "deleteByQuery"); + assertEquals((arg.getValue()).getString("index"), "nyc-open-data"); + assertEquals((arg.getValue()).getBoolean("waitForRefresh"), null); + assertEquals(((ConcurrentHashMap) ((ConcurrentHashMap) (((KuzzleMap) (arg.getValue()).get("body"))).get("query")).get("match")).get("Hello"), "Clarisse"); + } + + @Test + public void deleteByQueryDocumentTestB() throws NotConnectedException, InternalException { + + Kuzzle kuzzleMock = spy(new Kuzzle(networkProtocol)); + String index = "nyc-open-data"; + String collection = "yellow-taxi"; + + ConcurrentHashMap searchQuery = new ConcurrentHashMap<>(); + ConcurrentHashMap query = new ConcurrentHashMap<>(); + ConcurrentHashMap match = new ConcurrentHashMap<>(); + match.put("Hello", "Clarisse"); + query.put("match", match); + searchQuery.put("query", query); + + ArgumentCaptor arg = ArgumentCaptor.forClass(KuzzleMap.class); + + kuzzleMock.getDocumentController().deleteByQuery(index, collection, searchQuery, false); + Mockito.verify(kuzzleMock, Mockito.times(1)).query(arg.capture()); + + assertEquals((arg.getValue()).getString("controller"), "document"); + assertEquals((arg.getValue()).getString("action"), "deleteByQuery"); + assertEquals((arg.getValue()).getString("index"), "nyc-open-data"); + assertEquals((arg.getValue()).getBoolean("waitForRefresh"), false); + assertEquals(((ConcurrentHashMap) ((ConcurrentHashMap) (((KuzzleMap) (arg.getValue()).get("body"))).get("query")).get("match")).get("Hello"), "Clarisse"); + } + + @Test(expected = NotConnectedException.class) + public void deleteByQueryDocumentShouldThrowWhenNotConnected() throws NotConnectedException, InternalException { + AbstractProtocol fakeNetworkProtocol = Mockito.mock(WebSocket.class); + Mockito.when(fakeNetworkProtocol.getState()).thenAnswer((Answer) invocation -> ProtocolState.CLOSE); + + Kuzzle kuzzleMock = spy(new Kuzzle(fakeNetworkProtocol)); + String index = "nyc-open-data"; + String collection = "yellow-taxi"; + + ConcurrentHashMap searchQuery = new ConcurrentHashMap<>(); + ConcurrentHashMap match = new ConcurrentHashMap<>(); + match.put("Hello", "Clarisse"); + searchQuery.put("match", match); + + kuzzleMock.getDocumentController().deleteByQuery(index, collection, searchQuery); + } + @Test public void validateDocumentTest() throws NotConnectedException, InternalException {