Skip to content

Commit 53bc6b4

Browse files
Merge pull request #7056 from cosh/tocAndPartitionedGraphs
Enhance graph documentation: add partitioning options to make-graph o…
2 parents 1de7562 + 4013010 commit 53bc6b4

File tree

2 files changed

+127
-43
lines changed

2 files changed

+127
-43
lines changed

data-explorer/kusto/query/make-graph-operator.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ The `make-graph` operator builds a graph structure from tabular inputs of edges
1717

1818
*Edges* `|` `make-graph` *SourceNodeId* `-->` *TargetNodeId* [ `with_node_id=` *NodeIdPropertyName* ]
1919

20+
*Edges* `|` `make-graph` *SourceNodeId* `-->` *TargetNodeId* [ `with` *Nodes1* `on` *NodeId1* [`,` *Nodes2* `on` *NodeId2* ]] `partitioned-by` *PartitionColumn* `(` *GraphOperator* `)`
21+
22+
*Edges* `|` `make-graph` *SourceNodeId* `-->` *TargetNodeId* [ `with_node_id=` *NodeIdPropertyName* ] `partitioned-by` *PartitionColumn* `(` *GraphOperator* `)`
23+
2024
## Parameters
2125

2226
| Name | Type | Required | Description |
@@ -27,11 +31,15 @@ The `make-graph` operator builds a graph structure from tabular inputs of edges
2731
| *Nodes1*, *Nodes2* | `string` | | The tabular expressions containing the properties of the nodes in the graph. |
2832
| *NodesId1*, *NodesId2* | `string` | | The corresponding columns with the node IDs in *Nodes1*, *Nodes2* respectively. |
2933
| *NodeIdPropertyName* | `string` | | The name of the property for node ID on the nodes of the graph. |
34+
| *PartitionColumn* | `string` | | The column to partition the graph by. Creates separate graphs for each unique value in this column. |
35+
| *GraphOperator* | `string` | | The graph operator to apply to each partitioned graph. |
3036

3137
## Returns
3238

3339
The `make-graph` operator returns a graph expression and must be followed by a [graph operator](graph-operators.md#supported-graph-operators). 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.
3440

41+
When using the `partitioned-by` clause, separate graphs are created for each unique value in the specified *PartitionColumn*. The specified *GraphOperator* is then applied to each partitioned graph independently, and the results are combined into a single output. This is particularly useful for multitenant scenarios where you want to analyze each tenant's data separately while maintaining the same graph structure and analysis logic.
42+
3543
> [!NOTE]
3644
> 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.
3745
@@ -115,6 +123,78 @@ edges
115123
|---|---|---|
116124
|Mallory|Bob|Trent|
117125

126+
### Multitenant partitioned graph
127+
128+
This example demonstrates using the `partitioned-by` clause to analyze a multitenant social network. The `partitioned-by` clause creates separate graphs for each unique value in the partition column (in this case, `tenantId`), applies the graph operator to each partition independently, and combines the results.
129+
130+
:::moniker range="azure-data-explorer"
131+
> [!div class="nextstepaction"]
132+
> <a href="https://dataexplorer.azure.com/clusters/help/databases/Samples?query=H4sIAAAAAAAAA62XbW%2FiOBDH3%2FMp5ngFJ6AkhKfesRLQ7kOv7VYLq9WpqlZuMiK%2BBjtyTCt27777jUPCgxO0W%2B4iVY2NPf%2Bfx%2BPx5OwMbmWACWj2GCEojBUmKDQXC1glqBJgvpJJAstVpHlMQzQKJnQCNakWTPBvTHMpknolQg0iNTWCgGmWGqwZGx%2BC80QrstjIJu91CLbEbSPAmCm9JPltl5LR7vdI%2BqnaebCmedyvQ%2BW%2BAvScncE8tXwOU7mMmViPoQkzJuCtYsLniS9hwtYwVsjSCdVVu%2B1UG1DNh5v3ccR9hCsZikQK03EpFlwgGnHTnKHgUsEFPmMkY1TUl4HUvlf1OsbqOVTvJBfajPalVAEXTGNC%2FfdNx3VbnjP0GtDpt%2Fp9b%2FjwT72xhXFtmIl8hNmS67AEZI5sCdfIghMI2jnBwCLo2ATTkKmII0yUfEndccPUE%2BqM4YYJtjjJB25O0LMIPJvggpMI3NGyfTTt9582vQp9LU%2BQ7gxz6aEl3bWlL58RvvCoPBCuVv85EDo5SXePpBjGEwrjW3yBP6V6SqMXYMvc22OemHcT609ww6MoRarOWESSJrB9X66EhpP2rO%2B1SKvdAK%2Fd6jvu4MBvfZvhnWJ0hq4R%2F%2FfzYzicnMO1OAY2x3sUag0X7JknhdCdxehzFvFEv5Kg0xoOc4K2RTC0CT48r2EaYln8nHh%2BjQfcXL9zqO%2B0bf0r5j%2FBPKSeLIg%2Fkss3yfrk82s8MMgIesM9gmLkTilyx6uEfH6QdZ39rDs173%2BQdRiLgO6aDedbEswO%2FFiwaP3abRrSqfI6Dh2vdsvt9d0DN7m2%2FDWnvbhZqThcl%2BzUWPkh15RuXo%2FQyRH6FkLHRrhha8pyNDkyrTslg1UqeNoeGe1urt21tD1b%2B1ayEN4xRQeiZPknHlWD0MsRPAuhayN8jPgzZ%2FBJBoovVvitcF5PS%2FgGws0hBilE5eG3CkXqZbAoL3gURpsDEvI4OSO7dGD8tAMeUb8gik1NlNY6mFrZr3USuVI%2BfrYqHqYWqO1OuwzaV57TyrY%2F0H8UCx2eE019V%2B1sq5wD5MPSpqyqoOVKpZO5pMbQuv5%2BNH5gVStlNYMvo4g9SpXuQgN6jQJS4aY15Z5UZnTfKkbKSgJLoFsUKGFaLunC9rMpXsNy4uSIE%2FuZwUJmP%2BLE3mZ8MRMfceIgs1%2B4x487cXhMwlpjt1FYReGGLPV7tobivV7OtHPi9NCJuzJlk3LKMm%2B5G50stopp0kLYUTvOT07pWUm4LBuVb9aWqjD%2BqOe3VIWcu%2FN8z2SkNJFU%2FoYle8LmQrE4hP1MAs3mm4MsAi%2F0VZB9aslNRqJO8%2BXEjf8xaD6utxkGailPara5ZNoPwV%2F7VBeOBA2FmnDqzXv81W21vIfmm5pw6%2Bl487yEqBCE08oUfhmBcPMGE%2FQXRTUs5i4YjSzP183wrd2UB%2BXXgAowc9F%2FdWOTw5OaloFcmURKmvmnXmsvrd%2B3H%2BqUO380ynnIbpujz86E%2B1NCR0aRUB1%2BB6dNz1YwVvIvuq1gRlum6XqgBE5TDaz51KXdhxptPDXzHaKuepXk5tl39YgCITZ%2BzQfQb5fk7Z0p99CUa5mq1P8FZTuk0NUPAAA%3D" target="_blank">Run the query</a>
133+
::: moniker-end
134+
135+
```kusto
136+
// Nodes table representing users across multiple tenants (organizations)
137+
let nodes = datatable(userId:string, tenantId:string, name:string, department:string, role:string, location:dynamic)
138+
[
139+
// Tenant: CompanyA - San Francisco Bay Area
140+
"u001", "CompanyA", "Alice Johnson", "Engineering", "Senior Developer", dynamic({"type": "Point", "coordinates": [-122.4194, 37.7749]}),
141+
"u002", "CompanyA", "Bob Smith", "Engineering", "Team Lead", dynamic({"type": "Point", "coordinates": [-122.4094, 37.7849]}),
142+
"u003", "CompanyA", "Charlie Brown", "Marketing", "Manager", dynamic({"type": "Point", "coordinates": [-122.4294, 37.7649]}),
143+
"u004", "CompanyA", "Diana Prince", "HR", "Director", dynamic({"type": "Point", "coordinates": [-122.3994, 37.7949]}),
144+
"u005", "CompanyA", "Eve Wilson", "Engineering", "Junior Developer", dynamic({"type": "Point", "coordinates": [-122.4394, 37.7549]}),
145+
// Tenant: CompanyB - New York Area
146+
"u006", "CompanyB", "Frank Miller", "Sales", "Account Manager", dynamic({"type": "Point", "coordinates": [-74.0060, 40.7128]}),
147+
"u007", "CompanyB", "Grace Lee", "Engineering", "Senior Developer", dynamic({"type": "Point", "coordinates": [-74.0160, 40.7228]}),
148+
"u008", "CompanyB", "Henry Davis", "Marketing", "Specialist", dynamic({"type": "Point", "coordinates": [-73.9960, 40.7028]}),
149+
"u009", "CompanyB", "Ivy Chen", "Engineering", "Team Lead", dynamic({"type": "Point", "coordinates": [-74.0260, 40.7328]}),
150+
"u010", "CompanyB", "Jack Thompson", "Operations", "Manager", dynamic({"type": "Point", "coordinates": [-73.9860, 40.6928]}),
151+
// Tenant: CompanyC - Austin Area
152+
"u011", "CompanyC", "Kate Anderson", "Finance", "Analyst", dynamic({"type": "Point", "coordinates": [-97.7431, 30.2672]}),
153+
"u012", "CompanyC", "Liam Murphy", "Engineering", "Architect", dynamic({"type": "Point", "coordinates": [-97.7331, 30.2772]}),
154+
"u013", "CompanyC", "Maya Patel", "Product", "Manager", dynamic({"type": "Point", "coordinates": [-97.7531, 30.2572]}),
155+
"u014", "CompanyC", "Noah Garcia", "Engineering", "Developer", dynamic({"type": "Point", "coordinates": [-97.7631, 30.2472]}),
156+
"u015", "CompanyC", "Olivia Rodriguez", "Marketing", "Director", dynamic({"type": "Point", "coordinates": [-97.7231, 30.2872]})
157+
];
158+
// Edges table representing relationships/interactions between users
159+
let edges = datatable(sourceUserId:string, targetUserId:string, tenantId:string, relationshipType:string, strength:int)
160+
[
161+
// CompanyA relationships
162+
"u001", "u002", "CompanyA", "reportsTo", 9,
163+
"u005", "u002", "CompanyA", "reportsTo", 8,
164+
"u002", "u003", "CompanyA", "collaborates", 6,
165+
"u001", "u005", "CompanyA", "mentors", 7,
166+
"u003", "u004", "CompanyA", "collaborates", 5,
167+
"u001", "u003", "CompanyA", "communicates", 4,
168+
// CompanyB relationships
169+
"u007", "u009", "CompanyB", "reportsTo", 9,
170+
"u006", "u010", "CompanyB", "reportsTo", 8,
171+
"u008", "u006", "CompanyB", "collaborates", 6,
172+
"u009", "u010", "CompanyB", "communicates", 5,
173+
"u007", "u008", "CompanyB", "mentors", 7,
174+
"u006", "u007", "CompanyB", "collaborates", 6,
175+
// CompanyC relationships
176+
"u014", "u012", "CompanyC", "reportsTo", 9,
177+
"u012", "u013", "CompanyC", "collaborates", 7,
178+
"u011", "u013", "CompanyC", "collaborates", 6,
179+
"u013", "u015", "CompanyC", "reportsTo", 8,
180+
"u012", "u015", "CompanyC", "communicates", 5,
181+
"u011", "u014", "CompanyC", "mentors", 6
182+
];
183+
edges
184+
| make-graph sourceUserId --> targetUserId with nodes on userId partitioned-by tenantId (
185+
graph-match cycles=none (n1)-[e*2..4]->(n2)
186+
where n1.userId != n2.userId and all(e, relationshipType == "collaborates") and
187+
geo_distance_2points(todouble(n1.location.coordinates[0]), todouble(n1.location.coordinates[1]),
188+
todouble(n2.location.coordinates[0]), todouble(n2.location.coordinates[1])) < 10000
189+
project Start = strcat(n1.name, " (", n1.tenantId, ")"), Tenants = map(e, tenantId), End = strcat(n2.name, " (", n2.tenantId, ")")
190+
)
191+
```
192+
193+
|Start|Tenants|End|
194+
|---|---|---|
195+
|Bob Smith (CompanyA)|[<br> "CompanyA",<br> "CompanyA"<br>]|Diana Prince (CompanyA)|
196+
|Henry Davis (CompanyB)|[<br> "CompanyB",<br> "CompanyB"<br>]|Grace Lee (CompanyB)|
197+
118198
## Related content
119199

120200
* [Graph operators](graph-operators.md)

data-explorer/kusto/query/toc.yml

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,52 +1278,56 @@ items:
12781278
href: variancepif-aggregation-function.md
12791279
- name: Graph
12801280
items:
1281-
- name: Graph semantics overview
1282-
href: graph-semantics-overview.md
1283-
- name: Graph sample data
1284-
href: graph-sample-data.md
1285-
- name: Graph exploration basics
1286-
href: graph-exploration-basics.md
1287-
- name: Graph operators
1281+
- name: Start here
12881282
items:
1289-
- name: Graph operators overview
1290-
href: graph-operators.md
1291-
- name: make-graph
1292-
href: make-graph-operator.md
1293-
- name: graph-match
1294-
href: graph-match-operator.md
1295-
- name: graph-shortest-paths
1296-
href: graph-shortest-paths-operator.md
1297-
- name: graph-mark-components
1298-
href: graph-mark-components-operator.md
1299-
- name: graph-to-table
1300-
href: graph-to-table-operator.md
1301-
- name: Graph functions
1283+
- name: Graph semantics overview
1284+
href: graph-semantics-overview.md
1285+
- name: Graph sample data
1286+
href: graph-sample-data.md
1287+
- name: Graph exploration basics
1288+
href: graph-exploration-basics.md
1289+
- name: Reference
13021290
items:
1303-
- name: all()
1304-
displayName: all() graph function
1305-
href: all-graph-function.md
1306-
- name: any()
1307-
displayName: any() graph function
1308-
href: any-graph-function.md
1309-
- name: map()
1310-
displayName: map() graph function
1311-
href: map-graph-function.md
1312-
- name: inner_nodes()
1313-
displayName: inner nodes() graph function, inner nodes
1314-
href: inner-nodes-graph-function.md
1315-
- name: labels()
1316-
displayName: labels() graph function, labels
1317-
href: labels-graph-function.md
1318-
- name: node_degree_in()
1319-
displayName: graph, node, Node degree in node_degree_in, indegree node_degree_in
1320-
href: node-degree-in.md
1321-
- name: node_degree_out()
1322-
displayName: graph, node, Node degree out node_degree_out, outdegree node_degree_out
1323-
href: node-degree-out.md
1324-
- name: Graph scenarios
1291+
- name: Operators
1292+
items:
1293+
- name: Graph operators overview
1294+
href: graph-operators.md
1295+
- name: make-graph
1296+
href: make-graph-operator.md
1297+
- name: graph-match
1298+
href: graph-match-operator.md
1299+
- name: graph-to-table
1300+
href: graph-to-table-operator.md
1301+
- name: graph-shortest-paths
1302+
href: graph-shortest-paths-operator.md
1303+
- name: graph-mark-components
1304+
href: graph-mark-components-operator.md
1305+
- name: Functions
1306+
items:
1307+
- name: all()
1308+
displayName: all() graph function
1309+
href: all-graph-function.md
1310+
- name: any()
1311+
displayName: any() graph function
1312+
href: any-graph-function.md
1313+
- name: map()
1314+
displayName: map() graph function
1315+
href: map-graph-function.md
1316+
- name: inner_nodes()
1317+
displayName: inner nodes() graph function, inner nodes
1318+
href: inner-nodes-graph-function.md
1319+
- name: labels()
1320+
displayName: labels() graph function, labels
1321+
href: labels-graph-function.md
1322+
- name: node_degree_in()
1323+
displayName: graph, node, Node degree in node_degree_in, indegree node_degree_in
1324+
href: node-degree-in.md
1325+
- name: node_degree_out()
1326+
displayName: graph, node, Node degree out node_degree_out, outdegree node_degree_out
1327+
href: node-degree-out.md
1328+
- name: Scenarios
13251329
href: graph-scenarios.md
1326-
- name: Graph best practices
1330+
- name: Best practices
13271331
href: graph-best-practices.md
13281332
- name: Geospatial
13291333
items:

0 commit comments

Comments
 (0)