Поделиться через


Рекомендации по семантике графа язык запросов Kusto (KQL)

В этой статье объясняется, как использовать функцию семантики графа в KQL эффективно и эффективно для различных вариантов использования и сценариев. В нем показано, как создавать и запрашивать графы с синтаксисом и операторами, а также как интегрировать их с другими функциями и функциями KQL. Кроме того, пользователи могут избежать распространенных ошибок или ошибок, таких как создание графов, превышающих ограничения памяти или производительности, а также применение неподходящих или несовместимых фильтров, проекций или агрегатов.

Размер графа

Оператор make-graph создает представление графа в памяти. Он состоит из самой структуры графа и ее свойств. При создании графа используйте соответствующие фильтры, проекции и агрегаты, чтобы выбрать только соответствующие узлы и края и их свойства.

В следующем примере показано, как уменьшить количество узлов и ребер и их свойств. В этом сценарии Боб изменил менеджера с Алисы на Ева, и пользователь хочет увидеть последнее состояние графа для своей организации. Чтобы уменьшить размер графа, узлы сначала фильтруются по свойству организации, а затем свойство удаляется из графа с помощью оператора project-away. То же самое происходит для ребер. Затем оператор суммировать вместе с 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

Выходные данные

Сотрудника topManager
Борис Мэллори

Последнее известное состояние графа

В примере графа показано, как получить последнее известное состояние ребер графа с помощью summarize оператора и arg_max функции агрегирования. Получение последнего известного состояния — это операция с интенсивным вычислением.

Рассмотрите возможность создания материализованного представления для повышения производительности запроса следующим образом:

  1. Создайте таблицы, имеющие некоторое представление о версии в рамках модели. Рекомендуется использовать datetime столбец, который можно использовать позже для создания временных рядов графа.

    .create table employees (organization: string, name:string, stateOfEmployment:string, properties:dynamic, modificationDate:datetime)
    
    .create table reportsTo (employee:string, manager:string, modificationDate: datetime)
    
  2. Создайте материализованное представление для каждой таблицы и используйте функцию агрегирования 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
    }
    
  3. Создайте две функции, которые гарантируют, что применяются только материализованные компоненты материализованного представления, а также применяются дополнительные фильтры и проекции.

    .create function currentEmployees () {
        materialized_view('employees_MV')
        | where stateOfEmployment == "employed"
    }
    
    .create function reportsTo_lastKnownState () {
        materialized_view('reportsTo_MV')
        | project-away modificationDate
    }
    

Результирующий запрос с помощью материализованных делает запрос более быстрым и эффективным для более крупных графов. Кроме того, он включает более высокий параллелизм и более низкие запросы задержки для последнего состояния графа. Пользователь по-прежнему может запрашивать журнал графов на основе таблиц employees и 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 года.

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

Выходные данные

Сотрудника topManager
Борис Dave

Работа с несколькими типами узлов и ребер

Иногда требуется контекстуализировать данные временных рядов с графом, состоящим из нескольких типов узлов. Одним из способов обработки этого сценария является создание графа свойств общего назначения, представленного канонической моделью.

Иногда может потребоваться контекстуализировать данные временных рядов с графом с несколькими типами узлов. Вы можете приблизиться к проблеме, создав граф свойств общего назначения, основанный на канонической модели, например следующей.

  • Узлов
    • nodeId (string)
    • label (string)
    • properties (dynamic)
  • Края
    • source (string)
    • назначение (строка)
    • label (string)
    • properties (dynamic)

В следующем примере показано, как преобразовать данные в каноническую модель и как запросить ее. Базовые таблицы для узлов и ребер графа имеют разные схемы.

Этот сценарий включает в себя менеджера фабрики, который хочет узнать, почему оборудование не работает хорошо и кто несет ответственность за его исправление. Менеджер решает использовать граф, который объединяет граф активов производственного этажа и иерархию персонала обслуживания, которые изменяются каждый день.

На следующем графике показаны отношения между активами и их временными рядами, такими как скорость, температура и давление. Операторы и активы, такие как насос, подключаются через ребра. Сами операторы сообщают об управлении.

Инфографика в сценарии графа свойств.

Данные для этих сущностей можно хранить непосредственно в кластере или получать с помощью федерации запросов к другой службе, например Azure Cosmos DB, AZURE SQL или Azure Digital Twin. Чтобы проиллюстрировать пример, в рамках запроса создаются следующие табличные данные:

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 в этом экземпляре , где сокращает теги до тех, которые имеют аномалию и работали в определенный день.

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 operatorName responsibleManager
Температура Pump Ева Мэллори

Проекция в граф-сопоставлении выводит сведения о том, что датчик температуры показал аномалию в указанный день. Она была оперирована Евом, который в конечном счете сообщает Мэллори. С этой информацией менеджер фабрики может обратиться к Евам и потенциально Mallory, чтобы получить лучшее понимание аномалии.