diff --git a/doc/7/controllers/bulk/delete-by-query/index.md b/doc/7/controllers/bulk/delete-by-query/index.md
new file mode 100644
index 000000000..8a44b45a3
--- /dev/null
+++ b/doc/7/controllers/bulk/delete-by-query/index.md
@@ -0,0 +1,43 @@
+---
+code: true
+type: page
+title: deleteByQuery
+---
+
+# deleteByQuery
+
+Deletes documents matching the provided search query.
+
+This is a low level route intended to bypass Kuzzle actions on document deletion, notably:
+ - check document write limit
+ - trigger [realtime notifications](/core/2/guides/essentials/real-time)
+
+---
+
+```js
+deleteByQuery(index, collection, [query], [options]);
+```
+
+| Argument | Type | Description |
+| ------------ | ----------------- | --------------- |
+| `index` |
string
| Index name |
+| `collection` | string
| Collection name |
+| `query` | object
| documents matching this search query will be deleted. Uses the [ElasticSearch Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl.html) syntax. |
+| `options` | object
| Query options |
+
+### Options
+
+Additional query options
+
+| Options | Type
(default) | Description |
+| ---------- | ------------------------------- | ---------------------------------------------------------------------------------- |
+| `queuable` | boolean
(`true`) | If true, queues the request during downtime, until connected to Kuzzle again |
+| `refresh` | string
(`""`) | If set to `wait_for`, waits for the change to be reflected for `search` (up to 1s) |
+
+## Resolves
+
+Resolves to the number of the deleted documents.
+
+## Usage
+
+<<< ./snippets/delete-by-query.js
diff --git a/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.js b/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.js
new file mode 100644
index 000000000..16cfba6ff
--- /dev/null
+++ b/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.js
@@ -0,0 +1,15 @@
+try {
+ const deleted = await kuzzle.bulk.deleteByQuery(
+ 'nyc-open-data',
+ 'yellow-taxi',
+ {
+ query: {
+ term: { capacity: 7 }
+ }
+ }
+ );
+
+ console.log(`Successfully deleted ${deleted} documents`);
+} catch (error) {
+ console.error(error.message);
+}
\ No newline at end of file
diff --git a/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.test.yml b/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.test.yml
new file mode 100644
index 000000000..f1a308132
--- /dev/null
+++ b/doc/7/controllers/bulk/delete-by-query/snippets/delete-by-query.test.yml
@@ -0,0 +1,20 @@
+name: bulk#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/_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/yellow-taxi/_refresh
+ after:
+template: default
+expected: Successfully deleted 5 documents
diff --git a/src/controllers/Bulk.js b/src/controllers/Bulk.js
index c11e36f4d..2c48f1ce9 100644
--- a/src/controllers/Bulk.js
+++ b/src/controllers/Bulk.js
@@ -5,6 +5,30 @@ class BulkController extends BaseController {
super(kuzzle, 'bulk');
}
+ /**
+ * Directly deletes every documents matching the search query without:
+ * - applying max documents write limit
+ * - fetching deleted documents
+ * - triggering realtime notifications
+ * {@link https://docs.kuzzle.io/core/2/api/controllers/bulk/delete-by-query/|Official documentation}
+ * @param {String} index - Index name
+ * @param {String} collection - Collection name
+ * @param {Object[]} query - Query matching documents to delete
+ * @param {Object} [options] - Additional options
+ * @returns {Promise}
+ */
+ deleteByQuery(index, collection, query = {}, options = {}) {
+ const request = {
+ index,
+ collection,
+ body: query,
+ action: 'deleteByQuery'
+ };
+
+ return this.query(request, options)
+ .then(response => response.result.deleted);
+ }
+
/**
* Creates, updates or deletes large amounts of documents as fast as possible.
* {@link https://docs.kuzzle.io/core/2/api/controllers/bulk/import/|Official documentation}
diff --git a/src/protocols/routes.json b/src/protocols/routes.json
index 1a6836f0d..df55cbad4 100644
--- a/src/protocols/routes.json
+++ b/src/protocols/routes.json
@@ -58,6 +58,10 @@
}
},
"bulk": {
+ "deleteByQuery": {
+ "url": "/:index/:collection/_bulk/_query",
+ "verb": "DELETE"
+ },
"import": {
"verb": "POST",
"url": "/:index/:collection/_bulk"
diff --git a/test/controllers/bulk.test.js b/test/controllers/bulk.test.js
index 8df3e1e3f..750a6792a 100644
--- a/test/controllers/bulk.test.js
+++ b/test/controllers/bulk.test.js
@@ -20,6 +20,27 @@ describe('Bulk Controller', () => {
kuzzle.bulk = new BulkController(kuzzle);
});
+ describe('deleteByQuery', () => {
+ it('should call document/deleteByQuery query and return a Promise which resolves the list of deleted document ids', () => {
+ kuzzle.query.resolves({ result: { deleted: 3 } });
+ const options = {refresh: 'wait_for'};
+ return kuzzle.bulk.deleteByQuery('index', 'collection', { query: { match: { foo: 'bar' } } }, options)
+ .then(res => {
+ should(kuzzle.query)
+ .be.calledOnce()
+ .be.calledWith({
+ controller: 'bulk',
+ action: 'deleteByQuery',
+ index: 'index',
+ collection: 'collection',
+ body: {query: {match: {foo: 'bar' }}}
+ }, options);
+
+ should(res).be.an.Number();
+ should(res).be.equal(3);
+ });
+ });
+ });
describe('import', () => {
it('should call bulk/import query with the bulk data and return a Promise which resolves json object', () => {