Skip to content

Commit 2cd333e

Browse files
authored
Merge pull request #251108 from sajeetharan/js_sdk_updates
JavaScript v4 SDK updates
2 parents 6a35999 + 241242f commit 2cd333e

File tree

3 files changed

+239
-1
lines changed

3 files changed

+239
-1
lines changed

articles/cosmos-db/hierarchical-partition-keys.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Find the latest preview version of each supported SDK:
6464
| --- | --- | --- |
6565
| .NET SDK v3 | >= 3.33.0 | <https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.33.0/> |
6666
| Java SDK v4 | >= 4.42.0 | <https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4420-2023-03-17/> |
67-
| JavaScript SDK v3 | 3.17.4-beta.1 | <https://www.npmjs.com/package/@azure/cosmos/v/3.17.4-beta.1/> |
67+
| JavaScript SDK v4 | 4.0.0 | <https://www.npmjs.com/package/@azure/cosmos/> |
6868

6969
## Create a container by using hierarchical partition keys
7070

@@ -362,6 +362,25 @@ Mono<CosmosItemResponse<UserSession>> readResponse = container.readItem(id, part
362362

363363
---
364364

365+
##### [JavaScript SDK v4](#tab/javascript-v4)
366+
367+
```javascript
368+
// Store the unique identifier
369+
String id = "f7da01b0-090b-41d2-8416-dacae09fbb4a";
370+
371+
// Build the full partition key path
372+
PartitionKey partitionKey = new PartitionKeyBuilder()
373+
.add("Microsoft") //TenantId
374+
.add("8411f20f-be3e-416a-a3e7-dcd5a3c1f28b") //UserId
375+
.add("0000-11-0000-1111") //SessionId
376+
.build();
377+
378+
// Perform a point read
379+
Mono<CosmosItemResponse<UserSession>> readResponse = container.readItem(id, partitionKey, UserSession.class);
380+
```
381+
382+
---
383+
365384
### Run a query
366385

367386
The SDK code that you use to run a query on a subpartitioned container is identical to running a query on a non-subpartitioned container.

articles/cosmos-db/nosql/change-feed-pull-model.md

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,214 @@ Machine 2:
298298

299299
[!code-java[](~/azure-cosmos-java-sql-api-samples/src/main/java/com/azure/cosmos/examples/changefeedpull/SampleChangeFeedPullModel.java?name=Machine2)]
300300

301+
### [JavaScript](#tab/JavaScript)
302+
303+
To process the change feed by using the pull model, create an instance of `ChangeFeedPullModelIterator`. When you initially create `ChangeFeedPullModelIterator`, you must specify a required `changeFeedStartFrom` value inside the `ChangeFeedIteratorOptions` which consists of both the starting position for reading changes and the resource(a partition key or a FeedRange) for which changes are to be fetched.
304+
> [!NOTE]
305+
> If no `changeFeedStartFrom` value is specified, then changefeed will be fetched for an entire container from Now().
306+
> Currently, only [latest version](change-feed-modes.md#latest-version-change-feed-mode) is supported by JS SDK and is selected by default.
307+
308+
You can optionally use `maxItemCount` in `ChangeFeedIteratorOptions` to set the maximum number of items received per page.
309+
Here's an example of how to obtain the iterator in latest version mode that returns entity objects:
310+
311+
```js
312+
const options = {
313+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning()
314+
};
315+
316+
const iterator = container.items.getChangeFeedIterator(options);
317+
```
318+
319+
### Consume the changes for an entire container
320+
321+
If you don't supply a `FeedRange` or `PartitionKey` parameter inside `ChangeFeedStartFrom`, you can process an entire container's change feed at your own pace. Here's an example, which starts reading all changes, starting at the current time:
322+
323+
```js
324+
async function waitFor(milliseconds: number): Promise<void> {
325+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
326+
}
327+
328+
const options = {
329+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning()
330+
};
331+
332+
const iterator = container.items.getChangeFeedIterator(options);
333+
334+
let timeout = 0;
335+
336+
while(iterator.hasMoreResults) {
337+
const response = await iterator.readNext();
338+
if (response.statusCode === StatusCodes.NotModified) {
339+
timeout = 5000;
340+
}
341+
else {
342+
console.log("Result found", response.result);
343+
timeout = 0;
344+
}
345+
await waitFor(timeout);
346+
}
347+
```
348+
349+
Because the change feed is effectively an infinite list of items that encompass all future writes and updates, the value of `hasMoreResults` is always `true`. When you try to read the change feed and there are no new changes available, you receive a response with `NotModified` status. In the preceding example, it's handled by waiting five seconds before rechecking for changes.
350+
351+
### Consume the changes for a partition key
352+
353+
In some cases, you might want to process only the changes for a specific partition key. You can obtain iterator for a specific partition key and process the changes the same way that you can for an entire container.
354+
355+
```js
356+
async function waitFor(milliseconds: number): Promise<void> {
357+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
358+
}
359+
360+
const options = {
361+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning("partitionKeyValue")
362+
};
363+
364+
const iterator = container.items.getChangeFeedIterator(options);
365+
366+
let timeout = 0;
367+
368+
while(iterator.hasMoreResults) {
369+
const response = await iterator.readNext();
370+
if (response.statusCode === StatusCodes.NotModified) {
371+
timeout = 5000;
372+
}
373+
else {
374+
console.log("Result found", response.result);
375+
timeout = 0;
376+
}
377+
await waitFor(timeout);
378+
}
379+
```
380+
381+
### Use FeedRange for parallelization
382+
383+
In the change feed pull model, you can use the `FeedRange` to parallelize the processing of the change feed. A `FeedRange` represents a range of partition key values.
384+
385+
Here's an example that shows how to get a list of ranges for your container:
386+
387+
```js
388+
const ranges = await container.getFeedRanges();
389+
```
390+
391+
When you get a list of `FeedRange` values for your container, you get one `FeedRange` per [physical partition](../partitioning-overview.md#physical-partitions).
392+
393+
By using a `FeedRange`, you can create iterator to parallelize the processing of the change feed across multiple machines or threads. Unlike the previous example that showed how to obtain a changefeed iterator for the entire container or a single partition key, you can use FeedRanges to obtain multiple iterators, which can process the change feed in parallel.
394+
395+
Here's a sample that shows how to read from the beginning of the container's change feed by using two hypothetical separate machines that read in parallel:
396+
397+
Machine 1:
398+
399+
```js
400+
async function waitFor(milliseconds: number): Promise<void> {
401+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
402+
}
403+
404+
const options = {
405+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning(ranges[0])
406+
};
407+
408+
const iterator = container.items.getChangeFeedIterator(options);
409+
410+
let timeout = 0;
411+
412+
while(iterator.hasMoreResults) {
413+
const response = await iterator.readNext();
414+
if (response.statusCode === StatusCodes.NotModified) {
415+
timeout = 5000;
416+
}
417+
else {
418+
console.log("Result found", response.result);
419+
timeout = 0;
420+
}
421+
await waitFor(timeout);
422+
}
423+
```
424+
425+
Machine 2:
426+
427+
```js
428+
async function waitFor(milliseconds: number): Promise<void> {
429+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
430+
}
431+
432+
const options = {
433+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning(ranges[1])
434+
};
435+
436+
const iterator = container.items.getChangeFeedIterator(options);
437+
438+
let timeout = 0;
439+
440+
while(iterator.hasMoreResults) {
441+
const response = await iterator.readNext();
442+
if (response.statusCode === StatusCodes.NotModified) {
443+
timeout = 5000;
444+
}
445+
else {
446+
console.log("Result found", response.result);
447+
timeout = 0;
448+
}
449+
await waitFor(timeout);
450+
}
451+
```
452+
453+
### Save continuation tokens
454+
455+
You can save the position of your iterator by obtaining the continuation token. A continuation token is a string value that keeps of track of your changefeed iterator last processed changes and allows the iterator to resume at this point later. The continuation token, if specified, takes precedence over the start time and start from beginning values. The following code reads through the change feed since container creation. After no more changes are available, it will persist a continuation token so that change feed consumption can be later resumed.
456+
457+
```js
458+
const options = {
459+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning()
460+
};
461+
462+
const iterator = container.items.getChangeFeedIterator(options);
463+
464+
let timeout = 0;
465+
let continuation = "";
466+
while(iterator.hasMoreResults) {
467+
const response = await iterator.readNext();
468+
if (response.statusCode === StatusCodes.NotModified) {
469+
continuation = response.continuationToken;
470+
break;
471+
}
472+
else {
473+
console.log("Result found", response.result);
474+
}
475+
}
476+
477+
// For checking any new changes using the continuation token
478+
const continuationOptions = {
479+
changeFeedStartFrom: ChangeFeedStartFrom(continuation)
480+
}
481+
const newIterator = container.items.getChangeFeedIterator(continuationOptions);
482+
```
483+
Continuation token never expires as long as the Azure Cosmos DB container still exists.
484+
485+
### Use AsyncIterator
486+
487+
You can use the JavaScript Async Iterator to fetch the changefeed. Here is an example to use Async Iterator.
488+
489+
```js
490+
async function waitFor(milliseconds: number): Promise<void> {
491+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
492+
}
493+
const options = {
494+
changeFeedStartFrom: ChangeFeedStartFrom.Beginning()
495+
};
496+
let timeout = 0;
497+
498+
for await(const result of container.items.getChangeFeedIterator(options).getAsyncIterator()) {
499+
if (result.statusCode === StatusCodes.NotModified) {
500+
timeout = 5000;
501+
}
502+
else {
503+
console.log("Result found", result.result);
504+
timeout = 0;
505+
}
506+
await waitFor(timeout);
507+
}
508+
```
301509
---
302510

303511
## Next steps

articles/cosmos-db/nosql/index-metrics.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ You can enable indexing metrics for a query by setting the `PopulateIndexMetrics
8080
return Flux.just(itemsResponse);
8181
}).blockLast();
8282
```
83+
84+
## [JavaScript SDK](#tab/javascript)
85+
```javascript
86+
const querySpec = {
87+
query: "SELECT TOP 10 c.id FROM c WHERE c.Item = 'value1234' AND c.Price > 2",
88+
};
89+
const { resources: resultsIndexMetrics, indexMetrics } = await container.items
90+
.query(querySpec, { populateIndexMetrics: true })
91+
.fetchAll();
92+
console.log("IndexMetrics: ", indexMetrics);
93+
```
8394
---
8495

8596
### Example output

0 commit comments

Comments
 (0)