Skip to content

Commit f51b9d3

Browse files
authored
Add document:updateByQuery (#519)
## What does this PR do? This PR adds `document:updateByQuery` to the sdk Issue #518
1 parent d877d6f commit f51b9d3

File tree

9 files changed

+252
-11
lines changed

9 files changed

+252
-11
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Loads the Kuzzle SDK module and the websocket protocol
2+
const {
3+
Kuzzle,
4+
WebSocket
5+
} = require('kuzzle-sdk');
6+
7+
// Instantiates a Kuzzle client
8+
const
9+
kuzzle = new Kuzzle(
10+
new WebSocket('kuzzle', { autoReconnect: false })
11+
);
12+
13+
// Adds a listener to detect connection problems
14+
kuzzle.on('networkError', error => {
15+
console.error(`Network Error: ${error.message}`);
16+
});
17+
18+
(async () => {
19+
let result;
20+
try {
21+
await kuzzle.connect();
22+
} catch (error) {
23+
console.log(`Cannot connect to Kuzzle: ${error.message}`);
24+
}
25+
[snippet-code] finally {
26+
kuzzle.disconnect();
27+
}
28+
for (const elem of result.successes) {
29+
console.log(elem);
30+
}
31+
})();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
code: true
3+
type: page
4+
title: updateByQuery
5+
description: Updates documents matching query
6+
---
7+
8+
# updateByQuery
9+
10+
Updates documents matching the provided search query.
11+
12+
Kuzzle uses the [ElasticSearch Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl.html) syntax.
13+
14+
An empty or null query will match all documents in the collection.
15+
16+
<br/>
17+
18+
```js
19+
updateByQuery(index, collection, searchQuery, changes, [options])
20+
```
21+
22+
| Argument | Type | Description |
23+
| ------------------ | -------------------------------------------- | --------------- |
24+
| `index` | <pre>string</pre> | Index name |
25+
| `collection` | <pre>string</pre> | Collection name |
26+
| `searchQuery` | <pre>object</pre> | Query to match |
27+
| `changes` | <pre>object</pre> | Partial changes to apply to the documents |
28+
| `options` | <pre>object</pre> | Optional parameters |
29+
30+
---
31+
32+
### options
33+
34+
Additional query options.
35+
36+
| Options | Type<br/>(default) | Description |
37+
| ----------------- | ------------------------------- | ---------------------------------------------------------------------------------- |
38+
| `refresh` | <pre>string</pre><br/>(`""`) | If set to `wait_for`, waits for the change to be reflected for `search` (up to 1s) |
39+
| `source` | <pre>boolean</pre><br/>(`false`)| If true, returns the updated document inside the response
40+
41+
## Resolves
42+
43+
Returns an object containing 2 arrays: `successes` and `errors`
44+
45+
Each updated document is an object of the `successes` array with the following properties:
46+
47+
| Property | Type | Description |
48+
|------------- |--------------------------------------------- |--------------------------------- |
49+
| `_source` | <pre>object<String, Object></pre> | Updated document (if `source` option set to true) |
50+
| `_id` | <pre>string</pre> | ID of the udated document |
51+
| `_version` | <pre>number</pre> | Version of the document in the persistent data storage |
52+
| `status` | <pre>number</pre> | HTTP status code |
53+
54+
Each errored document is an object of the `errors` array with the following properties:
55+
56+
| Property | Type | Description |
57+
|------------- |--------------------------------------------- |--------------------------------- |
58+
| `document` | <pre>object<String, Object></pre> | Document that causes the error |
59+
| `status` | <pre>number</pre> | HTTP error status |
60+
| `reason` | <pre>string</pre> | Human readable reason |
61+
62+
## Usage
63+
64+
<<< ./snippets/update-by-query.js
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
try {
2+
result = await kuzzle.document.updateByQuery(
3+
'nyc-open-data',
4+
'yellow-taxi',
5+
{
6+
match: {
7+
capacity: 4
8+
}
9+
}, {
10+
capacity: 42
11+
});
12+
/*
13+
{
14+
successes: [
15+
{
16+
_id: <document-id>,
17+
_source: <updated document> // if source set to true
18+
status: 200
19+
},
20+
{
21+
_id: <document id>,
22+
_source: <updated document> // if source set to true
23+
status: 200
24+
}
25+
],
26+
errors: []
27+
}
28+
*/
29+
} catch (error) {
30+
console.log(error.message);
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: document#updateByQuery
2+
description: Update documents matching query
3+
hooks:
4+
before: |
5+
curl -XDELETE kuzzle:7512/nyc-open-data
6+
curl -XPOST kuzzle:7512/nyc-open-data/_create
7+
curl -XPUT kuzzle:7512/nyc-open-data/yellow-taxi
8+
for i in 1 2 ; do
9+
curl -H "Content-type: application/json" -d '{"capacity": 4}' kuzzle:7512/nyc-open-data/yellow-taxi/document_$i/_create
10+
done
11+
for i in 1 2 3 4 5; do
12+
curl -H "Content-type: application/json" -d '{"capacity": 7}' kuzzle:7512/nyc-open-data/yellow-taxi/_create
13+
done
14+
curl -XPOST kuzzle:7512/nyc-open-data/yellow-taxi/_refresh
15+
after:
16+
template: print-result-successes
17+
expected:
18+
- "{ _id: 'document_1', _version: 2, status: 200 }"
19+
- "{ _id: 'document_2', _version: 2, status: 200 }"

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"@babel/core": "^7.9.0",
4646
"@babel/preset-env": "^7.9.5",
4747
"babel-loader": "^8.1.0",
48-
"codecov": "^3.6.5",
48+
"codecov": "^3.7.0",
4949
"cucumber": "^6.0.5",
5050
"eslint": "^6.8.0",
5151
"eslint-friendly-formatter": "^4.0.1",

src/controllers/Document.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ class DocumentController extends BaseController {
220220
.then(response => response.result);
221221
}
222222

223+
updateByQuery(index, collection, searchQuery, changes, options = {}) {
224+
const request = {
225+
index,
226+
collection,
227+
body: {query: searchQuery, changes},
228+
action: 'updateByQuery',
229+
source: options.source
230+
};
231+
232+
return this.query(request, options)
233+
.then(response => response.result);
234+
}
235+
223236
validate (index, collection, body, options = {}) {
224237
return this.query({
225238
index,

src/protocols/routes.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@
116116
"url": "/:index/:collection/:_id/_update",
117117
"verb": "PUT"
118118
},
119+
"updateByQuery": {
120+
"url": "/:index/:collection/_query",
121+
"verb": "PUT"
122+
},
119123
"mUpdate": {
120124
"url": "/:index/:collection/_mUpdate",
121125
"verb": "PUT"

test/controllers/document.test.js

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,86 @@ describe('Document Controller', () => {
556556
body: { foo: 'bar' },
557557
retryOnConflict: undefined,
558558
source: true
559-
}, { source: true });
559+
});
560+
561+
should(res).be.equal(result);
562+
});
563+
});
564+
});
565+
566+
describe('updateByQuery', () => {
567+
it('should call document/update query and return a Promise which resolves the updated document', () => {
568+
const result = {
569+
_id: 'document-id',
570+
_version: 1,
571+
_source: { foo: 'bar' },
572+
created: false
573+
};
574+
kuzzle.query.resolves({ result });
575+
const searchQuery = {
576+
match: { foo: 'bar' }
577+
};
578+
const changes = {
579+
bar: 'foo'
580+
};
581+
return kuzzle.document.updateByQuery('index', 'collection', searchQuery, changes, options)
582+
.then(res => {
583+
should(kuzzle.query)
584+
.be.calledOnce()
585+
.be.calledWith({
586+
controller: 'document',
587+
action: 'updateByQuery',
588+
index: 'index',
589+
collection: 'collection',
590+
body: {
591+
query: {
592+
match: {foo: 'bar'}
593+
},
594+
changes: {
595+
bar: 'foo'
596+
}
597+
},
598+
source: undefined
599+
}, options);
600+
601+
should(res).be.equal(result);
602+
});
603+
});
604+
605+
it('should inject the "source" option into the request', () => {
606+
const result = {
607+
_id: 'document-id',
608+
_version: 1,
609+
_source: { foo: 'bar' },
610+
created: false
611+
};
612+
kuzzle.query.resolves({ result });
613+
const searchQuery = {
614+
match: { foo: 'bar' }
615+
};
616+
const changes = {
617+
bar: 'foo'
618+
};
619+
620+
return kuzzle.document.updateByQuery('index', 'collection', searchQuery, changes, { source: true })
621+
.then(res => {
622+
should(kuzzle.query)
623+
.be.calledOnce()
624+
.be.calledWith({
625+
controller: 'document',
626+
action: 'updateByQuery',
627+
index: 'index',
628+
collection: 'collection',
629+
body: {
630+
query: {
631+
match: { foo: 'bar' }
632+
},
633+
changes: {
634+
bar: 'foo'
635+
}
636+
},
637+
source: true
638+
});
560639

561640
should(res).be.equal(result);
562641
});

0 commit comments

Comments
 (0)