Udostępnij za pośrednictwem


Najlepsze rozwiązania dotyczące semantyki grafu język zapytań Kusto (KQL)

W tym artykule wyjaśniono, jak efektywnie i wydajnie używać funkcji semantyki grafu w języku KQL dla różnych przypadków użycia i scenariuszy. Pokazuje on, jak tworzyć wykresy i wykonywać zapytania za pomocą składni i operatorów oraz jak zintegrować je z innymi funkcjami i funkcjami języka KQL. Pomaga również użytkownikom uniknąć typowych pułapek lub błędów, takich jak tworzenie grafów, które przekraczają limity pamięci lub wydajności, lub stosowanie nieodpowiednich lub niezgodnych filtrów, projekcji lub agregacji.

Rozmiar grafu

Operator make-graph tworzy reprezentację grafu w pamięci. Składa się z samej struktury grafu i jej właściwości. Podczas tworzenia wykresu użyj odpowiednich filtrów, projekcji i agregacji, aby wybrać tylko odpowiednie węzły i krawędzie oraz ich właściwości.

W poniższym przykładzie pokazano, jak zmniejszyć liczbę węzłów i krawędzi oraz ich właściwości. W tym scenariuszu Bob zmienił menedżera z Alice na Eve, a użytkownik chce zobaczyć tylko najnowszy stan grafu dla swojej organizacji. Aby zmniejszyć rozmiar grafu, węzły są najpierw filtrowane według właściwości organizacji, a następnie właściwość zostanie usunięta z grafu przy użyciu operatora od projektu. Dzieje się tak samo w przypadku krawędzi. Następnie operator podsumowania wraz z arg_max służy do uzyskiwania ostatniego znanego stanu grafu.

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

Wyjście

pracownik topManager
Robert Mallory

Ostatni znany stan grafu

W przykładzie Rozmiar grafu pokazano, jak uzyskać ostatni znany stan krawędzi grafu przy użyciu summarize operatora i arg_max funkcji agregacji. Uzyskanie ostatniego znanego stanu to operacja intensywnie korzystająca z obliczeń.

Rozważ utworzenie zmaterializowanego widoku w celu zwiększenia wydajności zapytań w następujący sposób:

  1. Utwórz tabele, które mają pewne pojęcie wersji w ramach modelu. Zalecamy użycie datetime kolumny, której można później użyć do utworzenia serii czasowej grafu.

    .create table employees (organization: string, name:string, stateOfEmployment:string, properties:dynamic, modificationDate:datetime)
    
    .create table reportsTo (employee:string, manager:string, modificationDate: datetime)
    
  2. Utwórz zmaterializowany widok dla każdej tabeli i użyj funkcji agregacji arg_max, aby określić ostatni znany stan pracowników i relację 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. Utwórz dwie funkcje, które zapewniają, że używany jest tylko zmaterializowany składnik zmaterializowanego widoku, a zastosowane są dodatkowe filtry i projekcje.

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

Wynikowe zapytanie przy użyciu zmaterializowanego sprawia, że zapytanie jest szybsze i bardziej wydajne dla większych grafów. Umożliwia również większą współbieżność i mniejsze opóźnienia zapytań dotyczących najnowszego stanu grafu. Użytkownik nadal może wykonywać zapytania dotyczące historii grafu na podstawie pracowników i raportówW razie potrzeby

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

Podróż w czasie grafu

Niektóre scenariusze wymagają analizowania danych na podstawie stanu grafu w określonym punkcie w czasie. Podróż w czasie grafu używa kombinacji filtrów czasu i podsumowuje przy użyciu funkcji agregacji arg_max.

Poniższa instrukcja KQL tworzy funkcję z parametrem, który definiuje interesujący punkt w czasie dla grafu. Zwraca gotowy wykres.

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

Dzięki funkcji użytkownik może utworzyć zapytanie, aby uzyskać najwyższego menedżera Boba na podstawie grafu w czerwcu 2022 roku.

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

Wyjście

pracownik topManager
Robert Dave

Obsługa wielu typów węzłów i krawędzi

Czasami jest to wymagane do kontekstowania danych szeregów czasowych za pomocą grafu składającego się z wielu typów węzłów. Jednym ze sposobów obsługi tego scenariusza jest utworzenie grafu właściwości ogólnego przeznaczenia reprezentowanego przez model kanoniczny.

Czasami może być konieczne kontekstowe tworzenie kontekstu danych szeregów czasowych za pomocą grafu z wieloma typami węzłów. Problem można rozwiązać, tworząc wykres właściwości ogólnego przeznaczenia oparty na modelu kanonicznym, na przykład poniżej.

  • Węzłów
    • nodeId (ciąg)
    • label (ciąg)
    • properties (dynamic)
  • Krawędzie
    • źródło (ciąg)
    • miejsce docelowe (ciąg)
    • label (ciąg)
    • properties (dynamic)

W poniższym przykładzie pokazano, jak przekształcić dane w model kanoniczny i jak wykonywać względem niego zapytania. Tabele podstawowe dla węzłów i krawędzi grafu mają różne schematy.

Ten scenariusz obejmuje menedżera fabryki, który chce dowiedzieć się, dlaczego sprzęt nie działa dobrze i kto jest odpowiedzialny za jego naprawę. Menedżer decyduje się na użycie grafu, który łączy wykres zasobów podłogi produkcyjnej i hierarchię personelu konserwacyjnego, która zmienia się każdego dnia.

Na poniższym wykresie przedstawiono relacje między elementami zawartości i ich szeregami czasowym, takimi jak szybkość, temperatura i ciśnienie. Operatory i zasoby, takie jak pompa, są połączone za pośrednictwem krawędzi operacji . Sami operatorzy zgłaszają się do zarządzania.

Grafika informacyjna dotycząca scenariusza grafu właściwości.

Dane dla tych jednostek można przechowywać bezpośrednio w klastrze lub uzyskiwać przy użyciu federacji zapytań w innej usłudze, takiej jak Azure Cosmos DB, Azure SQL lub Azure Digital Twin. Aby zilustrować przykład, następujące dane tabelaryczne są tworzone w ramach zapytania:

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

Pracownicy, czujniki i inne jednostki i relacje nie współużytkują kanonicznego modelu danych. Operator unii umożliwia łączenie i kanonizowanie danych.

Poniższe zapytanie łączy dane czujników z danymi szeregów czasowych, aby znaleźć czujniki, które mają nietypowe odczyty. Następnie używa projekcji do utworzenia wspólnego modelu dla węzłów grafu.

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

Krawędzie są przekształcane w podobny sposób.

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

Za pomocą kanonicznych węzłów i danych krawędzi można utworzyć graf przy użyciu operatora make-graph w następujący sposób:

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

Po utworzeniu zdefiniuj wzorzec ścieżki i przeprojektuj wymagane informacje. Wzorzec rozpoczyna się od węzła tagu, po którym następuje zmienna długość krawędzi elementu zawartości. Ten zasób jest obsługiwany przez operatora, który raportuje do najwyższego menedżera za pośrednictwem krawędzi o zmiennej długości o nazwie reportsTo. Sekcja ograniczeń operatora dopasowania grafu, w tym przypadku , w którym zmniejsza tagi do tych, które mają anomalię i były obsługiwane w określonym dniu.

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)

Wyjście

tagWithAnomaly impactedAsset operatorName responsibleManager
temperature Pompy Eve Mallory

Projekcja w grafie odpowiada informacjom, że czujnik temperatury wykazał anomalię w określonym dniu. To było obsługiwane przez Eve, który ostatecznie zgłasza Mallory. Dzięki tym informacjom menedżer fabryki może skontaktować się z Eve i potencjalnie Mallory, aby lepiej zrozumieć anomalię.