Kusto 查询语言 (KQL) 图形语义最佳做法
本文介绍如何对不同的用例和方案高效地使用 KQL 中的图形语义功能。 它演示如何使用语法和运算符创建和查询图形,以及如何将它与其他 KQL 功能和函数集成。 它还可帮助用户避免常见的陷阱或错误,例如创建超出内存或性能上限的图形,或者应用不适合或不兼容的筛选器、投影或聚合。
图形的大小
make-graph 运算符会创建图形的内存中表示形式。 它由图形结构本身及其属性组成。 创建图形时,请使用适当的筛选器、投影和聚合来仅选择相关的节点和边缘及其属性。
以下示例演示如何减少节点和边缘及其属性的数量。 在此方案中,Bob 将经理从 Alice 更改为 Eve,用户只想查看其组织图形的最新状态。 为了缩减图形的大小,首先按组织属性筛选节点,然后使用 project-away 运算符从图形中移除该属性。 边缘也是如此。 然后,summarize 运算符与 arg_max 一起用于获取图形的最后已知状态。
let allEmployees = datatable(organization: string, name:string, age:long)
[
"R&D", "Alice", 32,
"R&D","Bob", 31,
"R&D","Eve", 27,
"R&D","Mallory", 29,
"Marketing", "Alex", 35
];
let allReports = datatable(employee:string, manager:string, modificationDate: datetime)
[
"Bob", "Alice", datetime(2022-05-23),
"Bob", "Eve", datetime(2023-01-01),
"Eve", "Mallory", datetime(2022-05-23),
"Alice", "Dave", datetime(2022-05-23)
];
let filteredEmployees =
allEmployees
| where organization == "R&D"
| project-away age, organization;
let filteredReports =
allReports
| summarize arg_max(modificationDate, *) by employee
| project-away modificationDate;
filteredReports
| make-graph employee --> manager with filteredEmployees on name
| graph-match (employee)-[hasManager*2..5]-(manager)
where employee.name == "Bob"
project employee = employee.name, topManager = manager.name
输出
employee | topManager |
---|---|
Bob | Mallory |
图形的最后已知状态
图形大小示例演示了如何使用 summarize
运算符和 arg_max
聚合函数获取图形边缘的最后已知状态。 获取最后已知状态是计算密集型操作。
请考虑创建具体化视图以提高查询性能,如下所示:
创建在模型中具有一些版本概念的表。 建议使用
datetime
列,它以后可用于创建图形时序。.create table employees (organization: string, name:string, stateOfEmployment:string, properties:dynamic, modificationDate:datetime) .create table reportsTo (employee:string, manager:string, modificationDate: datetime)
为每个表创建一个具体化视图,并使用 arg_max 聚合函数来确定员工的最后已知状态和 reportsTo 关系。
.create materialized-view employees_MV on table employees { employees | summarize arg_max(modificationDate, *) by name } .create materialized-view reportsTo_MV on table reportsTo { reportsTo | summarize arg_max(modificationDate, *) by employee }
创建两个函数,确保仅使用具体化视图的具体化组件,并应用其他筛选器和投影。
.create function currentEmployees () { materialized_view('employees_MV') | where stateOfEmployment == "employed" } .create function reportsTo_lastKnownState () { materialized_view('reportsTo_MV') | project-away modificationDate }
使用具体化生成的查询使查询更快、对较大的图形更高效。 它还具备更高的并发性和较低的查询延迟,便于获取图形的最新状态。 如果需要,用户仍可以根据员工和 reportsTo 表查询图形历史记录
let filteredEmployees =
currentEmployees
| where organization == "R&D"
| project-away organization;
reportsTo_lastKnownState
| make-graph employee --> manager with filteredEmployees on name
| graph-match (employee)-[hasManager*2..5]-(manager)
where employee.name == "Bob"
project employee = employee.name, reportingPath = hasManager.manager
图形按时间顺序查看
某些方案要求根据图形在特定时间点的状态分析数据。 图形按时间顺序查看使用时间筛选器的组合,并使用 arg_max 聚合函数进行汇总。
以下 KQL 语句会创建一个函数,其中包含一个参数,用于定义图形值得关注的时间点。 它会返回一个现成的图形。
.create function graph_time_travel (interestingPointInTime:datetime ) {
let filteredEmployees =
employees
| where modificationDate < interestingPointInTime
| summarize arg_max(modificationDate, *) by name;
let filteredReports =
reportsTo
| where modificationDate < interestingPointInTime
| summarize arg_max(modificationDate, *) by employee
| project-away modificationDate;
filteredReports
| make-graph employee --> manager with filteredEmployees on name
}
有了这个函数,用户就可以创建一个查询,基于 2022 年 6 月的图形获取 Bob 的上级经理。
graph_time_travel(datetime(2022-06-01))
| graph-match (employee)-[hasManager*2..5]-(manager)
where employee.name == "Bob"
project employee = employee.name, reportingPath = hasManager.manager
输出
employee | topManager |
---|---|
Bob | Dave |
处理多个节点和边缘类型
有时需要使用包含多个节点类型的图形来上下文化时序数据。 处理此方案的一种方法是创建由规范模型表示的常规用途属性图。
有时,可能需要使用具有多个节点类型的图形来上下文化时序数据。 可以通过创建基于规范模型(如下例)的常规用途属性图来处理该问题。
- nodes
- nodeId (string)
- label (string)
- properties (dynamic)
- edges
- source (string)
- destination (string)
- label (string)
- properties (dynamic)
以下示例演示如何将数据转换为规范模型以及如何对其进行查询。 图形节点和边缘的基表具有不同的架构。
此方案涉及一名工厂经理,他/她想要了解设备运行不佳的原因,以及谁负责修复设备。 该经理决定使用一个图形,将生产车间的资产图与每天更改的维护人员层次结构组合在一起。
下图显示了资产与其时序之间的关系,例如速度、温度和压力。 操作员和资产(如泵)通过操作边缘连接。 操作员自己向管理层上报。
这些实体的数据可以直接存储在群集中,也可以使用查询联合获取到其他服务,例如 Azure Cosmos DB、Azure SQL 或 Azure 数字孪生。 为了说明该示例,以下表格数据作为查询的一部分创建:
let sensors = datatable(sensorId:string, tagName:string, unitOfMeasuree:string)
[
"1", "temperature", "°C",
"2", "pressure", "Pa",
"3", "speed", "m/s"
];
let timeseriesData = datatable(sensorId:string, timestamp:string, value:double, anomaly: bool )
[
"1", datetime(2023-01-23 10:00:00), 32, false,
"1", datetime(2023-01-24 10:00:00), 400, true,
"3", datetime(2023-01-24 09:00:00), 9, false
];
let employees = datatable(name:string, age:long)
[
"Alice", 32,
"Bob", 31,
"Eve", 27,
"Mallory", 29,
"Alex", 35,
"Dave", 45
];
let allReports = datatable(employee:string, manager:string)
[
"Bob", "Alice",
"Alice", "Dave",
"Eve", "Mallory",
"Alex", "Dave"
];
let operates = datatable(employee:string, machine:string, timestamp:datetime)
[
"Bob", "Pump", datetime(2023-01-23),
"Eve", "Pump", datetime(2023-01-24),
"Mallory", "Press", datetime(2023-01-24),
"Alex", "Conveyor belt", datetime(2023-01-24),
];
let assetHierarchy = datatable(source:string, destination:string)
[
"1", "Pump",
"2", "Pump",
"Pump", "Press",
"3", "Conveyor belt"
];
员工、传感器以及其他实体和关系不共享规范数据模型。 可以使用联合运算符合并和规范数据。
以下查询将传感器数据与时序数据联接在一起,以查找具有异常读数的传感器。 然后,它使用投影为图形节点创建通用模型。
let nodes =
union
(
sensors
| join kind=leftouter
(
timeseriesData
| summarize hasAnomaly=max(anomaly) by sensorId
) on sensorId
| project nodeId = sensorId, label = "tag", properties = pack_all(true)
),
( employees | project nodeId = name, label = "employee", properties = pack_all(true));
边缘以类似的方式转换。
let edges =
union
( assetHierarchy | extend label = "hasParent" ),
( allReports | project source = employee, destination = manager, label = "reportsTo" ),
( operates | project source = employee, destination = machine, properties = pack_all(true), label = "operates" );
通过规范化的节点和边缘数据,可以使用 make-graph 运算符创建图形,如下所示:
let graph = edges
| make-graph source --> destination with nodes on nodeId;
创建后,定义路径模式并投影所需的信息。 模式从标记节点开始,后跟资产的可变长度边缘。 该资产由一个操作员操作,该操作员通过可变长度边缘(名为 reportsTo)向上级经理汇报。 graph-match 运算符的约束部分(在此例中为 where)将标记减少到具有异常且在特定日期操作的那些。
graph
| graph-match (tag)-[hasParent*1..5]->(asset)<-[operates]-(operator)-[reportsTo*1..5]->(topManager)
where tag.label=="tag" and tobool(tag.properties.hasAnomaly) and
startofday(todatetime(operates.properties.timestamp)) == datetime(2023-01-24)
and topManager.label=="employee"
project
tagWithAnomaly = tostring(tag.properties.tagName),
impactedAsset = asset.nodeId,
operatorName = operator.nodeId,
responsibleManager = tostring(topManager.nodeId)
输出
tagWithAnomaly | impactedAsset | 1operatorName | responsibleManager |
---|---|---|---|
温度 | 泵 | Eve | Mallory |
graph-match 中的投影输出了温度传感器在指定日期显示了异常这一信息。 它是由 Eve 操作的,Eve 最终向 Mallory 汇报。 利用此信息,工厂经理可以联系 Eve,并可能联系 Mallory,以便更好地了解异常情况。