Advanced graph queries

更新时间:
复制 MD 格式

Overview

UModel supports advanced graph queries through the Cypher query language, including basic queries, multi-hop traversals, aggregation statistics, and path pattern matching. This topic also covers data integrity handling and performance optimization.

Detailed explanation of Cypher queries

Basic syntax structure

Cypher is a declarative query language. It follows a three-part structure similar to SQL:

.topo | graph-call cypher(`
    MATCH <graph query path>
    WHERE <filter condition> 
    RETURN <return content>
`)

Basic three-part queries

Single-node query

-- Query all nodes of a specific type
.topo | graph-call cypher(`
    MATCH (n {__entity_type__:"apm.service"})
    WHERE n.__domain__ STARTS WITH 'a' AND n.__entity_type__ = "apm.service"
    RETURN n
`)

Advantages over graph-match:

  • Supports the WHERE clause for complex filtering.

  • MATCH can contain only nodes without specifying relationships.

  • Supports queries on more properties, such as __entity_type__ and __domain__.

Relationship query

-- Query the call relationships between services
.topo | graph-call cypher(`
    MATCH (src:``apm@apm.service``)-[e:calls]->(dest:``apm@apm.service``)
    WHERE src.cluster = 'production' AND dest.cluster = 'production'
    RETURN src.service_name, dest.service_name, e.__type__
`)

Multi-hop queries

Basic multi-hop syntax

-- Find call chains with 2 to 3 hops
.topo | graph-call cypher(`
    MATCH (src {__entity_type__:"acs.service"})-[e:calls*2..3]->(dest)
    WHERE dest.__domain__ = 'acs'
    RETURN src, dest, dest.__entity_type__
`)

Important:

  • The multi-hop syntax is left-inclusive and right-exclusive. For example, *2..3 queries for exactly two hops.

  • *1..3 queries for one or two hops, but not three hops.

  • A single number, such as *3, queries for exactly three hops.

Multi-hop query scenarios

Connectivity analysis:

-- Find reachable paths between services
.topo | graph-call cypher(`
    MATCH (startNode:``apm@apm.service`` {service_name: 'gateway'})
          -[path:calls*1..4]->
          (endNode:``apm@apm.service`` {service_name: 'database'})
    RETURN startNode.service_name, length(path) as hop_count, endNode.service_name
`)

Impact chain analysis:

-- Analyze fault propagation paths
.topo | graph-call cypher(`
    MATCH (failed:``apm@apm.service`` {status: 'error'})
          -[impact:depends_on*1..3]->
          (affected)
    WHERE affected.__entity_type__ = 'apm.service'
    RETURN failed.service_name, 
           length(impact) as impact_distance,
           affected.service_name
    ORDER BY impact_distance ASC
`)

Node aggregation statistics

Basic aggregation query

-- Count the number of services in different domains
.topo | graph-call cypher(`
    MATCH (src {__entity_type__:"apm.service"})-[e:calls*2..3]->(dest)
    WHERE dest.__domain__ = 'apm'
    RETURN src, count(src) as connection_count
`)

Scenarios:

  • Connected component analysis: Identify connected subgraphs.

  • Centrality calculation: Find key nodes in the network.

  • Cluster detection: Discover tightly connected node groups.

Path pattern matching

Complex path patterns

-- Find specific topology patterns
.topo | graph-call cypher(`
    MATCH (src:``acs@acs.vpc.vswitch``)-[e1]->(n1)<-[e2]-(n2)-[e3]->(n3)
    WHERE NOT (src = n2 AND e1.__type__ = e2.__type__) 
        AND n1.__entity_type__ <> n3.__entity_type__ 
        AND NOT (src)<-[e1:``calls``]-(n1)
    RETURN src, e1.__type__, n1, e2.__type__, n2, e3.__type__, n3
`)

Scenarios:

  • Security audit: Detect abnormal network connectivity patterns.

  • Compliance check: Verify that the network architecture meets compliance requirements.

  • Pattern detection: Identify specific topology structures.

Full Cypher features: Support for custom node properties

Free property search

Query based on custom entity properties

-- Query using custom properties of entities
.topo | graph-call cypher(`
    MATCH (n:``acs@acs.alb.listener`` {ListenerId: 'lsn-rxp574lk5vu3gos29g'})-[e]->(d)
    WHERE d.vSwitchId CONTAINS 'vsw-bp1gvyids4uwo20crmp15' 
        AND d.user_id IN ['1654218965343050', '2'] 
        AND d.dnsName ENDS WITH '.com'
    RETURN n, e, d
`)

Combined property conditions

-- Query with complex property conditions
.topo | graph-call cypher(`
    MATCH (instance:``acs@acs.ecs.instance``)
    WHERE instance.instance_type STARTS WITH 'ecs.c6'
        AND instance.cpu_cores >= 4
        AND instance.memory_gb >= 8
        AND instance.status = 'Running'
        AND instance.create_time > '2024-01-01'
    RETURN 
        instance.instance_id,
        instance.instance_type,
        instance.cpu_cores,
        instance.memory_gb,
        instance.availability_zone
    ORDER BY instance.cpu_cores DESC, instance.memory_gb DESC
`)

Multi-level path output

Return path information

-- Return the full path information for multi-hop traversals
.topo | graph-call cypher(`
    MATCH (n:``acs@acs.alb.listener``)-[e:``calls``*2..3]-()
    RETURN e
`)

Path result format:

  • Returns an array of all edges in the path.

  • Each edge includes the start node, end node, and their properties.

  • Supports path length and path weight calculations.

Real-world business scenarios

1. Fine-grained connectivity search

Cross-network-layer connection analysis

-- Find the connection path from an ECS instance to a Server Load Balancer
.topo | graph-call cypher(`
    MATCH (start_node:``acs@acs.ecs.instance``)
          -[e*2..3]-
          (mid_node {ListenerName: 'entity-test-listener-zuozhi'})
          -[e2*1..2]-
          (end_node:``acs@acs.alb.loadbalancer``)
    WHERE start_node.__entity_id__ <> mid_node.__entity_id__ 
        AND start_node.__entity_type__ <> mid_node.__entity_type__
    RETURN 
        start_node.instance_name, 
        e, 
        mid_node.__entity_type__, 
        e2, 
        end_node.instance_name
`)

Service mesh connection analysis

-- Analyze traffic paths in a microservice mesh
.topo | graph-call cypher(`
    MATCH (client:``apm@apm.service``)
          -[request:calls]->
          (gateway:``apm@apm.gateway``)
          -[route:routes_to]->
          (service:``apm@apm.service``)
          -[backend:calls]->
          (database:``middleware@database``)
    WHERE client.environment = 'production'
        AND request.protocol = 'HTTP'
        AND route.load_balancer_type = 'round_robin'
    RETURN 
        client.service_name,
        gateway.gateway_name,
        service.service_name,
        database.database_name,
        request.request_count,
        backend.connection_pool_size
`)

2. Topology graph construction

Query equivalent to getNeighborNodes

-- Traditional method
.topo | graph-call getNeighborNodes(
  'sequence', 5, 
  [(:"acs@acs.alb.listener" {__entity_id__: '0b04e22d024b31a4cb880c0b2c652638'})]
)

-- Equivalent Cypher query
.topo | graph-call cypher(`
    MATCH (n:``acs@acs.alb.listener`` {__entity_id__: '0b04e22d024b31a4cb880c0b2c652638'})
          -[e1*1..6]->(),
          (n:``acs@acs.alb.listener`` {__entity_id__: '0b04e22d024b31a4cb880c0b2c652638'})
          <-[e2*1..6]-()
    RETURN n, e1, e2
`)

3. Fault diagnosis and impact analysis

Cascading failure analysis

-- Analyze the cascading impact of a service failure
.topo | graph-call cypher(`
    MATCH (failed_service:``apm@apm.service`` {status: 'error'})
    MATCH cascade_path = (failed_service)-[impact:depends_on*1..4]->(affected_service)
    WHERE affected_service.__entity_type__ = 'apm.service'
    RETURN 
        failed_service.service_name as root_cause,
        length(cascade_path) as impact_depth,
        affected_service.service_name as affected_service,
        affected_service.criticality_level,
        impact as dependency_chain
    ORDER BY impact_depth ASC, affected_service.criticality_level DESC
`)

Data integrity and troubleshooting

Analysis of missing data scenarios

UModel

Entity

Topo

Query behavior

Normal

Normal

Normal

Normal. All queries are available.

Normal

Normal

Missing 

Graph queries fail.

Normal

Missing 

Normal

Relationships are normal, but node information is empty.

Normal

Missing 

Missing 

Queries fail. The schema exists, but no data is available.

Missing 

Normal

Normal

Queries fail. The pure-topo mode works as expected.

Missing 

Normal

Missing 

Queries fail.

Missing 

Missing 

Normal

Queries fail. The pure-topo mode works as expected.

Missing 

Missing 

Missing 

Queries fail.

pure-topo mode

When entity data is incomplete, use pure-topo mode to query based only on relationship data:

-- Standard mode (requires complete data)
.topo | graph-call cypher(`
    MATCH (n:``acs@acs.alb.listener`` {ListenerId: 'lsn-123'})-[e]->(d)
    WHERE d.vSwitchId CONTAINS 'vsw-456'
    RETURN n, e, d
`)

-- pure-topo mode (relies only on relationship data)
.topo | graph-call cypher(`
    MATCH (n:``acs@acs.alb.listener``)-[e]->(d)
    RETURN n, e, d
`, 'pure-topo')

Features of pure-topo mode:

  • Advantage: Independent of entity data, resulting in faster queries.

  • Limitation: Cannot filter by custom entity properties.

  • Applicable scenarios: Topology analysis and relationship validation.

Data quality checks

Check data integrity

-- Check the data integrity of entities and relationships
.topo | graph-call cypher(`
    MATCH (n)-[e]->(m)
    RETURN 
        count(DISTINCT n) as unique_nodes,
        count(DISTINCT e) as unique_edges,
        count(DISTINCT e.__type__) as edge_types,
        collect(DISTINCT n.__entity_type__)[0..10] as node_types_sample
`)

Identify dangling relationships

-- Find relationships that point to non-existent entities
.let topoData = .topo | graph-call cypher(`
        MATCH ()-[e]->()
        RETURN e
    `)
    | extend startNodeId = json_extract_scalar(e, '$.startNodeId'), endNodeId = json_extract_scalar(e, '$.endNodeId'), relationType = json_extract_scalar(e, '$.type')
    | project startNodeId, endNodeId, relationType;
--$topoData
.let entityData = .entity with(domain='*', type='*') 
| project __entity_id__, __entity_type__, __domain__
| extend matchedId = concat(__domain__, '@', __entity_type__, ':', __entity_id__)
| join -kind='left' $topoData on matchedId = $topoData.endNodeId
| project matchedId, startNodeId, endNodeId, relationType
| extend status = COALESCE(startNodeId, 'Dangling')
| where status = 'Dangling';
$entityData

Performance optimization suggestions

1. Optimize query structure

Use indexes properly

-- Before optimization: Full table scan
.topo | graph-call cypher(`
    MATCH (n) WHERE n.service_name = 'web-app'
    RETURN n
`)

-- After optimization: Use a label index
.topo | graph-call cypher(`
    MATCH (n:``apm@apm.service`` {service_name: 'web-app'})
    RETURN n
`)

Filter conditions early

-- Before optimization: Late filtering
.topo | graph-call cypher(`
    MATCH (start)-[*1..5]->(endNode)
    WHERE start.environment = 'production' AND endNode.status = 'active'
    RETURN start, endNode
`)

-- After optimization: Early filtering
.topo | graph-call cypher(`
    MATCH (start {environment: 'production'})-[*1..5]->(endNode {status: 'active'})
    RETURN start, endNode
`)

2. Control the result set

Paging and limits

-- Use LIMIT to control the number of results
.topo | graph-call cypher(`
    MATCH (service:``apm@apm.service``)-[calls:calls]->(target)
    WHERE calls.request_count > 1000
    RETURN service.service_name, target.service_name, calls.request_count
    ORDER BY calls.request_count DESC
    LIMIT 50
`)

Result sampling

-- Sample a large result set
.topo | graph-call cypher(`
    MATCH (n:``apm@apm.service``)
    RETURN n.service
    LIMIT 100
`)
| extend seed = random()
| where seed < 0.1

3. Multi-hop optimization

Control the hop depth

-- Avoid excessively deep traversals
.topo | graph-call cypher(`
    MATCH (start)-[path*1..3]->(endNode)
    WHERE length(path) <= 2
    RETURN path
`)

Use directional optimization

-- Use relationship direction to reduce the search space
.topo | graph-call cypher(`
    MATCH (start)-[calls:calls*1..3]->(endNode)  -- Specify direction
    WHERE start.__entity_type__ = 'apm.service'
    RETURN start, endNode
`)