Your analytical data.
Graph query speed.
Fast relationship queries on ClickHouse and Apache Iceberg. A head-to-head comparison with Neo4j. One flight-network study shows how PuppyGraph performs across four multi-hop query patterns.
Illustrative route; the benchmark queries two or three hops.
The performance difference
Faster on every
tested query pattern.
PuppyGraph reading directly from ClickHouse or Iceberg outperformed Neo4j Community on all four query patterns in the 12.3M-record test.
Linear axis, 0–500 s for every query. Small values remain labeled even when their bars are very short.
Two-hop connections
Count qualifying two-hop paths between fixed airports by month.
View Cypher query for Q1
The airport filters use PuppyGraph vertex IDs and Neo4j airportId properties. The remaining query text is identical.
MATCH p = (a)-[r1]->(b)-[r2]->(c)
WHERE (r1.FlightDate = r2.FlightDate)
AND (r1.CRSArrTime + 100 <= r2.CRSDepTime)
AND (id(a) = "airport[12892]")
AND (id(c) = "airport[12953]")
AND (r1.Year = {_year})
WITH r1.Month as month, count(p) as path_count
RETURN month, path_count
ORDER BY month;MATCH p = (a)-[r1]->(b)-[r2]->(c)
WHERE (r1.FlightDate = r2.FlightDate)
AND (r1.CRSArrTime + 100 <= r2.CRSDepTime)
AND (a.airportId = "12892")
AND (c.airportId = "12953")
AND (r1.Year = {_year})
WITH r1.Month as month, count(p) as path_count
RETURN month, path_count
ORDER BY month;Years tested: 2020, 2021, 2022.
The benchmark runner substitutes {_year} with each value before execution. The chart summarizes all tested values across three rounds.
Two-hop arrival constraints
Add an actual-arrival versus departure condition to the two-hop connection query.
View Cypher query for Q2
The airport filters use PuppyGraph vertex IDs and Neo4j airportId properties. The remaining query text is identical.
MATCH p = (a)-[r1]->(b)-[r2]->(c)
WHERE (r1.FlightDate = r2.FlightDate)
AND (r1.CRSArrTime + 100 <= r2.CRSDepTime)
AND (r1.ArrTime >= r2.DepTime)
AND (id(a) = "airport[12892]")
AND (id(c) = "airport[12953]")
AND (r1.Year = {_year})
WITH r1.Month as month, count(p) as path_count
RETURN month, path_count
ORDER BY month;MATCH p = (a)-[r1]->(b)-[r2]->(c)
WHERE (r1.FlightDate = r2.FlightDate)
AND (r1.CRSArrTime + 100 <= r2.CRSDepTime)
AND (r1.ArrTime >= r2.DepTime)
AND (a.airportId = "12892")
AND (c.airportId = "12953")
AND (r1.Year = {_year})
WITH r1.Month as month, count(p) as path_count
RETURN month, path_count
ORDER BY month;Years tested: 2020, 2021, 2022.
The benchmark runner substitutes {_year} with each value before execution. The chart summarizes all tested values across three rounds.
Two-hop aircraft connections
Rank airports by distinct aircraft in qualifying two-hop sequences.
View Cypher query for Q3
The same query text is used for all three configurations.
MATCH ()-[r1]->(n)-[r2]->()
WHERE (r1.FlightDate = date("{_date}"))
AND (r1.FlightDate = r2.FlightDate)
AND (r1.Tail_Number = r2.Tail_Number)
AND (r1.Tail_Number IS NOT NULL)
AND (r1.Tail_Number <> "")
AND (r1.CRSArrTime < r2.CRSDepTime)
WITH n, count(DISTINCT r1.Tail_Number) AS cnt
RETURN n, cnt
ORDER BY cnt DESC
LIMIT 10;Dates tested: 2020-03-12, 2020-08-15, 2020-10-07, 2020-11-29, 2021-02-18, 2021-06-20, 2021-07-07, 2021-12-21, 2022-02-01, 2022-03-29.
The benchmark runner substitutes {_date} with each value before execution. The chart summarizes all tested values across three rounds.
Three-hop aircraft sequences
Count distinct aircraft in qualifying three-hop sequences.
View Cypher query for Q4
The same query text is used for all three configurations.
MATCH ()-[r1]->()-[r2]->()-[r3]->()
WHERE (r1.FlightDate = date("{_date}"))
AND (r1.FlightDate = r2.FlightDate)
AND (r1.FlightDate = r3.FlightDate)
AND (r1.Tail_Number = r2.Tail_Number)
AND (r1.Tail_Number = r3.Tail_Number)
AND (r1.Tail_Number IS NOT NULL)
AND (r1.Tail_Number <> "")
AND (r1.CRSArrTime < r2.CRSDepTime)
AND (r2.CRSArrTime < r3.CRSDepTime)
RETURN count(DISTINCT r1.Tail_Number) AS cnt;Dates tested: 2020-03-12, 2020-08-15, 2020-10-07, 2020-11-29, 2021-02-18, 2021-06-20, 2021-07-07, 2021-12-21, 2022-02-01, 2022-03-29.
The benchmark runner substitutes {_date} with each value before execution. The chart summarizes all tested values across three rounds.
All times are medians of three per-round means, in seconds. Results reflect this workload and deployment.
Test configuration
Data and queries
12,282,219 flight records and 435 airports from ClickHouse’s OnTime dataset (2020–2022, with partial 2022 coverage). All configurations use the same records and query parameters.
Deployment
Single-node deployments on AWS m6a.2xlarge hosts, each with 8 vCPUs and 32 GiB RAM. PuppyGraph reads directly from ClickHouse or Iceberg without using local tables, and runs on the same host as its data source. Neo4j runs on its own host.
Measurement
Three rounds per configuration, with warmup before timing. Each round averages query times across the tested parameters; charts show the median of those three averages, in seconds.
Versions
PuppyGraph 1.11.1 · ClickHouse 25.6.2.5 · Neo4j 2026.07.1 Community.
Explore the complete experiment.
Beyond the comparison shown here, the full experiment also tests PuppyGraph on 204.8 million flight records, compares direct source reads with local tables, and explores five Iceberg file layouts. Find the complete results, queries, setup instructions, and reproduction steps in the public benchmark repository.


