Blog Post

Fabric Updates Blog
7 MIN READ

More expressive graph analysis: GQL improvements in Microsoft Fabric Graph

tushar-msft's avatar
tushar-msft
Icon for Microsoft Employee rankMicrosoft Employee
8 hours ago

Coauthors: Idris Motiwala, and Justin Fine

January–August 2026 updates expand how users find, inspect, compose, and analyze connections.

Organizations use graph data to understand how information moves between systems, how services depend on one another, how conversations develop, and how people or organizations relate. Graph Query Language (GQL) in Microsoft Fabric Graph helps users explore and analyze those connections with standardized query patterns. 

From January through August 2026, new GQL capabilities made it easier to control traversals, inspect paths, compose multistage queries, summarize connected data, and work with complex values. These improvements help analysts express more graph questions in a single query and give developers finer control over query behavior. The following sections explain where each capability is useful and show the associated syntax. 

Find and control paths 

Shortest paths with ANY SHORTEST 

When several paths connect two entities, many applications need one shortest path rather than every possible path. ANY SHORTEST makes that intent explicit. This is useful for dependency analysis, recommendations, network routing, and investigations where fewer intermediate steps are preferred. 

MATCH ANY SHORTEST ({id: 1})->{,2}(d) 
RETURN d.id AS dst 

Quantified edge patterns 

Quantified edge patterns repeat a complete relationship pattern and can apply conditions to intermediate nodes. In the following example, each repeated step requires the source person to use Chrome. This gives developers more control than repeating only an edge pattern. 

MATCH p=(a:Person {id: 987})((x)-[k:knows]->() WHERE x.browserUsed = 'Chrome'){2}(b:Person) 
FOR n IN NODES(p) WITH OFFSET off 
RETURN n.browserUsed 
ORDER BY a.id, b.id, off 
LIMIT 12 

Complete path modes 

Path modes define whether a traversal can revisit nodes or edges.  

  • WALK permits repetition.  
  • TRAIL prevents repeated edges. 
  • SIMPLE prevents repeated interior nodes.  
  • ACYCLIC prevents any repeated node.  
  • DIFFERENT EDGES prevent one edge from satisfying more than one edge variable in the match.  

These choices help query results reflect the meaning of a route, hierarchy, or dependency chain. 

MATCH WALK (:Person {firstName: "Juliana"})-[:studyAt]->(:University)<-[:studyAt]-(p:Person) 
RETURN p.firstName AS name 
MATCH TRAIL (:Person {firstName: "Juliana"})-[:studyAt]->(:University)<-[:studyAt]-(p:Person) 
RETURN p.firstName AS name 
MATCH SIMPLE (x)->{2}(y) 
RETURN COUNT(*) AS path_count 
MATCH ACYCLIC (x:`node` {id: 1})->(y)->(z) 
RETURN x.id, y.id, z.id 
ORDER BY x.id, y.id, z.id 
MATCH DIFFERENT EDGES (p:Person {name: 'Keanu Reeves'})-[e:ACTED_IN]->(m:Movie) 
RETURN m.title AS movie 

Unbounded variable-length path patterns 

Variable-length paths previously required a maximum hop count. Unbounded quantifiers allow the graph structure to determine the traversal depth when no meaningful fixed limit exists. The example follows one or more relationships, while ACYCLIC prevents the traversal from visiting the same node twice. Unbounded refers to the path expression, not unlimited compute, so use selective starting points and test performance with representative graph density. 

MATCH ACYCLIC ()-[]->{1,}() 
RETURN COUNT(*) AS total 

Inspect paths and graph elements 

NODES, EDGES, and INNER_NODES 

These functions expose the contents of a matched path. NODES returns all nodes in the path, EDGES returns all relationships in the path, and INNER_NODES returns the intermediate nodes without the endpoints. Applications can use these results to explain lineage, dependencies, and investigative findings instead of returning only a destination. 

MATCH p=({id: 1})-[]->{0,4}(z) 
RETURN NODES(p) AS res_nodes, 
       INNER_NODES(p) AS res_inner_nodes 
ORDER BY z.id 

ELEMENT_ID 

ELEMENT_ID returns the identity of a graph element. It is useful when an application must compare, track, or reference a specific node or edge independently of its business properties. 

MATCH (x) 
RETURN ELEMENT_ID(x) AS vid, x AS actual_id 
ORDER BY x.id 
LIMIT 5 

SOURCE and DESTINATION 

SOURCE and DESTINATION identify the endpoints of an edge. This makes directional checks and edge-centric analysis easier, especially when a query starts from a relationship rather than a node. 

MATCH (x)-[e]->() 
RETURN x.id, SOURCE(e).id AS s 
ORDER BY e.src, e.dst 
LIMIT 10 
MATCH ()-[e]->(x) 
RETURN false IN COLLECT_LIST(x = DESTINATION(e)) AS no 

Compose larger queries 

NEXT 

NEXT passes the output of one query stage into the next, allowing analysts to express a multistep question in a single query. For example, they can calculate a measure, filter the result, traverse another relationship, and summarize the final result without moving intermediate data into application code. 

MATCH (m:Message)-[:hasCreator]-(p:Person) 
RETURN p, AVG(m.length) AS avg_msg_length GROUP BY p 
NEXT 
FILTER avg_msg_length > 100u 
MATCH (p)-[:knows]->(:Person) 
RETURN avg_msg_length, COUNT(*) AS connections 
GROUP BY p, avg_msg_length 

EXISTS subqueries 

EXISTS answers a yes-no question about another graph pattern. It can filter rows or participate inside an aggregate without requiring the outer query to return the matching details. 

MATCH (x) 
WHERE EXISTS { 
  MATCH (y) 
  WHERE y.id = 5 
  RETURN y 
} 
RETURN x.id AS xid 
ORDER BY xid 
MATCH (x) 
RETURN COUNT(*) FILTER ( 
  WHERE EXISTS { MATCH (x)-[]->(y) RETURN y } 
) AS count_with_out 

CALL subqueries 

CALL runs a contained query for each current outer result. In the example, it calculates each node’s outgoing degree and returns that value with the node identifier, keeping the intermediate calculation contained within the query. 

MATCH (x) 
CALL { 
  MATCH (x)-[e]->(y) 
  RETURN COUNT(e) AS out_degree 
} 
RETURN x.id AS xid, out_degree 
ORDER BY xid 

OPTIONAL CALL 

OPTIONAL CALL preserves the outer row when the contained query has no match. This supports enrichment scenarios where related information is useful but not required. 

MATCH (x {id: 1}) 
OPTIONAL CALL { 
  MATCH (y)-[]->(x) 
  RETURN y.id AS yid 
} 
RETURN x.id AS xid, yid 
ORDER BY yid 

UNION DISTINCT 

UNION DISTINCT combines compatible result sets and removes duplicate rows. It complements UNION ALL, which preserves duplicates, so the query can choose the correct set behavior. 

MATCH () LIMIT 3 
RETURN 42 AS x 
UNION 
MATCH () LIMIT 3 
RETURN 100u AS x 

Summarize connected data

Filtered and paged aggregates 

Filtered aggregates calculate a summary over a selected subset. Paging controls such as LIMIT can be applied within the aggregate, giving developers precise control over which values contribute to the result. 

MATCH (m:Movie) 
RETURN COUNT(*) FILTER (WHERE m.released < 1980 LIMIT 10) AS filtered_count 

Nested aggregations 

Nested aggregation supports layered questions, such as finding the minimum of averages calculated across groups or paths. This reduces the need to move intermediate results into application code. 

MATCH (a:`node`)-[e:`edge`]->{1,2}(b:`node`) 
RETURN a.id AS id, MIN(AVG(e.weight)) AS min_average 
GROUP BY id 

COLLECT_ONE, COLLECT_LIST, and COLLECT_ELEMENTS 

Collection functions retain values or graph elements for evaluation as a group. COLLECT_ONE selects one value, COLLECT_LIST builds a list, and COLLECT_ELEMENTS gathers graph elements. They are useful for path-level checks, ordered output, and analysis across repeated relationships. 

MATCH (p:`node` {id: 1})-[e]->{2}(p1:`node` {id: 1}) 
RETURN (COLLECT_ONE(e.weight) IN [e[0].weight, e[1].weight]) AS contained 
MATCH ()-[e]->{1,2}() 
RETURN COLLECT_LIST(ALL e.weight) AS w 
ORDER BY w 
LIMIT 20 
MATCH ()-[a:ACTED_IN]->{0,4}() 
WHERE SIZE(COLLECT_ELEMENTS(DISTINCT a.roles)) > 4 
RETURN TO_JSON_STRING(a.roles) AS r 

Work with values and text 

Dynamic nested data 

Support for dynamic nested data lets queries work with richer values that do not fit a flat property model. This is useful when graph entities contain structured application payloads, nested attributes, or evolving schemas. 

CAST 

 CAST converts a value to a supported target type. Explicit conversion makes comparisons and calculations more predictable when source data contains different numeric types. 

RETURN CAST(42 AS DOUBLE) AS double_res, 
       CAST(42 AS UINT) AS uint_res, 
       CAST(42.5d AS INT) AS int_res 

RANGE 

RANGE creates a numeric sequence. A generated sequence can support iteration, testing, bucketing, and list-oriented calculations within a query. 

RETURN RANGE(0, 4) AS res 

POWER 

POWER performs exponent calculations. It extends the numeric expressions available for scoring, weighting, and mathematical transformations. 

RETURN POWER(-2, 3) AS res 

NORMALIZE and IS NORMALIZED 

Unicode text can have visually identical but technically different encodings. NORMALIZE converts text to a normalization form, while IS NORMALIZED checks its current form. This supports more consistent matching and grouping across multilingual data. 

RETURN NORMALIZE('cafe\u0301') AS normalized_text 

RETURN 'cafe\u0301' IS NFKD NORMALIZED AS is_normalized 

Regular-expression functions 

The MSFT.REGEXP functions match, count, locate, extract, and replace text patterns. They help prepare identifiers, names, codes, and descriptions before those values are compared or summarized. 

RETURN MSFT.REGEXP_LIKE('café', 'caf.') AS x 

RETURN MSFT.REGEXP_COUNT('abcabcabc', 'abc') AS n 

RETURN MSFT.REGEXP_INSTR('hello world', 'world') AS p 

RETURN MSFT.REGEXP_REPLACE('a1b2c3', '[0-9]', '#') AS s 

RETURN MSFT.REGEXP_SUBSTR('abc123def', '[0-9]+') AS s 

Design guidance 

Use the narrowest pattern that answers the question. Start from selective nodes, specify relationship and node labels, and apply filters early. Use bounded traversal when the business question has a natural maximum. When depth is unknown, combine an unbounded path with an appropriate path mode and test it against representative graph density. Return only the properties or path elements the application requires. 

Feature availability can vary by environment and release stage.  Confirm the release stage and regional availability of each feature before publication. If availability differs by feature, identify the applicable release stage in each section. 

Next steps 

Start with a connected-data scenario such as lineage, dependency analysis, recommendations, investigations, or knowledge discovery. Identify the relationships the question must follow, choose the path mode and traversal depth that match the scenario, and then use composition and aggregation features to shape the result. 

Updated 5 hours ago
Version 2.0
No CommentsBe the first to comment