Momento Cache 4つのコレクションデータ その1 Dictionary

2023/11/03に公開

Momento Cacheには標準系のKey-Valueに加えて4つのコレクションデータ型があります。
標準系は以下の記事でやった通りです。
https://zenn.dev/momentobigfun/articles/aa24ff7817e06c

const setResponse = await client.set(cacheName, key, data);
const readResponse = await client.get(cacheName, key);

のように単純にKey Valueを扱います。
このKeyに対してネストされたデータを取り扱うことを可能とし、アプリケーション側での実装負担を少なくするものがコレクションデータ型です。
・Dictionary
・Lists
・Sets
・Sorted Sets
です。これから4回シリーズでそれを見ていきたいと思います。

Dictionary

以下のようにNestされたデータを単一のKeyに持たせることが出来ます。

test-dictionaryというキーに対して

value
{
	"test-field": "test-value",
	"key1": "value1",
	"key2": "value2"
}

がセットされています。

早速やってみる

https://zenn.dev/momentobigfun/articles/aa24ff7817e06c
test.jsの実行まで終わらせておきましょう。

test.jsを以下に改造します。

test.js
const {
  Configurations,
  CacheClient, CredentialProvider,
  CacheDictionarySetField,
} = require('@gomomento/sdk');

const dotenv = require('dotenv');

dotenv.config();

async function createCacheClient() {
  return await CacheClient.create({
    configuration: Configurations.Laptop.v1(),
    credentialProvider: CredentialProvider.fromEnvironmentVariable({
      environmentVariableName: 'MOMENTO_API_KEY',
    }),
    defaultTtlSeconds: 600,
  });
}

async function run() {
  const cacheClient = await createCacheClient();

  const result = await cacheClient.dictionarySetField('demo-cache', 'test-dictionary', 'test-field', 'test-value');
  if (result instanceof CacheDictionarySetField.Success) {
    console.log("Field set successfully into cache 'test-cache'");
  } else if (result instanceof CacheDictionarySetField.Error) {
    throw new Error(
      `An error occurred while attempting to call cacheDictionarySetField on dictionary 'test-dictionary' in cache 'demo-cache': ${result.errorCode()}: ${result.toString()}`
    );
  }
}

run();

実行が成功すると

Field set successfully into cache 'demo-cache'

と表示されます。

const result = await cacheClient.dictionarySetField('demo-cache', 'test-dictionary', 'test-field', 'test-value');

が実体です。demo-cacheという名前のキャッシュに、test-dictionaryというキーを用いて、
'test-field', 'test-value'を書き込んでいます。
少しややこしいのですが、特定のキーに紐付いているアイテムが存在し、そのアイテムがDictionaryです。そしてDictionaryアイテムの中に格納さるフィールドは複数存在することが可能で、フィールド自体がネストされたKey-Valueを構成できます。
またここは注意点ですがDictionaryは順序付けされていない要素の集まりです。つまり順番はばらばらに出力されます。これは同じDictionaryアイテムの呼び出しにおいても中のフィールドは順番が時と場合によって異なって出力されます。その分ネストされたアイテムでも早く動作します。

以下のような指定も可能です。

const {
  Configurations,
  CacheClient, CredentialProvider,
  CacheDictionarySetField,
  CacheDictionarySetFields,
} = require('@gomomento/sdk');

<snip>

async function run() {
  const cacheClient = await createCacheClient();

  const result = await cacheClient.dictionarySetFields(
    'demo-cache',
    'test-dictionary',
    new Map([
      ['key1', 'value1'],
      ['key2', 'value2'],
    ])
  );
  if (result instanceof CacheDictionarySetFields.Success) {
    console.log("Fields set successfully into cache 'demo-cache'");
  } else if (result instanceof CacheDictionarySetFields.Error) {
    throw new Error(
      `An error occurred while attempting to call cacheDictionarySetFields on dictionary 'test-dictionary' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
    );
  }
}
<snip>

この例ではCacheDictionarySetFieldの代わりにCacheDictionarySetFieldsを用いています。ValueにはMapを用いることでさらにネストさせたデータを書き込んでいます。

Valueから特定のネストされたFiledだけを削除することも可能です。

const {
  Configurations,
  CacheClient, CredentialProvider,
  CacheDictionarySetField,
  CacheDictionarySetFields,
  CacheDictionaryRemoveField,
} = require('@gomomento/sdk');
<snip>
const result = await cacheClient.dictionaryRemoveField('demo-cache', 'test-dictionary', 'test-field');
  if (result instanceof CacheDictionaryRemoveField.Success) {
    console.log("Field removed successfully from dictionary 'test-dictionary'");
  } else if (result instanceof CacheDictionaryRemoveField.Error) {
    throw new Error(
      `An error occurred while attempting to call cacheDictionaryRemoveField on dictionary 'test-dictionary' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
    );
  }
<snip>

以下のようにFieldが一つ消えています。

キーに紐付く値を全て取得するのは以下で行えます。

const {
  Configurations,
  CacheClient, CredentialProvider,
  CacheDictionarySetField,
  CacheDictionarySetFields,
  CacheDictionaryRemoveField,
  CacheDictionaryFetch,
} = require('@gomomento/sdk');
<snip>
 const result = await cacheClient.dictionaryFetch('demo-cache', 'test-dictionary');
  if (result instanceof CacheDictionaryFetch.Hit) {
    console.log('Dictionary fetched successfully- ');
    result.valueMapStringString().forEach((value, key) => {
      console.log(`${key} : ${value}`);
    });
  } else if (result instanceof CacheDictionaryFetch.Miss) {
    console.log("Dictionary 'test-dictionary' was not found in cache 'demo-cache'");
  } else if (result instanceof CacheDictionaryFetch.Error) {
    throw new Error(
      `An error occurred while attempting to call cacheDictionaryFetch on dictionary 'test-dictionary' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
    );
  }
<snip>
Dictionary fetched successfully- 
key1 : value1
key2 : value2

現在Dictionaryアイテムをまとめて削除するAPIが無いようです。と、思いきやtest-dictionaryがただのキャッシュキーとして指定可能ですので以下で削除できました。

const result = await cacheClient.delete('demo-cache', 'test-dictionary');

詳しいAPI諸元はこちらです。
https://docs.momentohq.com/ja/cache/develop/api-reference/dictionary-collections
次回はLists型を見ていきます!

(2023/12/06追記)
特定の値を持つFieldだけを抜き出す方法です。例えば以下の値がある場合

{
	"test-field": "test-value",
	"key2": "value2",
	"key1": "value1"
}

value1だけを取り出すには

const result2 = await cacheClient.dictionaryGetField('demo-cache', 'test-dictionary','key1');

を使います。

const {
    Configurations,
    CacheClient, CredentialProvider,
    CacheDictionarySetField,
    CacheDictionarySetFields,
} = require('@gomomento/sdk');

const dotenv = require('dotenv');

dotenv.config();

async function createCacheClient() {
    return await CacheClient.create({
        configuration: Configurations.Laptop.v1(),
        credentialProvider: CredentialProvider.fromEnvironmentVariable({
            environmentVariableName: 'MOMENTO_API_KEY',
        }),
        defaultTtlSeconds: 600,
    });
}


async function run() {
    const cacheClient = await createCacheClient();

    const result = await cacheClient.dictionarySetFields(
        'demo-cache',
        'test-dictionary',
        new Map([
            ['key1', 'value1'],
            ['key2', 'value2'],
        ])
    );
    if (result instanceof CacheDictionarySetFields.Success) {
        console.log("Fields set successfully into cache 'demo-cache'");
    } else if (result instanceof CacheDictionarySetFields.Error) {
        throw new Error(
            `An error occurred while attempting to call cacheDictionarySetFields on dictionary 'test-dictionary' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
        );
    }
    const result2 = await cacheClient.dictionaryGetField('demo-cache', 'test-dictionary', 'key1');
    console.log("result2 is " + result2);

}

run()

Discussion