Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Applies to: ✅ Azure Data Explorer
This page provides reusable Kusto Query Language (KQL) patterns for quickly exploring graph datasets and answering common questions about structure, nodes, edges, and properties.
Tip
Looking for design and performance guidance? See Best practices for graph semantics.
Common analysis queries
These reusable query patterns work across all graph models and help you understand the structure and characteristics of any graph dataset. The example below use sample graphs available on our help cluster in the Samples database. For detailed information about these graphs, see Graph sample datasets and examples. Use these queries to explore new graphs, perform basic analysis, or as starting points for more complex graph investigations.
Graph overview and statistics
Understanding the basic characteristics of your graph is essential for analysis planning and performance optimization. These queries provide fundamental metrics about graph size and structure.
Count total nodes and edges:
Use these queries to understand the scale of your graph dataset. Node and edge counts help determine appropriate query strategies and identify potential performance considerations. These examples use the Simple
graph, which is ideal for learning basic graph operations.
// Get node count
graph('Simple')
| graph-match (node)
project node
| count
Count |
---|
11 |
// Get edge count
graph('Simple')
| graph-match (source)-[edge]->(target)
project edge
| count
Count |
---|
20 |
Get graph summary statistics:
This combined query efficiently provides both metrics in a single result, useful for initial graph assessment and reporting. This example demonstrates the technique using the Simple
graph.
let nodes = view() { graph('Simple') | graph-match (node) project node | count };
let edges = view() { graph('Simple') | graph-match (source)-[edge]->(target) project edge | count };
union withsource=['Graph element'] nodes, edges
Graph element | Count |
---|---|
nodes | 11 |
edges | 20 |
Alternative using graph-to-table:
For basic counting, the graph-to-table
operator can be more efficient as it directly exports graph elements without pattern matching overhead. This example shows the alternative approach using the same Simple
graph.
let nodes = view() { graph('Simple') | graph-to-table nodes | count };
let edges = view() { graph('Simple') | graph-to-table edges | count };
union nodes, edges
Count |
---|
11 |
20 |
Node analysis
Node analysis helps you understand the entities in your graph, their types, and distribution. These patterns are essential for data quality assessment and schema understanding.
Discover all node types (labels):
This query reveals the different entity types in your graph and their frequencies. Use it to understand your data model, identify the most common entity types, and spot potential data quality issues. This example uses the Simple
graph, which contains Person, Company, and City entities.
graph('Simple')
| graph-match (node)
project labels = labels(node)
| mv-expand label = labels to typeof(string)
| summarize count() by label
| order by count_ desc
label | count_ |
---|---|
Person | 5 |
Company | 3 |
City | 3 |
Find nodes with multiple labels:
Identifies nodes that belong to multiple categories simultaneously. This is useful for understanding overlapping classifications and complex entity relationships in your data model. This example uses the BloodHound_Entra
graph, which contains Microsoft Entra objects with multiple label classifications.
graph('BloodHound_Entra')
| graph-match (node)
project node_id = node.id, labels = labels(node), label_count = array_length(labels(node))
| where label_count > 1
| take 3
node_id | labels | label_count |
---|---|---|
2 | [ "AZBase", "AZServicePrincipal" ] |
2 |
4 | [ "AZBase", "AZUser" ] |
2 |
5 | [ "AZBase", "AZUser" ] |
2 |
Sample nodes by type:
Retrieves representative examples of specific node types to understand their structure and properties. Essential for data exploration and query development. This example uses the BloodHound_Entra
graph to explore AZUser node properties in Microsoft Entra environments.
graph('BloodHound_Entra')
| graph-match (node)
where labels(node) has "AZUser"
project node_id = node.id, properties = node.properties
| sample 2
node_id | properties |
---|---|
5 | { "lastseen": "2025-08-11T09:21:19.002Z[UTC]", "lastcollected": "2025-08-11T09:21:07.472380514Z[UTC]", "enabled": true, "displayname": "Jack Miller", "name": "JMILLER@PHANTOMCORP.ONMICROSOFT.COM", "tenantid": "6c12b0b0-b2cc-4a73-8252-0b94bfca2145", "objectid": "9a20c327-8cc7-4425-9480-11fb734db194", "onpremid": "", "usertype": "Member", "title": "", "userprincipalname": "jmiller@phantomcorp.partner.onmschina.cn", "system_tags": "admin_tier_0", "pwdlastset": "2021-06-16T17:51:03Z[UTC]", "onpremsyncenabled": false, "whencreated": "2021-06-16T17:29:16Z[UTC]", "email": "" } |
10 | { "lastseen": "2025-08-11T09:21:07.472380514Z[UTC]", "onpremid": "", "usertype": "Member", "title": "", "lastcollected": "2025-08-11T09:21:07.472380514Z[UTC]", "enabled": true, "userprincipalname": "cjackson@phantomcorp.partner.onmschina.cn", "system_tags": "admin_tier_0", "displayname": "Chris Jackson", "pwdlastset": "2022-07-19T15:18:49Z[UTC]", "onpremsyncenabled": false, "name": "CJACKSON@PHANTOMCORP.ONMICROSOFT.COM", "tenantid": "6c12b0b0-b2cc-4a73-8252-0b94bfca2145", "whencreated": "2022-07-19T15:01:55Z[UTC]", "email": "cjackson@phantomcorp.partner.onmschina.cn", "objectid": "bfb6a9c2-f3c8-4b9c-9d09-2924d38895f7" } |
Edge analysis
Understanding relationships in your graph is crucial for identifying patterns, data quality issues, and potential analysis directions.
Discover all edge types (works with different graph schemas):
This query identifies all relationship types in your graph, helping you understand the connections available for analysis. Different graphs use different property names for edge types, so multiple variations are provided. This example uses the BloodHound_Entra
graph to show permission relationships in Microsoft Entra environments.
graph('BloodHound_Entra')
| graph-match (source)-[edge]->(target)
project edge_labels = labels(edge)
| mv-expand label = edge_labels to typeof(string)
| summarize count() by label
| top 5 by count_ desc
label | count_ |
---|---|
AZMGAddOwner | 403412 |
AZMGAddSecret | 345324 |
AZAddSecret | 24666 |
AZContains | 12924 |
AZRunsAs | 6269 |
Find most connected nodes (highest degree):
Node degree analysis reveals the most influential or central entities in your graph. High-degree nodes often represent key players, bottlenecks, or important infrastructure components. This example uses the LDBC_SNB_Interactive
graph, a social network dataset ideal for analyzing connection patterns and influence.
// Find nodes with highest total degree (in + out)
graph('LDBC_SNB_Interactive')
| graph-match (node)
project node_id = node.id,
in_degree = node_degree_in(node),
out_degree = node_degree_out(node),
total_degree = node_degree_in(node) + node_degree_out(node)
| order by total_degree desc
| take 5
node_id | in_degree | out_degree | total_degree |
---|---|---|---|
0 | 41076 | 1 | 41077 |
1 | 35169 | 1 | 35170 |
50 | 12080 | 1 | 12081 |
49 | 11554 | 1 | 11555 |
58 | 7571 | 1 | 7572 |
Find nodes with highest in-degree (most incoming connections):
High in-degree nodes are often targets of influence, popular destinations, or central resources. In social networks, these might be influential people; in infrastructure graphs, these could be critical services. This example uses the LDBC_Financial
graph to identify accounts receiving the most transactions.
graph('LDBC_Financial')
| graph-match (node)
project node_id = node.node_id,
node_labels = labels(node),
in_degree = node_degree_in(node)
| order by in_degree desc
| take 3
node_id | node_labels | in_degree |
---|---|---|
Account::99079191802151398 | [ "ACCOUNT" ] |
314 |
Account::4868391197187506662 | [ "ACCOUNT" ] |
279 |
Account::4896538694858573544 | [ "ACCOUNT" ] |
184 |
Find nodes with highest out-degree (most outgoing connections):
High out-degree nodes are often sources of influence, distributors, or connector hubs. These entities typically initiate many relationships or distribute resources to others. This example uses the LDBC_Financial
graph to identify accounts making the most transactions.
graph('LDBC_Financial')
| graph-match (node)
project node_id = node.node_id,
node_labels = labels(node),
out_degree = node_degree_out(node)
| order by out_degree desc
| take 3
node_id | node_labels | out_degree |
---|---|---|
Account::236720455413661980 | [ "ACCOUNT" ] |
384 |
Account::56576470318842045 | [ "ACCOUNT" ] |
106 |
Account::4890627720347648300 | [ "ACCOUNT" ] |
81 |
Relationship pattern analysis
These queries help identify structural patterns and complex relationships that might indicate important behaviors or anomalies in your data.
Discover triangular relationships (nodes connected in a triangle):
Triangular patterns often indicate tight collaboration, mutual dependencies, or closed-loop processes. In social networks, these represent groups of friends; in business processes, they might indicate approval chains or redundancy patterns. This example uses the BloodHound_AD
graph to identify circular privilege relationships in Active Directory environments.
graph('BloodHound_AD')
| graph-match (a)-->(b)-->(c)-->(a)
where a.id != b.id and b.id != c.id and c.id != a.id
project node1 = a.name, node2 = b.name, node3 = c.name
| take 3
node1 | node2 | node3 |
---|---|---|
GHOST.CORP | USERS@GHOST.CORP | DOMAIN CONTROLLERS@GHOST.CORP |
WRAITH.CORP | USERS@WRAITH.CORP | DOMAIN CONTROLLERS@WRAITH.CORP |
DU001@PHANTOM.CORP | ADMINISTRATORS@PHANTOM.CORP | DOMAIN ADMINS@PHANTOM.CORP |
Property analysis
Understanding the properties available on your nodes helps you build more sophisticated queries and identify data quality issues.
Explore node properties:
This query reveals what information is stored with your nodes, helping you understand the available attributes for filtering and analysis. This example uses the BloodHound_Entra
graph to explore the schema of AZUser nodes and understand what properties are available for Microsoft Entra user objects.
graph('BloodHound_Entra')
| graph-match (node)
where labels(node) has "AZUser" // Replace with actual label
project properties = node.properties
| mv-apply properties on (
mv-expand kind=array properties
| where isnotempty(properties[1])
| extend bag =bag_pack(tostring(properties[0]), properties[1])
| summarize properties = make_bag(bag)
)
| summarize buildschema(properties)
schema_properties |
---|
{ "onpremsyncenabled": "bool", "system_tags": "string", "lastcollected": "string", "pwdlastset": "string", "usertype": "string", "userprincipalname": "string", "email": "string", "tenantid": "guid", "name": "string", "lastseen": "string", "displayname": "string", "enabled": "bool", "title": "string", "onpremid": "string", "objectid": "guid", "whencreated": "string" } |
Find all properties of all nodes by label:
This advanced schema discovery query identifies all property names that exist across nodes of each label type. Unlike the previous query that shows the schema structure, this query aggregates property names across all nodes of the same type, helping you understand which properties are consistently available and which might be optional or rare. This example uses the LDBC_SNB_Interactive
graph to explore the complete property landscape of different entity types in the social network dataset.
graph('LDBC_SNB_Interactive')
| graph-match (node)
project properties = node, labels = labels(node)
| mv-apply properties on (
mv-expand kind=array properties
| where isnotempty(properties[1])
| summarize properties = make_set(properties[0])
)
| mv-expand label = labels to typeof(string)
| summarize properties =make_set(properties) by label
| take 3
label | properties |
---|---|
TAGCLASS | [ "id", "node_id", "lbl", "name", "url" ] |
TAG | [ "id", "node_id", "lbl", "name", "url" ] |
FORUM | [ "id", "creationDate", "node_id", "lbl", "title" ] |
Find all properties of all edges by label:
This query performs schema discovery for edge (relationship) properties, showing what information is stored with each type of relationship in your graph. Understanding edge properties is crucial for analyzing relationship metadata such as timestamps, weights, confidence scores, or other attributes that provide context about connections. This example uses the BloodHound_AD
graph to explore the properties available on different types of Active Directory privilege relationships.
graph('BloodHound_AD')
| graph-match ()-[e]-()
project properties = e, labels = labels(e)
| mv-apply properties on (
mv-expand kind=array properties
| where isnotempty(properties[1])
| summarize properties = make_set(properties[0])
)
| mv-expand label = labels to typeof(string)
| summarize properties =make_set(properties) by label
| take 3
label | properties |
---|---|
GetChangesAll | [ "id", "lbl", "src", "dst", "properties", "lastseen" ] |
OwnsRaw | [ "id", "lbl", "src", "dst", "properties", "lastseen" ] |
AddKeyCredentialLink | [ "id", "lbl", "src", "dst", "properties", "lastseen" ] |
Find nodes with specific property values:
Use this pattern to locate entities with particular characteristics or to validate data quality by checking for expected property values. This example uses the BloodHound_Entra
graph to find nodes with specific name properties in Microsoft Entra environments.
graph('BloodHound_Entra')
| graph-match (node)
where isnotempty(node.properties.name)
project node_id = node.id, property_value = node.properties.name
| take 3
node_id | property_value |
---|---|
1 | JJACOB@PHANTOMCORP.ONMICROSOFT.COM |
10 | CJACKSON@PHANTOMCORP.ONMICROSOFT.COM |
12 | RHALL@PHANTOMCORP.ONMICROSOFT.COM |
Topology of the graph
Understanding the overall topology of your graph reveals the types of connections that exist between different entity types. This analysis helps you understand the data model, identify the most common relationship patterns, and discover potential paths for traversal queries. The topology query shows which node labels connect to which other node labels through specific edge types, providing a comprehensive view of your graph's structure.
//Topology of the graph - What's connected to what?
graph('LDBC_Financial')
| graph-match (src)-[e]->(dst)
project SourceLabels = labels(src), EdgeLabels = labels(e), DestinationLabels = labels(dst)
| mv-expand EdgeLabel = EdgeLabels to typeof(string)
| mv-expand SourceLabel = SourceLabels to typeof(string)
| mv-expand DestinationLabel = DestinationLabels to typeof(string)
| summarize Count = count() by SourceLabel, EdgeLabel, DestinationLabel
SourceLabel | EdgeLabel | DestinationLabel | Count |
---|---|---|---|
COMPANY | GUARANTEE | COMPANY | 202 |
COMPANY | APPLY | LOAN | 449 |
PERSON | APPLY | LOAN | 927 |
ACCOUNT | REPAY | LOAN | 2747 |
LOAN | DEPOSIT | ACCOUNT | 2758 |
ACCOUNT | TRANSFER | ACCOUNT | 8132 |
ACCOUNT | WITHDRAW | ACCOUNT | 9182 |
PERSON | GUARANTEE | PERSON | 377 |
COMPANY | OWN | ACCOUNT | 671 |
COMPANY | INVEST | COMPANY | 679 |
PERSON | OWN | ACCOUNT | 1384 |
MEDIUM | SIGN_IN | ACCOUNT | 2489 |
PERSON | INVEST | COMPANY | 1304 |