GFQL Query Patterns for Cypher¶
Overview¶
This document analyzes GFQL (GraphFrame Query Language) patterns from PyGraphistry and identifies equivalent Cypher patterns for the graph database. GFQL provides a declarative, chain-based approach to graph traversal that can inspire more expressive and maintainable Cypher queries in Graffold.
Key Insight: GFQL's chain operations model is conceptually similar to Cypher's pattern matching, but emphasizes progressive filtering and tagging through sequential operations.
Note: All Cypher examples in this document work on both Neo4j and Memgraph backends.
Core GFQL Concepts¶
1. Chain Operations¶
GFQL queries are built as chains of operations that progressively filter and traverse the graph:
# GFQL chain example
chain_operations = graphistry.Chain([
n(name="seed_nodes"), # Tag starting nodes
e_forward(hops=1, edge_match={...}), # Traverse edges
n(name="destination_nodes") # Tag destination nodes
])
Cypher Equivalent: Pattern matching with WHERE clauses and variable binding
MATCH (seed_nodes:Entity)
WHERE seed_nodes.property = value
MATCH (seed_nodes)-[r:RELATIONSHIP]->(destination_nodes:Entity)
WHERE r.property = value
RETURN seed_nodes, r, destination_nodes
Pattern Mappings¶
Pattern 1: Node Filtering with Tagging¶
GFQL Pattern:
Neo4j/Memgraph Cypher Equivalent:
Use Case in Graffold: Filter proteins by specific properties (e.g., subcellular location, gene symbol)
MATCH (proteins:Protein)
WHERE proteins.subcellular_location = "Nucleus"
AND proteins.gene_symbol IS NOT NULL
RETURN proteins
Pattern 2: Undirected Edge Traversal¶
GFQL Pattern:
Cypher Equivalent:
Use Case in Graffold: Find all proteins associated with a disease (bidirectional)
MATCH (p:Protein)-[r:ASSOCIATES_WITH]-(d:Disease)
WHERE d.name = "Alzheimer's disease"
RETURN p, r, d
Pattern 3: Multi-Hop Traversal with Fixed Depth¶
GFQL Pattern:
Cypher Equivalent:
Use Case in Graffold: Multi-hop protein-disease relationships (already implemented in US-001)
MATCH path = (p:Protein)-[*1..3]-(d:Disease)
WHERE p.uniprot_id = "P04637" // TP53
RETURN path,
[rel in relationships(path) | type(rel)] as relationship_chain,
[node in nodes(path) | node.name] as entity_chain
Pattern 4: Greedy Traversal to Fixed Point¶
GFQL Pattern:
Cypher Equivalent:
MATCH path = (start)-[*]-(end) // Variable-length path (greedy)
WHERE start.id = "seed_id"
RETURN path
Use Case in Graffold: Find all entities reachable from a seed protein (community detection)
MATCH path = (seed:Protein {uniprot_id: "P38398"})-[*]-(connected:Entity)
RETURN connected, length(path) as distance
ORDER BY distance
LIMIT 100
⚠️ Performance Warning: Greedy traversal can be expensive. Use LIMIT and depth constraints.
Pattern 5: Conditional Edge Filtering¶
GFQL Pattern:
e_forward(edge_match={
"originator_bank_country": "Latvia",
"beneficiary_bank_country": "Russia",
"amount_transactions": between(15800000, 16000000)
})
Cypher Equivalent:
MATCH (n)-[r:TRANSACTION]->(m)
WHERE r.originator_country = "Latvia"
AND r.beneficiary_country = "Russia"
AND r.amount >= 15800000 AND r.amount <= 16000000
RETURN n, r, m
Use Case in Graffold: Filter protein-disease associations by confidence score and evidence count
MATCH (p:Protein)-[r:ASSOCIATES_WITH]->(d:Disease)
WHERE r.avg_confidence >= 0.8
AND r.evidence_sources >= 5
AND r.last_seen >= datetime() - duration({days: 30})
RETURN p, r, d
ORDER BY r.avg_confidence DESC
Pattern 6: Progressive Filtering with Intermediate Tagging¶
GFQL Pattern:
chain_operations = graphistry.Chain([
n({"town": "Oakville"}, name="oakville_locations"),
e_forward(hops=1, name="direct_roads"),
n({"point_of_interest": "Movie theatre"}, name="theatres")
])
Cypher Equivalent:
MATCH (oakville_locations:Location {town: "Oakville"})
MATCH (oakville_locations)-[direct_roads:ROAD]-(theatres:Location)
WHERE theatres.point_of_interest = "Movie theatre"
RETURN oakville_locations, direct_roads, theatres
Use Case in Graffold: Find proteins in specific pathways that associate with cardiovascular diseases
MATCH (p:Protein)-[:PARTICIPATES_IN]->(pathway:Pathway)
WHERE pathway.name CONTAINS "cardiovascular"
MATCH (p)-[r:ASSOCIATES_WITH]->(d:Disease)
WHERE d.mondo_id STARTS WITH "MONDO:0005267" // Cardiovascular disease hierarchy
RETURN p, pathway, r, d
Pattern 7: Subgraph Extraction with Property Matching¶
GFQL Pattern:
chain_operations = graphistry.Chain([
n({"nodeId": contains(pat="expo")}),
e_forward(edge_match={"amount_transactions": between(15800000, 16000000)}),
n({"nodeId": contains(pat="soyuz")}, name="target_nodes")
])
Cypher Equivalent:
MATCH (source:Entity)
WHERE source.id CONTAINS "expo"
MATCH (source)-[r:TRANSACTION]->(target:Entity)
WHERE r.amount >= 15800000 AND r.amount <= 16000000
AND target.id CONTAINS "soyuz"
RETURN source, r, target
Use Case in Graffold: Find proteins with specific gene symbols that interact with diseases containing keywords
MATCH (p:Protein)
WHERE p.gene_symbol =~ ".*BRCA.*" // Regex match for BRCA genes
MATCH (p)-[r:ASSOCIATES_WITH]->(d:Disease)
WHERE d.name =~ "(?i).*cancer.*" // Case-insensitive cancer match
RETURN p, r, d
ORDER BY r.avg_confidence DESC
Adopted Patterns for Graffold¶
Pattern 1: Multi-Hop Traversal with Path Context ✅ IMPLEMENTED (US-001)¶
Rationale: Enables discovery of indirect protein-disease relationships through intermediate entities.
Implementation: src/agents/neo4j_query_agent.py
MATCH path = (seed:Entity)-[*1..{hop_depth}]-(target:Entity)
WHERE seed.id IN $seed_ids
WITH path,
[rel in relationships(path) | type(rel)] as rel_chain,
[node in nodes(path) | {name: node.name, type: labels(node)[0]}] as node_chain
RETURN path, rel_chain, node_chain
LIMIT 50
Benefits: - Discovers hidden connections (e.g., Protein → Pathway → Disease) - Provides full path context for explainability - Configurable depth (1-3 hops) for performance control
Pattern 2: Conditional Relationship Filtering¶
Rationale: Filter relationships by evidence quality (confidence, recency, source count).
Prototype:
MATCH (p:Protein)-[r:ASSOCIATES_WITH]->(d:Disease)
WHERE r.avg_confidence >= $min_confidence
AND r.evidence_sources >= $min_sources
AND r.last_seen >= datetime() - duration({days: $max_age_days})
RETURN p, r, d
ORDER BY r.avg_confidence DESC, r.evidence_sources DESC
LIMIT 20
Integration Point: Neo4jQueryAgent._retrieve_neighborhood() - add confidence filtering parameter
Pattern 3: Hierarchical Disease Traversal¶
Rationale: Leverage MONDO disease hierarchies for broader or narrower disease queries.
Prototype:
// Find all diseases under "Cardiovascular disease" hierarchy
MATCH (parent:Disease {mondo_id: "MONDO:0005267"})-[:IS_A*]->(child:Disease)
MATCH (p:Protein)-[r:ASSOCIATES_WITH]->(child)
RETURN p, r, child, parent
Integration Point: New method _expand_disease_hierarchy() in Neo4jQueryAgent
Pattern 4: Protein Pathway Co-occurrence¶
Rationale: Find proteins that share pathways with a seed protein (functional similarity).
Prototype:
MATCH (seed:Protein {uniprot_id: $seed_id})-[:PARTICIPATES_IN]->(pathway:Pathway)
MATCH (related:Protein)-[:PARTICIPATES_IN]->(pathway)
WHERE related.uniprot_id <> $seed_id
WITH related, collect(DISTINCT pathway.name) as shared_pathways, count(pathway) as pathway_count
WHERE pathway_count >= 2 // At least 2 shared pathways
RETURN related, shared_pathways, pathway_count
ORDER BY pathway_count DESC
LIMIT 10
Integration Point: New retrieval mode pathway_similarity in QueryModeRouter
Pattern 5: Temporal Relationship Analysis¶
Rationale: Track when relationships were first discovered and last updated (incremental updates).
Prototype:
MATCH (p:Protein)-[r:ASSOCIATES_WITH]->(d:Disease)
WHERE r.first_seen >= datetime() - duration({days: 30}) // New relationships
RETURN p, r, d, r.first_seen as discovered_date, r.evidence_sources
ORDER BY r.first_seen DESC
Integration Point: New query mode recent_discoveries for tracking new findings
Pattern 6: Evidence Provenance Tracking¶
Rationale: Trace relationships back to source PMIDs for citation and validation.
Prototype:
MATCH (p:Protein {uniprot_id: "P04637"})-[r:ASSOCIATES_WITH]->(d:Disease {name: "Cancer"})
UNWIND r.pmids as pmid
MATCH (abstract:Abstract {pmid: pmid})
RETURN p, d, pmid, abstract.title, abstract.publication_date
ORDER BY abstract.publication_date DESC
Integration Point: Add include_provenance=True parameter to query methods
Pattern 7: Subgraph Extraction by Community¶
Rationale: Extract subgraphs for specific communities (global query mode).
Prototype:
MATCH (n:Entity)
WHERE n.community_id = $community_id
MATCH path = (n)-[r]-(m:Entity)
WHERE m.community_id = $community_id
RETURN path
LIMIT 100
Integration Point: _global_query() in QueryModeRouter - add subgraph extraction option
Performance Considerations¶
GFQL vs Cypher Performance Patterns¶
| Pattern | GFQL Approach | Cypher Optimization (Neo4j & Memgraph) |
|---|---|---|
| Greedy traversal | to_fixed_point=True |
Use LIMIT and depth constraints [*1..5] |
| Large result sets | Client-side filtering | Use WHERE clauses early in query |
| Multi-hop | Progressive filtering | Use CYPHER runtime=slotted for timeout handling |
| Tagging | name parameter |
Use WITH clauses for intermediate results |
Timeout Protection (from US-020)¶
-- Works on both Neo4j and Memgraph
MATCH path = (n)-[*1..{hop_depth}]-(m)
WHERE n.id IN $seed_ids
RETURN path
LIMIT 50
Timeout Handling: 5s primary query, 2s fallback with reduced seed set
Implementation Roadmap¶
Phase 1: Core Patterns (Completed)¶
- ✅ Multi-hop traversal with path context (US-001)
- ✅ Timeout protection for expensive queries (US-020)
Phase 2: Evidence Quality Filtering (Recommended Next)¶
- [ ] Add
min_confidenceandmin_sourcesparameters to_retrieve_neighborhood() - [ ] Integrate into
QueryRequestmodel insrc/api/app.py - [ ] Update documentation with confidence filtering examples
Phase 3: Hierarchical Queries (Future)¶
- [ ] Implement
_expand_disease_hierarchy()method - [ ] Add
include_hierarchy=Trueparameter to disease queries - [ ] Create validation script for MONDO hierarchy coverage
Phase 4: Temporal and Provenance (Future)¶
- [ ] Add
recent_discoveriesquery mode - [ ] Implement
include_provenance=Trueparameter - [ ] Create API endpoint for PMID-based relationship lookup
Key Takeaways¶
- Declarative Patterns: GFQL's chain operations map cleanly to Cypher's pattern matching
- Progressive Filtering: Use multiple
MATCHclauses withWHEREfor step-by-step filtering - Tagging via Variables: Cypher variable names serve the same purpose as GFQL's
nameparameter - Performance: Always use
LIMIT, depth constraints, and early filtering for production queries - Path Context: Extract relationship chains and node sequences for explainability
References¶
- GFQL 10 Minutes Tutorial
- GFQL Chain Operations Guide
- Neo4j Cypher Manual (Cypher is also supported by Memgraph)
- Graffold Multi-Hop Traversal
- Graffold Timeout Protection