Partilhar via


Melhores práticas para Linguagem de Pesquisa Kusto semântica de gráfico (KQL) (Pré-visualização)

Aviso

Esta funcionalidade está atualmente em pré-visualização e pode estar sujeita a alterações. A semântica e a sintaxe da funcionalidade do gráfico podem mudar antes de serem lançadas como disponíveis geralmente.

Este artigo explica como utilizar a funcionalidade semântica de gráficos no KQL de forma eficaz e eficiente para diferentes casos e cenários de utilização. Mostra como criar e consultar gráficos com a sintaxe e os operadores e como integrá-los com outras funcionalidades e funções KQL. Também ajuda os utilizadores a evitar falhas ou erros comuns, tais como a criação de gráficos que excedam os limites de memória ou de desempenho ou a aplicação de filtros, projeções ou agregações inadequados ou incompatíveis.

Tamanho do gráfico

O operador make-graph cria uma representação dentro da memória de um gráfico. Consiste na própria estrutura do grafo e nas respetivas propriedades. Ao criar um gráfico, utilize filtros, projeções e agregações adequados para selecionar apenas os nós e arestas relevantes e as respetivas propriedades.

O exemplo seguinte mostra como reduzir o número de nós e arestas e as respetivas propriedades. Neste cenário, Bob mudou de gestor de Alice para Eva e o utilizador só quer ver o estado mais recente do gráfico para a sua organização. Para reduzir o tamanho do gráfico, os nós são primeiro filtrados pela propriedade da organização e, em seguida, a propriedade é removida do gráfico com o operador de project-away. O mesmo acontece com as arestas. Em seguida, resuma o operador juntamente com arg_max é utilizado para obter o último estado conhecido do gráfico.

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

Saída

funcionário topManager
Bob Mallory

Último estado conhecido do gráfico

O exemplo Tamanho do gráfico demonstrou como obter o último estado conhecido das margens de um gráfico através summarize do operador e da arg_max função de agregação. A obtenção do último estado conhecido é uma operação de computação intensiva.

Considere criar uma vista materializada para melhorar o desempenho da consulta, da seguinte forma:

  1. Crie tabelas que tenham alguma noção de versão como parte do modelo. Recomendamos que utilize uma datetime coluna que possa utilizar mais tarde para criar uma série temporal de gráficos.

    .create table employees (organization: string, name:string, stateOfEmployment:string, properties:dynamic, modificationDate:datetime)
    
    .create table reportsTo (employee:string, manager:string, modificationDate: datetime)
    
  2. Crie uma vista materializada para cada tabela e utilize a função de agregação arg_max para determinar o último estado conhecido dos funcionários e os relatóriosPara a relação.

    .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. Crie duas funções que garantam que apenas o componente materializado da vista materializada é utilizado e são aplicados filtros e projeções adicionais.

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

A consulta resultante com materializado torna a consulta mais rápida e eficiente para gráficos maiores. Também permite consultas de simultaneidade e latência mais baixas para o estado mais recente do gráfico. O utilizador ainda pode consultar o histórico de gráficos com base nas tabelas employees e reportsTo , se necessário

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

Viagem no tempo do gráfico

Alguns cenários exigem que analise dados com base no estado de um gráfico num ponto específico do tempo. A viagem no tempo do gráfico utiliza uma combinação de filtros de tempo e resumos com a função de agregação arg_max.

A seguinte instrução KQL cria uma função com um parâmetro que define o ponto interessante no tempo para o gráfico. Devolve um gráfico pronto a utilizar.

.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
}

Com a função implementada, o utilizador pode criar uma consulta para obter o gestor superior do Bob com base no gráfico em junho de 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

Saída

funcionário topManager
Bob Dave

Lidar com vários tipos de nós e limites

Por vezes, é necessário contextualizar dados de série temporal com um gráfico que consiste em vários tipos de nós. Uma forma de lidar com este cenário é criar um gráfico de propriedades para fins gerais representado por um modelo canónico.

Ocasionalmente, poderá ter de contextualizar dados de série temporal com um gráfico com vários tipos de nós. Pode abordar o problema ao criar um gráfico de propriedades para fins gerais baseado num modelo canónico, como o seguinte.

  • nós
    • nodeId (cadeia)
    • etiqueta (cadeia)
    • propriedades (dinâmicas)
  • arestas
    • origem (cadeia)
    • destino (cadeia)
    • etiqueta (cadeia)
    • propriedades (dinâmicas)

O exemplo seguinte mostra como transformar os dados num modelo canónico e como os consultar. As tabelas base para os nós e arestas do gráfico têm esquemas diferentes.

Este cenário envolve um gestor de fábrica que quer saber por que motivo o equipamento não está a funcionar bem e quem é responsável por corrigi-lo. O gestor decide utilizar um gráfico que combina o gráfico de ativos do piso de produção e a hierarquia de pessoal de manutenção que muda todos os dias.

O gráfico seguinte mostra as relações entre os recursos e as respetivas séries temporizadores, como velocidade, temperatura e pressão. Os operadores e os recursos, como a bomba, estão ligados através do limite de operações . Os próprios operadores comunicam-se à gestão.

Infográfico no cenário do gráfico de propriedades.

Os dados dessas entidades podem ser armazenados diretamente no cluster ou adquiridos através da federação de consultas para um serviço diferente, como o Azure Cosmos DB, o SQL do Azure ou o Azure Digital Twin. Para ilustrar o exemplo, os seguintes dados tabulares são criados como parte da consulta:

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"
];

Os funcionários, sensores e outras entidades e relações não partilham um modelo de dados canónico. Pode utilizar o operador sindical para combinar e canonizar os dados.

A seguinte consulta associa os dados do sensor aos dados da série temporal para encontrar os sensores que têm leituras anormais. Em seguida, utiliza uma projeção para criar um modelo comum para os nós de grafos.

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));

As arestas são transformadas de forma semelhante.

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" );

Com os dados de nós e arestas canonizados, pode criar um gráfico com o operador make-graph, da seguinte forma:

let graph = edges
| make-graph source --> destination with nodes on nodeId;

Depois de criado, defina o padrão de caminho e projete as informações necessárias. O padrão começa num nó de etiqueta seguido de uma margem de comprimento variável para um recurso. Esse recurso é operado por um operador que reporta a um gestor de topo através de uma margem de comprimento variável, denominada reportsTo. A secção de restrições do operador de correspondência de grafos, neste caso, em que reduz as etiquetas às que têm uma anomalia e foram operadas num dia específico.

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)

Saída

tagWithAnomaly impactedAsset operatorName responsibleManager
temperatura Pump Véspera Mallory

A projeção na correspondência de grafos produz a informação de que o sensor de temperatura mostrou uma anomalia no dia especificado. Foi operado por Eve que finalmente reporta a Mallory. Com estas informações, o gestor de fábrica pode contactar Eve e, potencialmente, Mallory para compreender melhor a anomalia.