CacheThing
CacheThing 提供了用于存储和检索键值数据的快速内存接口。它非常适合缓存成本高昂或特别耗时的操作 (例如慢速查询) 的结果。
由于 CacheThing 在内存中运行,因此,ThingWorx 重新启动时会清除其内容。在高可用性 (HA) 环境中,每个 ThingWorx 节点会保留其自己的独立缓存。因此,在某个节点上创建或更新缓存条目时不会自动共享给其他节点。这包括缓存无效调用,例如 PurgeCacheDeleteEntryDeleteEntryByKey
您可以使用 CacheThing 缓存各种类型的数据,包括:
对数据库事物进行成本高昂或频繁查询的结果
对值流或数据表进行成本高昂或频繁查询的结果
慢网络调用的结果,例如,使用 ContentLoaderFunctions 进行的调用
成本高昂的计算值
FileRepositories 中的常用小文件的内容
与访问数据库或网络资源等外部系统相比,使用 CacheThing 可以显著提高性能并减少资源使用。
使用指南
CacheThing 并不适合用作持久化数据存储。缓存在 CacheThing 中的任何数据都应该可以从原始数据重新生成。
CacheThing 中的数据可能会到期或被逐出。在使用 CacheThing 的服务中,应确保当缓存未返回结果时,系统能够从原始数据源重新获取数据。有关详情,请参阅示例
配置
必须为每个 CacheThing 配置包含主键的 DataShape。主键用于检索缓存中的条目。
支持复合主键,但它们会阻止使用下面列出的 ByKey 便利服务、到期策略和全局缓存上限。
到期策略
到期策略用于定义每个缓存条目的生存时间 (TTL)。可以使用“缓存条目到期时间 (秒)”设置来配置截止时间。缓存条目到期后,检索该值将返回空结果,这与已将该值从缓存中删除类似。
支持的到期策略:
缓存条目到期策略
说明
“从不”
系统从不根据缓存条目储存在缓存中的时间来移除该缓存条目。然而,该条目在下列情况中仍可能被逐出:缓存达到配置的最大容量上限时,或通过 DeleteEntry 服务进行手动清理时。配置为 Never 到期策略时,系统将忽略“缓存条目到期时间”设置。
自上次访问后的到期时间
如果在指定的截止时间内未读取条目,则条目将被移除。
自创建以来的到期时间
条目自创建起经过指定的时间后将被移除。
自上次修改后的到期时间
如果在指定的截止时间内未修改条目,则条目将被移除。
自上次操作后的到期时间
如果在指定的截止时间内未访问或修改条目,则条目将被移除。
有关接口到期策略的详情,请参阅接口到期策略
缓存上限 (逐出)
当缓存达到“缓存上限 (MB)”设置中指定的大小时,系统将自动逐出 (删除) 缓存条目。逐出过程将移除最不常用 (LFU) 的条目,以便为新条目腾出空间。条目被逐出后,检索该条目将返回一个空结果,这与已将该条目删除类似。
如果添加一个大条目,则可能会逐出多个小条目以容纳该条目。为避免此情况,在将条目添加到缓存之前,请使用 EstimateEntrySize 服务检查条目的大小。
“缓存逐出”和“到期”将独立运行:
逐出操作以缓存大小和使用频率为基础。
到期以时间为基础,由配置的到期策略定义。
全局缓存上限
平台子系统包括一个名为“每个 ThingWorx 节点的所有 CacheThing 共有上限 (MB)”的设置。此设置用于限制单个节点上所有 CacheThings 的总大小,以防止内存使用过量。禁用的 CacheThings 不包括在此限制内。
此配置可防止执行以下操作:
创建超过缓存上限的新 CacheThing
更新超过缓存上限的 CacheThing“缓存上限 (MB)”
导入超过缓存上限的 CacheThings (无论作为实体导入还是作为扩展的一部分),这会导致整个导入操作失败。
将缓存上限减小到低于所有 CacheThing 的当前“缓存上限 (MB)”之和
启用超过全局缓存上限的 CacheThing
更改缓存配置
对缓存配置进行任何更改都会自动清空缓存,以维持其内部一致性。编辑下列任一属性都将清除缓存:
“数据形状”- 包括修改已配置的 DataShape,例如添加或移除字段。
缓存条目到期策略
缓存条目到期时间 (秒)
缓存上限 (MB)
Cache Performs Loading of Missing Entries
服务
针对 CacheThing 提供了以下服务。在高可用性 (HA) 环境中,这些服务会影响执行服务所在的 ThingWorx 节点上的缓存。
仅当缓存的 DataShape 具有单一主键时,下面列出的 *ByKey 便利服务才可用。如果缓存的 DataShape 包括多个主键,则使用服务的完整信息表输入版本。
服务名称
说明
PutEntry
向缓存中添加条目。
输入值 - 使用为 CacheThing 配置的 DataShape 的单行信息表。
GetEntry
从缓存返回单行信息表结果。
如果未找到条目 (缓存缺失),且缓存未配置 Cache Performs Loading of Missing Entries 选项,则会返回空信息表。如果缓存已配置该选项,则会自动调用 LoadEntry 服务,并返回其结果。
条目从未添加至缓存、已从缓存中逐出或到期时,可能会发生缓存未命中。
输入值 - 使用为 CacheThing 配置的 DataShape 的单行信息表。应仅填充 Primary Key 值。
GetEntryByKey
从缓存返回单行信息表结果。
如果未找到该条目,则会返回一个空信息表。条目从未添加至缓存、已从缓存中逐出或到期时,可能会发生缓存未命中。
输入值 - 表示正在检索的条目主键的字符串。仅当 DataShape 具有单一主键时,此服务才可用。它最适合字符串主键,但也支持大多数具有明确字符串转换的类型。
LoadEntry
用于在发生缓存未命中时自动从数据源获取缓存数据。
如果已启用 Cache Performs Loading of Missing Entries 功能,则必须改写此服务。
DeleteEntry
删除缓存中的条目。
在高可用性环境中,这仅影响调用 DeleteEntry 服务的节点。
输入值 - 使用为 CacheThing 配置的 DataShape 的单行信息表。应仅填充 Primary Key 值。
DeleteEntryByKey
删除缓存中的条目。
在高可用性环境中,这仅影响调用 DeleteEntryByKey 服务的节点。
输入值 - 表示正在检索的条目主键的字符串。仅当 DataShape 具有单一主键时,此服务才可用。
PurgeCache
移除所有缓存条目。
在高可用性环境中,这仅影响调用 PurgeCache 服务的节点。
GetDataShape
返回为缓存配置的 DataShape。
SetDataShape
设置缓存配置表中的 DataShape 字段。更改此值将自动清除缓存。
GetEstimatedEntryCount
返回缓存中预估的条目数。正在进行的 PUT 和 DELETE 操作以及“到期”或“逐出”进程可能会影响准确性。
EstimateEntrySize
返回条目的预估大小 (如果已将其添加到缓存中)。此服务不会将条目实际放入缓存中,也不需要缓存中已有的任何条目。此服务主要用于估计适当的“缓存上限 (MB)”或防止将大条目添加到缓存中。
输入值 - 使用为 CacheThing 配置的 DataShape 的单行信息表。
改写 LoadEntry 服务
要启用 Cache Performs Loading of Missing Entries (通读缓存),必须改写 CacheThing 上的 LoadEntry 服务。当发生缓存未命中时,将在内部调用此服务,以允许缓存从源检索和存储数据。
直接调用 LoadEntry 服务不会向缓存添加条目。只有在缓存未命中期间由 GetEntry 内部调用 LoadEntry 时,才会添加条目。
输入
输入是与相应 GetEntry 调用的参数相匹配的单行信息表。通常,仅填充主键字段。
服务实施
LoadEntry 服务应检索或生成要从数据源缓存的值。这可能包括数据库查询、Web 服务调用或生成的值。避免更新服务中的应用程序状态,例如属性或文件。
不要从 LoadEntry 中调用其他缓存服务,例如 GetEntryPutEntry。缓存未命中后,缓存会自动处理存储返回值。
输出
输出必须是与输入具有相同 DataShape 的信息表。使用检索的数据填充非主键字段。任何异常都将传播到 GetEntry 调用中。
最佳做法是为“输出”结果信息表创建新信息表。尽管可以复用和填充输入值信息表,但由于信息表处理 Javascript 中某些基本类型的方式存在限制,这样做可能会导致意外行为。请查看示例以了解有关使用 LoadEntry 的详情。
通读缓存
选中 Cache Performs Loading of Missing Entries 复选框将启用通读缓存。在此模式下,当发生缓存未命中时,缓存会使用 CacheThing 上的 LoadEntry 服务自动加载缺失的条目值。
要支持此行为,必须改写 CacheThing 上的 LoadEntry 服务,以查询源数据并返回相应的值。当 GetEntry 服务在缓存中未找到任何值时,缓存会调用 LoadEntry 服务来检索值。
通读缓存有助于缓解惊群场景。如果在填充前对同一键发出多个并行 GetEntry 请求,则系统仅调用一次后端 LoadEntry 服务。LoadEntry 调用完成后,所有待处理 GetEntry 请求均会返回检索到的值。
有关提供 LoadEntry 服务实施的详情,请参阅上面的“改写 LoadEntry 服务”部分。
指标
提供了一些与缓存相关的指标。在高可用性 (HA) 环境中,每个节点的缓存都是独立的。应使用 platformcache_name 标签按节点处理指标。
以下缓存指标可用:
缓存指标
说明
thingworx_cache_hit_rate
导致在缓存中找到条目的缓存请求比率
thingworx_cache_hits
缓存查找方法返回缓存值的次数
thingworx_cache_request_count
缓存查找方法返回缓存值或未缓存值的次数
thingworx_cache_miss_rate
缓存查找方法返回未缓存 (新加载的) 值或空值的比率
thingworx_cache_misses
缓存查找方法返回未缓存 (新加载的) 值或空值的次数
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) 环境中,每个节点都有一个独立的缓存。在一个节点上添加、删除或清除缓存不会影响所有 ThingWorx 节点。
已禁用 Cache Performs Loading of Missing Entries 时 (非通读缓存):
1. 使用主键检索缓存中的条目。
2. 检查是否已填充结果 (返回的信息表中是否有一行)。
3. 如果找到结果,则使用该结果。
4. 如果未找到结果:
从源数据 (例如,Database Thing、DataTable、ContentLoader) 获取或生成所需条目。
使用 PutEntry 将条目添加到缓存中。
返回新缓存的条目。
已启用 Cache Performs Loading of Missing Entries 时 (通读缓存):
1. 改写 CacheThing 上的 LoadEntry 服务。
2. 使用主键检索缓存中的条目。
如果未找到条目,则系统会自动调用 LoadEntry 来填充值。
示例:使用 TimesTwo 服务 
TimesTwo 服务将获取一个数值,将该数值乘以 2,然后将结果存储在缓存中。要进行此设置,请按照下列步骤操作:
1. 使用以下字段创建 DataShape (TimesTwoDataShape):operand: (LONG, primaryKey)timesTwoValue: (LONG)
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 的 DataShape:
maxItems: (NUMBER, primaryKey)
nameMask: (STRING, primaryKey)
query: (QUERY, primaryKey)
isSortFirst: (BOOLEAN, primaryKey)
result: (INFOTABLE, DataShape RootEntityListV2)
2. 创建名为 Cached_QueryImplementingThingsV2 的 CacheThing,并分配 Cached_QueryImplementingThingsV2_DataShape 作为其 DataShape。
3. 要观察缓存到期行为,请配置短整型 ExpirationTime,并在 Cached_ReadThrough_QueryImplementingThingsV2 事物上设置除 Never 以外的 ExpirationPolicy
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 的 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 分配为其 DataShape,并启用 Cache Performs Loading of Missing Entries 选项。
3. 要观察缓存到期行为,请配置短整型 ExpirationTime,并在 Cached_ReadThrough_QueryImplementingThingsV2 事物上设置除 Never 以外的 ExpirationPolicy
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 服务来查找数据表中两个日期之间的最大值,并将结果存储在缓存中。唯一调用 CachingBetweenDatesDataTableQuery 最初会查询备份数据表,但后续相同调用将改为查询缓存。
要进行此设置,请按照下列步骤操作:
1. 使用以下字段创建 DataShape (TimeValueDataShape):longKey: (LONG, primaryKey)dateKey: (DATETIME, primaryKey)
2. 使用 TimeValueDataShape DataShape 创建 DataTable (DemoDataTable)。
3. 使用以下字段创建 DataShape (TimeSpanValueCacheDataShape):startDate: (DATETIME, primaryKey)endDate: (DATETIME, primaryKey)longValue: (LONG)
4. 使用配置的 TimeSpanValueCacheDataShape DataShape 创建 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);
}
这对您有帮助吗?