LoginSignup
2
1

More than 1 year has passed since last update.

Azure Cosmos DB - UPSERT

Last updated at Posted at 2022-02-27

前提

Partition Key は/pk、Unique keys は/col1, /col2とする。

image.png

UPSERT

使用コード

UPSERT には SDK の upsert を使用する。

const { CosmosClient } = require('@azure/cosmos');
const config = require('./config');

async function upsert() {
  const { connectionString, databaseId, containerId } = config;

  const client = new CosmosClient(connectionString);
  const database = client.database(databaseId);
  const container = database.container(containerId);

  try {
    const item = {
      // 内容は適時変更
      pk: 'A',
      id: '0001',
      col1: 1,
      col2: 2,
    };

    await container.items.upsert(item);
  } catch (err) {
    console.log(err.message);
  }
}

upsert();

挙動

競合するデータが存在しない場合は、INSERT、
競合するデータが存在する場合は、UPDATE
が基本的な UPSERT の挙動。

データの一意性、競合については、前回記事参照。

1.競合するデータが存在しない場合

UPSERT すると、以下のようにアイテムが追加される。

2022-02-27-06-48-15.png

2.Partition Key が競合するデータが存在する場合

pkid が一致するデータの UPSERT

{"pk": "A", "id": "0001", "col1": 1, "col2": 2} が存在する状態で、
{"pk": "A", "id": "0001", "col1": 2, "col2": 3} を UPSERT すると、
以下のようにアイテムが追加されることなく col1col2 の値が UPDATE される。

2022-02-27-06-55-14.png

3.Unique keys が競合するデータが存在する場合

pk と、col1col2 が一致するデータの UPSERT

{"pk": "A", "id": "0001", "col1": 1, "col2": 2} が存在する状態で、
{"pk": "A", "id": "0002", "col1": 1, "col2": 2, "col3": 3} を UPSERT すると、
競合が発生する。

つまり、Unique keys の競合の場合は UPDATE されない。
そのため、Unique keys の競合に対しては create でも挙動は同じ。

競合例外発生時に UPDATE を実行すれば、UPSERT は実現可能。

一括 UPSERT

使用コード

一括 UPSERT には SDK の bulk を使用する。

const { CosmosClient } = require('@azure/cosmos');
const config = require('./config');

async function bulkUpsert() {
  const { connectionString, databaseId, containerId } = config;

  const client = new CosmosClient(connectionString);
  const database = client.database(databaseId);
  const container = database.container(containerId);

  const operations = [
    {
      // INSERT
      operationType: 'Upsert',
      partitionKey: 'A',
      resourceBody: {
        pk: 'A',
        id: '0001',
        col1: 1,
        col2: 2,
      },
    },
    {
      // col1, col2 が同じため CONFLICT
      operationType: 'Upsert',
      partitionKey: 'A',
      resourceBody: {
        pk: 'A',
        id: '0002',
        col1: 1,
        col2: 2,
      },
    },
    {
      // col1, col2 が同じため CONFLICT
      operationType: 'Upsert',
      partitionKey: 'A',
      resourceBody: {
        pk: 'A',
        id: '0003',
        col1: 1,
        col2: 2,
        col3: 3,
      },
    },
    {
      // INSERT
      operationType: 'Upsert',
      partitionKey: 'A',
      resourceBody: {
        pk: 'A',
        id: '0004',
        col1: 1,
        col2: 3,
      },
    },
  ];
  bulkOptions = { continueOnError: true };

  try {
    await container.items.bulk(operations, bulkOptions);
  } catch (err) {
    console.log(err.message);
  }
}

bulkUpsert();

挙動

単発での UPSERT と同様。
ただし、競合が発生したアイテムに対して個別に例外処理を実行できないため、
UPSERT は実現不可。

上記コードのように
オプションで { continueOnError: true } を指定することで例外を無視できるため、

競合するデータが存在しない場合は、INSERT、
競合するデータが存在する場合は、IGNORE

は実現可能。

上記コードを実行した場合、
id: '0001' は INSERT、
id: '0002' は col1, col2 が同じため CONFLICT
id: '0003' は col1, col2 が同じため CONFLICT
id: '0004' は INSERT
となる。

2022-02-27-09-21-43.png

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1