All Products
Search
Document Center

Cloud Monitor:Entity relationship query

Last Updated:Sep 26, 2025

Overview

UModel offers powerful graph query capabilities for complex operations based on entity relationships. This document describes basic graph query methods, including the graph-match and graph-call features. You can use these features to query relationships and paths between entities.

Basic concepts of graph query

Node and edge description structure

In graph queries, you can use a specific syntax to describe nodes and edges:

  • Nodes: Use parentheses ().

  • Edges: Use brackets [].

  • Description format: <variable_name>:<label> {key-value_property_pairs}.

Basic syntax examples

// Any node
()

// A node with a specific label  
(:"apm@apm.service")           // graph-match syntax
(:`apm@apm.service`)           // Cypher syntax

// A node with a label and properties
(:"apm@apm.service" { __entity_type__: 'apm.service' })

// A node with a named variable
(s:"apm@apm.service" { __entity_id__: '123456' })

// Any edge
[]

// A named edge
[edge]

// An edge with a type
[e:calls { __type__: "calls" }]

Syntax differences

graph-match vs. Cypher:

  • In the Structured Process Language (SPL) context, graph-match requires that you enclose special characters in double quotation marks.

  • As a standalone syntax, Cypher uses backticks to enclose labels. Note that if you use a backtick string format in Cypher, you must escape internal backticks with another backtick.

// graph-match syntax
.topo | graph-match (s:"apm@apm.service" {__entity_id__: '123'})-[e]-(d)
        project s, e, d

// Cypher syntax (In the backtick string format ``apm@apm.service``, use double backticks to enclose the string)
.topo | graph-call cypher(`
    MATCH (s:``apm@apm.service`` {__entity_id__: '35af918180394ff853be6c9b458704ea'})-[e]-(d)
    RETURN s, e, d
`)

Graph query path syntax

Graph query paths use ASCII characters to describe the direction of a relationship:

Path expression

Direction description

(A)-[e]->(B) or (A)-->(B)

A directed relationship from A to B

(A)<-[e]-(B) or (A)<--(B)

A directed relationship from B to A

(A)-[e]-(B) or (A)--(B)

A bidirectional relationship, regardless of direction

Return value structure

Node JSON format

{
  "id": "apm@apm.service:347150ad7eaee43d2bd25d113f567569",
  "label": "apm@apm.service", 
  "properties": {
    "__domain__": "apm",
    "__entity_type__": "apm.service",
    "__entity_id__": "347150ad7eaee43d2bd25d113f567569",
    "__label__": "apm@apm.service"
  }
}

Edge JSON format

{
  "startNodeId": "apm@apm.service:347150ad7eaee43d2bd25d113f567569",
  "endNodeId": "apm@apm.service.host:34f627359470c9d36da593708e9f2db7",
  "type": "contains",
  "properties": {
    "__type__": "contains"
  }
}

graph-match query

Basic syntax

.topo | graph-match <path>
        project <output>
      | [additional SPL operations]

Core attributes

  • A starting point is required: You must provide a label and the __entity_id__ property.

  • Intermediate node restriction: For intermediate nodes, you can specify only a label, not properties.

  • Multi-level jumps are not supported: The [*2..3] syntax is not supported.

  • Loop handling: Each edge can be traversed multiple times.

Practical application examples

1. Full-link path query

Find the complete service invocation path that starts from a specific operation:

.topo |
  graph-match (s:"apm@apm.service.operation" {__entity_id__: '6f0bb4c892effff81538df574a5cfcd9'})
              <-[e1]-(v1)-[e2:runs_on]->(v2)-[e3]->(v3)
  project s, 
          "e1.__type__", 
          "v1.__label__", 
          "e2.__type__", 
          "v2.__label__", 
          "e3.__type__", 
          "v3.__label__", 
          v3

Returned result:

  • s: The starting operation node.

  • e1.type: The relationship type of the first segment.

  • v1.label: The label of the intermediate node.

  • v2, v3: Information about subsequent nodes.

2. Neighbor node statistics

Collect statistics about the neighbor distribution for a specific service:

.topo |
  graph-match (s:"apm@apm.service" {__entity_id__: 'd6385f422b457a59cce1f0f725ac3e14'})
              -[e]-(d)   
  project eType="e.__type__", dLabel="d.__label__"
| stats cnt=count(1) by dLabel, eType
| sort cnt desc
| limit 20

3. Conditional path query

Find the endpoint of a path that meets specific conditions:

.topo |
  graph-match (s:"apm@apm.service.operation" {__entity_id__: '6f0bb4c892effff81538df574a5cfcd9'})
              <-[e1]-(v1)-[e2:runs_on]->(v2)-[e3]->(v3)
  project s, 
          "e1.__type__", 
          "v1.__label__", 
          "e2.__type__", 
          "v2.__label__", 
          "e3.__type__", 
          destId="v3.__entity_id__", 
          v3 
| where destId='9a3ad23aa0826d643c7b2ab7c6897591'
| project s, v3

graph-match limitations

Limitation type

Description

Solution

Starting point restriction

A label and __entity_id__ must be specified.

Use a known entity ID as the starting point.

Intermediate node restriction

Properties cannot be specified for intermediate nodes.

Use SPL for subsequent filtering.

Multi-level jumps are not supported

The [*2..3] syntax is not supported.

Use Cypher or step-by-step queries.

Loop handling

Edges can be traversed multiple times, which may create loops.

Use SPL to deduplicate results or limit the query depth.

graph-call query

getNeighborNodes function

Retrieves the neighbor nodes for a specified list of nodes and returns the neighbor nodes and their relationship types.

Syntax format

.topo | graph-call getNeighborNodes(type, depth, nodeList)

Parameter description

Parameter

Type

Description

Valid values

type

string

Traversal type

sequence, sequence_in, sequence_out, full

depth

int

Traversal depth

A positive integer. A value of 5 or less is recommended.

nodeList

array

Starting node list

An array of node descriptions

Return format

Field

Description

Example

srcNode

Source node JSON

{"id":"app@app.service:347150…", "label":"app@app.service", …}

destNode

Destination node JSON

{"id":"app@app.operation:73ef19…", "labels":"app@app.operation", …}

relationType

Relationship type

"contains", "calls", "runs_on"

srcPosition

Source node position

-1, 0, 1, 2…

Traversal type description

sequence: A directed sequence traversal that returns relationships in a specific order.

sequence_in: A subset of sequence. This is a directed sequence traversal that maintains only the order of incoming edges. In the returned result set, all nodes ultimately point to the starting node.

sequence_out: A subset of sequence. This is a directed sequence traversal that maintains only the order of outgoing edges. In the returned result set, all nodes ultimately originate from the starting node.

full: A full-direction traversal.

  • Traverses all nodes without considering the direction of edges.

Practical example

.topo | graph-call getNeighborNodes(
  'sequence', 1, 
  [
    (:"app@app.operation" {__entity_id__: '73ef19770998ff5d4c1bfd042bc00a0f'})
  ]
)

getDirectRelations function

This function returns the direct relationships between a specified list of nodes and excludes indirect relationships. It is typically used for batch upstream and downstream queries for multiple nodes.

Syntax format

.topo | graph-call getDirectRelations(nodeList)

Parameter description

Parameter

Type

Description

nodeList

array

Node list

Return format

Field

Description

relations

A JSON array of relationships. Each object contains startNodeId, endNodeId, type, and properties.

Practical example

.topo | graph-call getDirectRelations(
  [
    (:"app@app.service" {__entity_id__: '347150ad7eaee43d2bd25d113f567569'}),
    (:"app@app.operation" {__entity_id__: '73ef19770998ff5d4c1bfd042bc00a0f'})
  ]
)
{
  "startNodeId": "app@app.service:347150ad7eaee43d2bd25d113f567569",
  "endNodeId": "app@app.operation:73ef19770998ff5d4c1bfd042bc00a0f", 
  "type": "contains",
  "properties": {"__type__": "contains"}
}

Scenarios

Get the complete neighbor relationships of a service

-- Get all neighbors of a service (within 5 hops)
.topo | graph-call getNeighborNodes(
  'full', 3,
  [(:"apm@apm.service" {__entity_id__: 'user-service-id'})]
)
| stats cnt=count(1) by relationType
| sort cnt desc

Analyze a service invocation chain

-- Analyze the invocation pattern of a specific service
.topo |
  graph-match (s:"apm@apm.service" {__entity_id__: 'payment-service'})
              -[e:calls]-(d:"apm@apm.service")
  project 
    source_service="s.service_name",
    target_service="d.service_name", 
    call_type="e.__type__"
| stats call_count=count(1) by source_service, target_service
| sort call_count desc

Pod-to-node relationship chain

-- Trace the complete deployment chain of a pod
.topo |
  graph-match (pod:"k8s@k8s.pod" {__entity_id__: '347150ad7eaee43d2bd25d113f567569'})
              <-[r1:contains]->(node:"k8s@k8s.node")
              <-[r2:contains]-(cluster:"k8s@k8s.cluster")
  project 
    pod_name="pod.pod_name",
    node_name="node.node_name", 
    cluster_name="cluster.cluster_name",
    "r1.__type__",
    "r2.__type__"

Cloud resource dependency analysis

-- Analyze the network dependencies of an ECS instance
.topo | graph-call getNeighborNodes(
  'sequence_out', 2,
  [(:"acs@acs.ecs.instance" {__entity_id__: 'i-bp1234567890'})]
)
| extend relation_category = CASE
    WHEN relationType in ('belongs_to', 'runs_in') THEN 'infrastructure'
    WHEN relationType in ('depends_on', 'uses') THEN 'dependency'
    WHEN relationType in ('connects_to', 'accesses') THEN 'network'
    ELSE 'other' END
| stats cnt=count(1) by relation_category
| sort cnt desc
| limit 0, 100

Analyze the impact of an upstream failure

-- Find upstream services that may affect the target service
.topo | graph-call getNeighborNodes(
  'sequence_in', 3,
  [(:"apm@apm.service" {__entity_id__: '029ea8f3f65dbc8da226f2c8d55bb8c6'})]
)
| where relationType in ('calls', 'depends_on')
| extend impact_level = CASE
    WHEN srcPosition = '-1' THEN 'direct'
    WHEN srcPosition = '-2' THEN 'secondary'
    ELSE 'indirect' END
| extend parsed_service_name = json_extract_scalar(srcNode, '$.properties.service_name')
| project 
    upstream_service = parsed_service_name,
    impact_level,
    relation_type = relationType
| stats cnt=count(1) by impact_level, relation_type

Analyze the impact of a downstream failure

-- Find downstream services affected by a failure in the target service
.topo | graph-call getNeighborNodes(
  'sequence_out', 3,
  [(:"apm@apm.service" {__entity_id__: 'failing-service-id'})]
)
| where relationType in ('calls', 'depends_on')
| extend affected_service = json_extract_scalar(destNode, '$.properties.service_name')
| stats impact_count=count(1) by affected_service
| sort impact_count desc
| limit 20

Trace a permission chain

-- Trace the access path from a user to a resource
.topo |
  graph-match (user:"identity@user" {__entity_id__: 'user-123'})
              -[auth:authenticated_to]->(app:"apm@apm.service")
              -[access:accesses]->(resource:"acs@acs.rds.instance")
  project 
    user_id="user.user_id",
    app_name="app.service_name",
    resource_id="resource.instance_id",
    auth_method="auth.auth_method",
    access_level="access.permission_level"

Performance optimization suggestions

1. Control the query scope

  • Time range optimization: Use time fields to limit the query scope.

  • Limit traversal depth: A traversal depth greater than 5 significantly affects performance.

  • Precise starting point: Use a specific entity_id instead of a fuzzy match.

  • Choose the correct traversal type: Select sequence or full based on your requirements.

2. Control the result set

  • Use SPL to filter: Filter unwanted results immediately after the graph query.

  • Batch processing: For large graph queries, consider batch processing.

  • Result caching: For frequently queried paths, consider caching the results.