make-graph operator

Applies to: ✅ Microsoft FabricAzure Data ExplorerAzure MonitorMicrosoft Sentinel

The make-graph operator builds a graph structure from tabular inputs of edges and nodes.

Syntax

Edges | make-graph SourceNodeId --> TargetNodeId [ with Nodes1 on NodeId1 [, Nodes2 on NodeId2 ]]

Edges | make-graph SourceNodeId --> TargetNodeId [ with_node_id= DefaultNodeId ]

Parameters

Name Type Required Description
Edges string ✔️ The tabular source containing the edges of the graph, each row represents an edge in the graph.
SourceNodeId string ✔️ The column in Edges with the source node IDs of the edges.
TargetNodeId string ✔️ The column in Edges with the target node IDs of the edges.
Nodes string The tabular expressions containing the properties of the nodes in the graph.
NodesId string The columns with the node IDs in Nodes.
DefaultNodeId string The name of the column for the default node ID.

Returns

The make-graph operator returns a graph expression and must be followed by a graph operator. Each row in the source Edges expression becomes an edge in the graph with properties that are the column values of the row. Each row in the Nodes tabular expression becomes a node in the graph with properties that are the column values of the row. Nodes that appear in the Edges table but don't have a corresponding row in the Nodes table are created as nodes with the corresponding node ID and empty properties.

Note

Each node has a unique identifier. If the same node ID appears in both the Nodes1 and Nodes2 tables, a single node is created by merging their properties. If there are conflicting property values for the same node, one of the values is arbitrarily chosen.

Users can handle node information in the following ways:

  1. No node information required: make-graph completes with source and target.
  2. Explicit node properties: use up to two tabular expressions using "with Nodes1 on NodeId1 [, Nodes2 on NodeId2 ]."
  3. Default node identifier: use "with_node_id= DefaultNodeId."

Example

Edges and nodes graph

The following example builds a graph from edges and nodes tables. The nodes represent people and systems, and the edges represent different relationships between nodes. The make-graph operator builds the graph. Then, the graph-match operator is used with a graph pattern to search for attack paths leading to the "Trent" system node.

let nodes = datatable(name:string, type:string, age:int) 
[ 
  "Alice", "Person", 23,  
  "Bob", "Person", 31,  
  "Eve", "Person", 17,  
  "Mallory", "Person", 29,  
  "Trent", "System", 99 
]; 
let edges = datatable(source:string, destination:string, edge_type:string) 
[ 
  "Alice", "Bob", "communicatesWith",  
  "Alice", "Trent", "trusts",  
  "Bob", "Trent", "hasPermission",  
  "Eve", "Alice", "attacks",  
  "Mallory", "Alice", "attacks",  
  "Mallory", "Bob", "attacks"  
]; 
edges 
| make-graph source --> destination with nodes on name 
| graph-match (mallory)-[attacks]->(compromised)-[hasPermission]->(trent) 
  where mallory.name == "Mallory" and trent.name == "Trent" and attacks.edge_type == "attacks" and hasPermission.edge_type == "hasPermission" 
  project Attacker = mallory.name, Compromised = compromised.name, System = trent.name

Output

Attacker Compromised System
Mallory Bob Trent

Default node identifier

The following example builds a graph using only edges, with the name property as the default node identifier. This approach is useful when creating a graph from a tabular expression of edges, ensuring that the node identifier is available for the constraints section of the subsequent graph-match operator.

let edges = datatable(source:string, destination:string, edge_type:string) 
[ 
  "Alice", "Bob", "communicatesWith",  
  "Alice", "Trent", "trusts",  
  "Bob", "Trent", "hasPermission",  
  "Eve", "Alice", "attacks",  
  "Mallory", "Alice", "attacks",  
  "Mallory", "Bob", "attacks"  
]; 
edges 
| make-graph source --> destination with_node_id=name
| graph-match (mallory)-[attacks]->(compromised)-[hasPermission]->(trent) 
  where mallory.name == "Mallory" and trent.name == "Trent" and attacks.edge_type == "attacks" and hasPermission.edge_type == "hasPermission" 
  project Attacker = mallory.name, Compromised = compromised.name, System = trent.name

Output

Attacker Compromised System
Mallory Bob Trent