CacheThing
CacheThing は、キーと値のデータを保存および取得するための高速なメモリ内インタフェースを提供します。これは、低速なクエリーなど、負荷の高い、または時間のかかる操作の結果をキャッシュするのに最適です。
CacheThing はメモリ内で動作するため、そのコンテンツは ThingWorx の再起動時にクリアされます。高可用性 (HA) 環境では、各 ThingWorx ノードがそれぞれ独立したキャッシュを保持します。その結果、1 つのノードで作成または更新されたキャッシュエントリは、ほかのノードと自動的に共有されません。これには、PurgeCacheDeleteEntryDeleteEntryByKey などのキャッシュ無効化呼び出しも含まれます。
CacheThing を使用して、次のようなさまざまなタイプのデータをキャッシュできます。
データベース Thing に対する高コストまたは高頻度のクエリーの結果
値ストリームまたはデータテーブルに対する高コストまたは高頻度のクエリーの結果
低速なネットワーク呼び出しの結果 (ContentLoaderFunctions での呼び出しなど)
高コストの計算値
FileRepositories の頻繁に使用される小さいファイルのコンテンツ
CacheThing を使用すると、データベースやネットワークリソースなどの外部システムにアクセスする場合と比較して、パフォーマンスを大幅に向上させ、リソース使用量を削減できます。
使用上のガイドライン
CacheThing は永続的なデータストアとして使用するためのものではありません。CacheThing にキャッシュされたデータは、元のソースから再生成できる必要があります。
CacheThing 内のデータは、期限切れまたは追い出しのいずれかの状態になる可能性があります。CacheThing を使用するサービスは、キャッシュから結果が返されない場合に元のソースからデータを取得するように設計されている必要があります。詳細については、を参照してください。
コンフィギュレーション
CacheThing はプライマリキーを含むデータシェイプで設定する必要があります。プライマリキーは、キャッシュからエントリを取得するために使用されます。
複合プライマリキーはサポートされていますが、これを使用すると、以下にリストされている ByKey コンビニエンスサービス、有効期限ポリシー、およびグローバル最大サイズを使用できなくなります。
有効期限ポリシー
有効期限ポリシーは、各キャッシュエントリの有効期間 (TTL) を定義します。「キャッシュエントリの有効期限 (秒)」設定を使用して有効期限を設定できます。エントリの有効期限が切れた後にその値を取得すると、キャッシュから削除された場合と同様に、空の結果が返されます。
サポートされている有効期限ポリシー:
キャッシュエントリの有効期限ポリシー
説明
なし
キャッシュエントリは、キャッシュ内での滞在時間の長さに基づいて除去されることはありません。ただし、キャッシュが最大サイズコンフィギュレーションに達したり、DeleteEntry サービスが使用されたりした場合に、追い出される可能性は残っています。Never 有効期限ポリシーが設定されている場合、「キャッシュエントリの有効期限 (秒)」設定は無視されます。
最後のアクセス以来の有効期限 (時間)
指定された有効期限内に読み取られなかったエントリは除去されます。
作成以来の有効期限 (時間)
作成されてからの経過時間が、指定された時間に達したエントリは除去されます。
最終修正後の有効期限 (時間)
指定された有効期限内に修正されなかったエントリは除去されます。
最後に使用して以来の有効期限 (時間)
指定された有効期限内にアクセスまたは修正されなかったエントリは除去されます。
インタフェースの有効期限ポリシーの詳細については、「Interface ExpiryPolicy」を参照してください。
キャッシュ最大サイズ (追い出し)
「キャッシュ最大サイズ (MB)」設定で指定されているサイズにキャッシュが達すると、キャッシュエントリは自動的に追い出されます (削除されます)。追い出しプロセスでは、新しいエントリ用のスペースを確保するために、最も使用頻度の低い (LFU) エントリが除去されます。エントリが追い出された後にそのエントリを取得すると、削除された場合と同様に空の結果が返されます。
大きなエントリが追加された場合、そのスペースを確保するために複数の小さなエントリが追い出される可能性があります。これを回避するには、エントリをキャッシュに追加する前に、EstimateEntrySize サービスを使用してエントリのサイズをチェックします。
キャッシュの追い出しと有効期限は独立して動作します。
追い出しは、キャッシュサイズと使用頻度に基づきます。
有効期限は、設定された有効期限ポリシーで定義されている時間に基づきます。
グローバル最大サイズ
プラットフォームサブシステムには、「ThingWorx ノードごとにすべての CacheThing 間で共有される最大サイズ (MB)」という設定が含まれています。この設定は、過剰なメモリ使用を防ぐために、単一ノード上のすべての CacheThing の合計サイズを制限します。無効にされた CacheThing は、この制限に含まれません。
このコンフィギュレーションにより、次の操作が防止されます。
最大サイズを超える新しい CacheThing を作成する
CacheThing の「キャッシュ最大サイズ (MB)」を、最大サイズを超えるサイズに更新する
最大サイズを超える CacheThing を (エンティティのインポートとして、または拡張の一部として) インポートする (インポート全体が失敗します)
現在すべての CacheThing に設定されている「キャッシュ最大サイズ (MB)」の合計を下回るように、最大サイズを減らす
グローバル制限を超える CacheThing をアクティブ化する
キャッシュコンフィギュレーションの変更
キャッシュのコンフィギュレーションに変更を加えると、内部の一貫性を維持するためにキャッシュが自動的にパージされます。以下のいずれかを編集すると、キャッシュがパージされます。
データシェイプ - フィールドの追加や除去など、設定されているデータシェイプの修正が含まれます。
キャッシュエントリの有効期限ポリシー
キャッシュエントリの有効期限 (秒)
キャッシュ最大サイズ (MB)
Cache Performs Loading of Missing Entries
サービス
CacheThing では次のサービスを使用できます。高可用性 (HA) 環境では、これらのサービスは、各サービスが実行されている ThingWorx ノード上のキャッシュに影響を及ぼします。
以下にリストされている *ByKey コンビニエンスサービスは、キャッシュのデータシェイプが単一のプライマリキーを持っている場合にのみ使用できます。キャッシュのデータシェイプに複数のプライマリキーが含まれている場合は、サービスの完全なインフォテーブル入力バージョンを使用します。
サービス名
説明
PutEntry
キャッシュにエントリを追加します。
入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。
GetEntry
キャッシュから 1 行のインフォテーブル結果を返します。
エントリが見つからず (キャッシュミス)、かつキャッシュで「Cache Performs Loading of Missing Entries」が設定されていない場合は、空のインフォテーブルが返されます。キャッシュでそのオプションが設定されている場合は、LoadEntry サービスが自動的に呼び出され、その結果が返されます。
キャッシュミスは、エントリがキャッシュに追加されていない場合、追い出された場合、または期限切れになった場合に発生する可能性があります。
入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。Primary Key 値のみを指定する必要があります。
GetEntryByKey
キャッシュから 1 行のインフォテーブル結果を返します。
エントリが見つからない場合は、空のインフォテーブルが返されます。キャッシュミスは、エントリがキャッシュに追加されていない場合、追い出された場合、または期限切れになった場合に発生する可能性があります。
入力値 - 取得するエントリのプライマリキーを表す文字列。このサービスは、データシェイプが単一のプライマリキーを持つ場合にのみ使用できます。これは文字列のプライマリキーで最適に機能しますが、明確な文字列変換に対応したほとんどのタイプもサポートしています。
LoadEntry
キャッシュミスが発生した場合に、データソースからキャッシュデータを自動的にフェッチするために使用されます。
「Cache Performs Loading of Missing Entries」が有効になっている場合、このサービスをオーバーライドする必要があります。
DeleteEntry
キャッシュからエントリを削除します。
高可用性環境では、これは DeleteEntry サービスが呼び出されたノードにのみ影響します。
入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。Primary Key 値のみを指定する必要があります。
DeleteEntryByKey
キャッシュからエントリを削除します。
高可用性環境では、これは DeleteEntryByKey サービスが呼び出されたノードにのみ影響します。
入力値 - 取得するエントリのプライマリキーを表す文字列。このサービスは、データシェイプが単一のプライマリキーを持つ場合にのみ使用できます。
PurgeCache
すべてのキャッシュエントリを除去します。
高可用性環境では、これは PurgeCache サービスが呼び出されたノードにのみ影響します。
GetDataShape
キャッシュの設定済みデータシェイプを返します。
SetDataShape
キャッシュのコンフィギュレーションテーブルのデータシェイプフィールドを設定します。この値を変更すると、キャッシュが自動的にパージされます。
GetEstimatedEntryCount
キャッシュ内の推定エントリ数を返します。進行中の PUT 操作や DELETE 操作、および有効期限プロセスや追い出しプロセスが精度に影響を及ぼす可能性があります。
EstimateEntrySize
エントリがキャッシュに追加された場合の推定サイズを返します。これによってエントリがキャッシュに追加されるわけではなく、キャッシュ内にすでにエントリが存在している必要もありません。このサービスは、適切な「キャッシュ最大サイズ (MB)」を推定したり、大きなエントリがキャッシュに追加されるのを回避したりするのに役立ちます。
入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。
LoadEntry サービスのオーバーライド
「Cache Performs Loading of Missing Entries」 (リードスルーキャッシュ) を有効にするには、CacheThing の LoadEntry サービスをオーバーライドする必要があります。このサービスは、キャッシュミスが発生した場合に内部的に呼び出され、キャッシュがソースからデータを取得して保存できるようにします。
LoadEntry サービスを直接呼び出しても、エントリはキャッシュに追加されません。エントリが追加されるのは、キャッシュミスの発生時に GetEntry によって LoadEntry が内部的に呼び出された場合のみです。
「入力」
入力は、対応する GetEntry 呼び出しのパラメータと一致する 1 行のインフォテーブルです。通常は、プライマリキーのフィールドのみが設定されます。
サービス実装
LoadEntry サービスは、キャッシュする値をデータソースから取得または生成する必要があります。これには、データベースクエリー、Web サービス呼び出し、生成された値などが含まれます。このサービス内では、アプリケーションの状態 (プロパティやファイルなど) を更新することは避けてください。
LoadEntry 内から、ほかのキャッシュサービス (GetEntryPutEntry など) を呼び出さないでください。キャッシュミスの後、キャッシュは返された値の保存を自動的に処理します。
出力
出力は、入力と同じデータシェイプを持つインフォテーブルでなければなりません。取得したデータで、プライマリキー以外のフィールドを設定します。例外は、呼び出し元の GetEntry 呼び出しに伝播されます。
ベストプラクティスは、出力結果のインフォテーブル用に新しいインフォテーブルを作成することです。入力値のインフォテーブルを再使用して値を設定することは可能ですが、これを行うと、インフォテーブルが Javascript で特定のベースタイプを処理する方法の制限により、予期しない動作を引き起こす可能性があります。LoadEntry の使用の詳細については、を参照してください。
リードスルーキャッシュ
「Cache Performs Loading of Missing Entries」チェックボックスをオンにすると、リードスルーキャッシュが有効になります。このモードでは、キャッシュミスが発生した場合に、キャッシュは CacheThing の LoadEntry サービスを使用して、欠落しているエントリ値を自動的にロードします。
この動作をサポートするには、ソースデータをクエリーして適切な値を返すように、CacheThing の LoadEntry サービスをオーバーライドする必要があります。GetEntry サービスがキャッシュ内で値を見つけられない場合、キャッシュは LoadEntry サービスを呼び出して値を取得します。
リードスルーキャッシュは、Thundering Herd シナリオを軽減するのに役立ちます。キーが設定される前に、その同じキーに対して複数の並列 GetEntry リクエストが行われた場合でも、その背後で LoadEntry サービスが呼び出されるのは 1 回だけです。LoadEntry 呼び出しが完了すると、保留中のすべての GetEntry リクエストが、取得した値を返します。
LoadEntry サービス実装の提供の詳細については、上記の「LoadEntry サービスのオーバーライド」のセクションを参照してください。
判定基準
キャッシュ関連の判定基準がいくつか用意されています。高可用性 (HA) 環境では、キャッシュはノードごとに独立しています。判定基準は、platform ラベルと cache_name ラベルを使用してノードごとに処理される必要があります。
次のキャッシュ判定基準を使用できます。
キャッシュ判定基準
説明
thingworx_cache_hit_rate
キャッシュリクエストのうち、キャッシュ内でエントリが見つかったリクエストの割合
thingworx_cache_hits
キャッシュ検索メソッドがキャッシュされた値を返した回数
thingworx_cache_request_count
キャッシュ検索メソッドがキャッシュされた値またはキャッシュされていない値を返した回数
thingworx_cache_miss_rate
キャッシュされていない (新たにロードされた) 値または Null を返したキャッシュ検索メソッドの割合
thingworx_cache_misses
キャッシュ検索メソッドがキャッシュされていない (新しくロードされた) 値または Null を返した回数
thingworx_cache_eviction_count
エントリが追い出された回数
thingworx_cache_average_load_penalty
新しい値のロードにかかった平均時間
thingworx_cache_weighted_size
現在のキャッシュのおおよそのサイズ (バイト単位)
thingworx_cache_max_weight
追い出し前のキャッシュの最大サイズ (バイト単位)
thingworx_cache_estimated_entry_count
キャッシュ内の推定エントリ数
thingworx_cache_global_max_size
すべてのキャッシュに対して設定されているグローバル最大サイズ (バイト単位)
アラート (PTC クラウドがホストする環境で使用可能)
PTC クラウドがホストする環境では、次のアラートを使用できます。
thingworxEntityCacheMissRate - 1 時間のキャッシュのミス率が 80% を超えた場合にアラートを発行します。
thingworxEntityCacheEvictions - 30 分間のキャッシュの追い出し率が 80% を超えた場合にアラートを発行します。
例とベストプラクティス
CacheThing を効果的に使用するサービスを実装するには、細部にまで注意を払う必要があります。以下のベストプラクティスを考慮してください。
ThingWorx ノードの再起動時にキャッシュはクリアされます。
ユーザーが操作しなくても、キャッシュ内のエントリが追い出されたり、期限切れになったりする場合があります。エントリがキャッシュに無期限に保持されると想定しないでください。
キャッシュの判定基準を使用して、キャッシュサイズが適切であることを検証してください。
すべてのデータを単一のエントリに保存したり、非常に大きなキャッシュエントリを作成したりすることは避けてください。
高可用性 (HA) 環境では、各ノードに独立したキャッシュがあります。1 つのノードでキャッシュを追加、削除、またはパージしても、すべての ThingWorx ノードに影響するわけではありません。
「Cache Performs Loading of Missing Entries」が無効になっている場合 (非リードスルーキャッシュ):
1. プライマリキーを使用してキャッシュからエントリを取得します。
2. 結果が取り込まれているかどうか (返されたインフォテーブルに 1 行あるかどうか) を確認します。
3. 結果が見つかった場合は、それを使用します。
4. 結果が見つからない場合:
ソースデータ (データベース Thing、データテーブル、コンテンツローダーなど) から必要なエントリをフェッチまたは生成します。
PutEntry を使用して、エントリをキャッシュに追加します。
新しくキャッシュされたエントリを返します。
「Cache Performs Loading of Missing Entries」が有効になっている場合 (リードスルーキャッシュ):
1. CacheThing の LoadEntry サービスをオーバーライドします。
2. プライマリキーを使用してキャッシュからエントリを取得します。
エントリが見つからない場合、LoadEntry が自動的に呼び出されて値が取り込まれます。
例: TimesTwo サービスの使用 
TimesTwo サービスは、数値を受け取り、その数値を 2 倍にし、その結果をキャッシュに保存します。これを設定するには、次の手順を実行します。
1. operand: (LONG, primaryKey) フィールドと timesTwoValue: (LONG) フィールドを含むデータシェイプ (TimesTwoDataShape) を作成します。
2. 設定した TimesTwoDataShape を含む CacheThing (MyTimesTwoCache) を作成します。
3. TimesTwo サービスを MyTimesTwoCache に追加します。
入力 - x (LONG 型の数値)
出力 - result (LONG 型の数値)
// Input is x as a LONG
// Output is result as LONG
// MyTimesTwoCache CacheThing is configured with a DataShape with two fields:
// operand: (LONG, primaryKey)
// timesTwoValue: (LONG)
result = -1;
// Check the cache first, to see if we have already calculated x*2
// GetEntryByKey is a convenience method that takes the String value the configured DataShape's Primary Key ("key" in this example)
// cacheResult is an InfoTable with the DataShape of the cache
let cacheResult = Things["MyTimesTwoCache"].GetEntryByKey({ operand: x.toString() });
if (cacheResult.getRowCount() === 0) {
// Cache didn't have a result, so calculate it
let timesTwo = x*2;

// Put the result in the cache for the next time we need this multiple
// operand and value are from the Cache's configured DataShape
let cacheInput = getInfoTableForPut(x, timesTwo);
Things["MyTimesTwoCache"].PutEntry({
values: cacheInput
});

// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("PutEntry the following entry into the MyTimesTwoCache: {operand: " + x + ", timesTwoValue: " + timesTwo + "}");

// Set the global result Output variable
result = timesTwo;
} else {
// If results are found, they are always in the first row of the InfoTable
let row = cacheResult.getRow(0);
logger.debug("Found the following entry already in MyTimesTwoCache: {operand: " + row.operand + ", timesTwoValue: " + row.timesTwoValue + "}");
result = row.timesTwoValue;
}
function getInfoTableForPut(operand, timesTwoValue) {
let infoTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: Things["MyTimesTwoCache"].GetDataShape()
});
infoTable.AddRow({"operand": operand, "timesTwoValue ": timesTwoValue });
return infoTable;
}
例: サービスラッパーのキャッシュ 
この例では、QueryImplementingThingsV2 サービスの結果を手動でキャッシュする方法を示します。
1. 次のフィールドを含む、Cached_QueryImplementingThingsV2_DataShape という名前のデータシェイプを作成します。
maxItems: (NUMBER, primaryKey)
nameMask: (STRING, primaryKey)
query: (QUERY, primaryKey)
isSortFirst: (BOOLEAN, primaryKey)
result: (INFOTABLE, DataShape RootEntityListV2)
2. Cached_QueryImplementingThingsV2 という名前の CacheThing を作成し、そのデータシェイプとして Cached_QueryImplementingThingsV2_DataShape を割り当てます。
3. キャッシュの期限切れの動作を確認するには、Cached_ReadThrough_QueryImplementingThingsV2 Thing で、短い ExpirationTime を設定し、ExpirationPolicyNever 以外に設定します。
4. 次のパラメータを指定して、CachingQueryImplementingThingsV2 という名前のサービスを作成します。
入力
maxItems: (NUMBER, required)
nameMask: (STRING, required)
query: (QUERY, required)
isSortFirst: (BOOLEAN, required)
出力 result: (INFOTABLE, DataShape RootEntityListV2)
/* 
This Service queries QueryImplementingThingsV2 and caches the results as needed
Service Name: CachingQueryImplementingThingsV2 (Overridden on CacheThing)
Input: values InfoTable with Cached_QueryImplementingThingsV2_DataShape
Cached_QueryImplementingThingsV2_DataShape:
maxItems: NUMBER, Primary Key
nameMask: STRING, Primary Key
query: QUERY, Primary Key
isSortFirst: BOOLEAN, Primary Key
result: INFOTABLE, DataShape RootEntityListV2 (NOT a Primary Key!)
*/
let cacheEntryInfoTable = getCacheEntryInfoTable();
cacheEntryInfoTable.AddRow({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */
});
let getEntryResult = me.GetEntry({values: cacheEntryInfoTable});
if (getEntryResult.getRowCount() === 0) {
// Cache didn't have results, so call QueryImplementingThingsV2
let queryResult = ThingTemplates["GenericThing"].QueryImplementingThingsV2({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */
});

// Add the result to the cache, with the primaryKey parameters used to uniquely identify the query.
let newCacheEntryInfoTable = getCacheEntryInfoTable();
newCacheEntryInfoTable.AddRow({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */,
result: queryResult /* INFOTABLE */
});
me.PutEntry({
values: newCacheEntryInfoTable
});
logger.debug("Entry not found in Cache, retrieved from QueryImplementingThingsV2:" + queryResult.ToJSON());
result = queryResult;
} else {
logger.debug("Found entry from Cache:" + getEntryResult.ToJSON());
let row = getEntryResult.getRow(0);
result = row.result;
}

function getCacheEntryInfoTable() {
return Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "Cached_QueryImplementingThingsV2_DataShape"
});
}
例: リードスルーキャッシュとしてのサービスラッパーのキャッシュ 
この例では、リードスルーキャッシュを使用して、QueryImplementingThingsV2 サービスの結果を自動的にキャッシュする方法を示します。LoadEntry サービスをオーバーライドした後、GetEntry サービスを使用して、QueryImplementingThingsV2 クエリー結果をキャッシュに自動的にロードできます。
1. 次のフィールドを含む、Cached_QueryImplementingThingsV2_DataShape という名前のデータシェイプを作成します。
maxItems: (NUMBER, primaryKey)
nameMask: (STRING, primaryKey)
query: (QUERY, primaryKey)
isSortFirst: (BOOLEAN, primaryKey)
result: (INFOTABLE, DataShape RootEntityListV2)
2. Cached_ReadThrough_QueryImplementingThingsV2 という名前の CacheThing を作成します。Cached_QueryImplementingThingsV2_DataShape をそのデータシェイプとして割り当て、「Cache Performs Loading of Missing Entries」オプションを有効にします。
3. キャッシュの期限切れの動作を確認するには、Cached_ReadThrough_QueryImplementingThingsV2 Thing で、短い ExpirationTime を設定し、ExpirationPolicyNever 以外に設定します。
4. 「サービス」タブに移動し、次の例のように、LoadEntry サービスの横にある「オーバーライド」を選択します。
/* 
This Service simply proxies a query to QueryImplementingThingsV2 for Read Through caching
Service Name: LoadEntry (Overridden on CacheThing)
Input: values InfoTable with Cached_QueryImplementingThingsV2_DataShape
Cached_QueryImplementingThingsV2_DataShape:
maxItems: NUMBER, Primary Key
nameMask: STRING, Primary Key
query: QUERY, Primary Key
isSortFirst: BOOLEAN, Primary Key
result: INFOTABLE, DataShape RootEntityListV2 (NOT a Primary Key!)
*/
let queryResult = ThingTemplates["GenericThing"].QueryImplementingThingsV2({
maxItems: values.maxItems /* NUMBER */,
nameMask: values.nameMask /* STRING */,
query: values.query /* QUERY */,
isSortFirst: values.isSortFirst /* BOOLEAN */
});
// Copy the results into a new InfoTable.
// This prevents some internal ThingWorx casting issues with some InfoTables
let resultTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: me.GetDataShape() // Returns Cached_QueryImplementingThingsV2_DataShape
});
resultTable.AddRow({
maxItems: values.maxItems /* NUMBER {"defaultValue":500} */,
nameMask: values.nameMask /* STRING */,
query: values.query /* QUERY */,
isSortFirst: values.isSortFirst /* BOOLEAN */,
result: queryResult
});
let result = resultTable;
DataTableQuery の使用例 
この例では、動作している BetweenDatesDataTableQuery サービスを使用して、2 つの日付間のデータテーブル内の最大値を見つけて、結果をキャッシュに保存する方法を示します。CachingBetweenDatesDataTableQuery の一意の呼び出しでは、最初はバッキングデータテーブルに対してクエリーが実行されますが、それ以降の同じ呼び出しでは、代わりにキャッシュが使用されます。
これを設定するには、次の手順を実行します。
1. longKey: (LONG, primaryKey) フィールドと dateKey: (DATETIME, primaryKey) フィールドを含むデータシェイプ (TimeValueDataShape) を作成します。
2. TimeValueDataShape データシェイプを含むデータテーブル (DemoDataTable) を作成します。
3. startDate: (DATETIME, primaryKey) フィールド、endDate: (DATETIME, primaryKey) フィールド、および longValue: (LONG) フィールドを含むデータシェイプ (TimeSpanValueCacheDataShape) を作成します。
4. 設定した TimeSpanValueCacheDataShape データシェイプを含む CacheThing (CacheThingDemo) を作成します。
5. CacheThingDemo に次のサービスを作成します。
CachingBetweenDatesDataTableQuery - 入力: StartDateEndDate (DATETIME 型)。出力: result (LONG 型)
BetweenDatesDataTableQuery - 入力: StartDateEndDate (DATETIME 型)。出力: result (LONG 型)
* 
このサービスは、クエリー対象の日付範囲のデータテーブルに、デモデータを自動的に取り込みます。
6. オプション。エントリがキャッシュから期限切れになるのを確認するために、CacheThingDemo に対して Never 以外の ExpirationPolicy で短い ExpirationTime を設定します。
CachingBetweenDatesDataTableQuery
/* Reads the Cached results of BetweenDatesDataTableQuery Service.
If Cache doesn't have the result, call BetweenDatesDataTableQuery and load results into Cache.

Service Name: CachingBetweenDatesDataTableQuery
Inputs: StartDate[DATETIME, required], EndDate[DATETIME, required]
Output: LONG
*/
// This example uses CacheThing.GetEntry, which requires a one-row infoTable populated with the CacheThing's DataShapes primaryKey getEntryByKey
let cacheEntryInfoTable = getCacheEntryInfoTable();
cacheEntryInfoTable.AddRow({"startDate": StartDate, "endDate": EndDate});
let getEntryResult = Things["CacheThingDemo"].GetEntry({
values: cacheEntryInfoTable
});
if (getEntryResult.getRowCount() === 0) {
// Cache didn't have results, so go to backing DataTable, by calling the BetweenDatesDataTableQuery Service
let queryResult = Things["CacheThingDemo"].BetweenDatesDataTableQuery({
StartDate: StartDate,
EndDate: EndDate
});

// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("PutEntry called for BetweenDatesQueryCache: {StartDate: " + StartDate + ", EndDate: " + EndDate + " value: " + queryResult + "}");
if (!queryResult || queryResult < 0) {
// BetweenDatesDataTableQuery should always populate with demo data if no data is in the StartDate - EndDate range
throw new Error("BetweenDatesDataTableQuery did not return results");
}
let newCacheEntryInfoTable = getCacheEntryInfoTable();
newCacheEntryInfoTable.AddRow({"startDate": StartDate, "endDate": EndDate, "longValue": queryResult});
Things["CacheThingDemo"].PutEntry({
values: newCacheEntryInfoTable
});
result = queryResult;
} else {
let row = getEntryResult.getRow(0);
logger.debug("Found the following entry already in Cache : {StartDate: " + row.startDate + ", EndDate: " + row.endDate + ", LongValue: " + row.longValue + "}");
result = row.longValue;
}
function getCacheEntryInfoTable() {
return Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "TimeSpanValueCacheDataShape"
});
}
BetweenDatesDataTableQuery
/*  This service queries a range of dates, based on the dateKey column, and returns the greatest longKey value found.
If there are no entries between StartDate and EndDate this Service dynamically populates demo data into demoDataTable, and returns that result.

Service Name: BetweenDatesDataTableQuery
Inputs: StartDate[DATETIME, required], EndDate[DATETIME, required]
Output: LONG
*/
if (StartDate >= EndDate) {
throw Error("StartDate must be before EndDate");
}
let dataTable = Things["DemoDataTable"];
let queryResult = queryGreatestLongKeyBetweenDates(dataTable, StartDate, EndDate);
if (queryResult.getRowCount() === 0) {
// If we don't get any results, first populate the date range with random values, then retry
// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("No values found between [" + StartDate + "] and [" + EndDate + "], populating 100 demo values and re-querying");
populateDemoDataTable(dataTable, StartDate, EndDate);
// Re-query the now populated DateTable
queryResult = queryGreatestLongKeyBetweenDates(dataTable, StartDate, EndDate);
result = queryResult.getRow(0).get("longKey");
} else {
result = queryResult.getRow(0).get("longKey");
}
// Queries the DataTable for the largest longKey column value between the given dates
// Returns: One-row InfoTable with the found row.
function queryGreatestLongKeyBetweenDates(dateTable, startDate, endDate) {
let greatestBetweenDatesQuery = {
"filters": {
"type": "Between",
"fieldName": "dateKey",
"from": startDate,
"to": endDate
},
"sorts": [{
"fieldName": "longKey",
"isAscending": false
}]
};
// Run the Query to find the greatest Value Between Two Dates
let queryResult = dataTable.QueryDataTableEntries({
maxItems: 1,
query: greatestBetweenDatesQuery,
});

logger.debug("Queried between [" + StartDate + "] and [" + EndDate + "], found:" + queryResult.toJSON());
return queryResult;
}
function populateDemoDataTable(dataTable, startDate, endDate) {
// add 100 random entries between startDate and endDate
for (let entry = 0; entry < 100; entry++) {
let randomTimeBetween = randomDate(startDate, endDate);
let entry = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "TimeValueDataShape"
});
entry.AddRow({
"longKey": Math.floor(Math.random() * 1000),
"dateKey": randomTimeBetween
});
Things["DemoDataTable"].AddDataTableEntry({
values: entry /* INFOTABLE */ ,
});
}
}
function randomDate(startDate, endDate) {
let randomTime = Math.random() * (endDate.getTime() - startDate.getTime()) + startDate.getTime();
return new Date(randomTime);
}
これは役に立ちましたか?