Schema Diagram and Graph Statistics
Node Statistics
| Node Label | Count |
|---|---|
| Airport | 379 |
Edge Statistics
| Relationship | Count |
|---|---|
| FLIGHT | 17,412,876 |
Data Loading
Overview
The data ingestion pipeline has two stages. Initially, raw flight datasets are stored in Hadoop Distributed
File System (HDFS). We then utilize Spark to perform schema enforcement and type casting, persisting the
structured data as Delta Lake tables registered within a Hive Metastore. PuppyGraph then interfaces directly
with this Metastore via a schema.json file, enabling a zero-ETL graph overlay on the existing
data
lake.
Metastore Import
First we create a Spark session using its Python API. This stage handles the transformation of monthly CSV partitions into a unified, high-performance relational structure.
from pyspark.sql import SparkSession spark = ( SparkSession.builder .appName("ot_puppy") .master("spark://master:7077") # EXECUTORS .config("spark.executor.instances", "14") .config("spark.executor.cores", "8") .config("spark.executor.memory", "30g") .config("spark.driver.memory", "10g") .config("spark.cores.max", "112") # Memory management .config("spark.memory.fraction", "0.6") .config("spark.memory.storageFraction", "0.5") # Parallelism .config("spark.default.parallelism", "112") .config("spark.sql.shuffle.partitions", "112") # Delta Lake .config("spark.jars.packages", "io.delta:delta-core_2.12:2.4.0") .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") # HDFS - CORRECTED WAREHOUSE PATH .config("spark.hadoop.fs.defaultFS", "hdfs://master:9000") .config("spark.sql.warehouse.dir", "hdfs://master:9000/spark-warehouse") # Hive Metastore .config("spark.sql.catalogImplementation", "hive") .config("hive.metastore.uris", "thrift://master:9083") .getOrCreate() )
Next, we create the database and table, and load data from HDFS into the Hive Metastore. We used a big dataframe to store all our CSV files.
from pyspark.sql.functions import col, lit from pyspark.sql.types import ShortType, ByteType, IntegerType years = [2020, 2021, 2022] combined_df = None for year in years: year_path = f"hdfs:///ontime20-22/{year}_*.csv" print(f"Processing Year: {year} from {year_path}") temp_df = spark.read.format("csv") \ .option("header", "true") \ .option("inferSchema", "true") \ .load(year_path) processed_df = temp_df.select( col("Year").cast(ShortType()), col("Quarter").cast(ByteType()), col("Month").cast(ByteType()), col("DayofMonth").cast(ByteType()), col("DayOfWeek").cast(ByteType()), col("FlightDate").cast("date"), col("Reporting_Airline"), col("DOT_ID_Reporting_Airline").cast(IntegerType()), col("IATA_CODE_Reporting_Airline"), col("Tail_Number"), col("Flight_Number_Reporting_Airline").cast("string"), col("OriginAirportID").cast(IntegerType()), col("OriginAirportSeqID").cast(IntegerType()), col("OriginCityMarketID").cast(IntegerType()), col("Origin"), col("OriginCityName"), col("OriginState"), col("OriginStateFips").cast("string"), col("OriginStateName"), col("OriginWac").cast(IntegerType()), col("DestAirportID").cast(IntegerType()), col("DestAirportSeqID").cast(IntegerType()), col("DestCityMarketID").cast(IntegerType()), col("Dest"), col("DestCityName"), col("DestState"), col("DestStateFips").cast("string"), col("DestStateName"), col("DestWac").cast(IntegerType()), col("CRSDepTime").cast(IntegerType()), col("DepTime").cast(IntegerType()), col("DepDelay").cast(IntegerType()), col("DepDelayMinutes").cast(IntegerType()), col("DepDel15").cast(IntegerType()), col("DepartureDelayGroups").cast("string"), col("DepTimeBlk"), col("TaxiOut").cast(IntegerType()), col("WheelsOff").cast("string"), col("WheelsOn").cast("string"), col("TaxiIn").cast(IntegerType()), col("CRSArrTime").cast(IntegerType()), col("ArrTime").cast(IntegerType()), col("ArrDelay").cast(IntegerType()), col("ArrDelayMinutes").cast(IntegerType()), col("ArrDel15").cast(IntegerType()), col("ArrivalDelayGroups"), col("ArrTimeBlk"), col("Cancelled").cast(ByteType()), col("CancellationCode"), col("Diverted").cast(ByteType()), col("CRSElapsedTime").cast(IntegerType()), col("ActualElapsedTime").cast(IntegerType()), col("AirTime").cast(IntegerType()), col("Flights").cast(IntegerType()), col("Distance").cast(IntegerType()), col("DistanceGroup").cast(ByteType()), col("CarrierDelay").cast(IntegerType()), col("WeatherDelay").cast(IntegerType()), col("NASDelay").cast(IntegerType()), col("SecurityDelay").cast(IntegerType()), col("LateAircraftDelay").cast(IntegerType()) ) if combined_df is None: combined_df = processed_df else: combined_df = combined_df.union(processed_df) combined_df.write.format("delta") \ .mode("overwrite") \ .partitionBy("Year") \ .option("overwriteSchema", "true") \ .saveAsTable("ontime_202x") print("Delta table 'ontime_202x' created successfully.")
And then we create a database and put our big table into this database.
from pyspark.sql import DataFrame spark._jsparkSession.sql("CREATE DATABASE IF NOT EXISTS ontime20_22") # Verify it was created db_list = DataFrame(spark._jsparkSession.sql("SHOW DATABASES"), spark) print(db_list.show()) target_table = "ontime20_22.ontime_202x" combined_df.write.format("delta") \ .mode("overwrite") \ .partitionBy("Year") \ .option("overwriteSchema", "true") \ .saveAsTable(target_table) print(f"Table successfully saved to {target_table}")
PuppyGraph Import
The graph topology is defined through a comprehensive schema.json mapping. This configuration designates the relational tables as vertex and edge sources, defining the unique identifiers and rich attribute sets required for graph-traversal operations without duplicating the underlying physical storage.
{
"catalogs": [
{
"name": "test_202x",
"type": "deltalake",
"metastore": {
"type": "HMS",
"hiveMetastoreUrl": "thrift://master:9083"
},
"storage": {
"type": "FromMetaStore"
}
}
],
"graph": {
"vertices": [
{
"label": "airport",
"manyToOne": {
"sources": [
{
"source": {
"catalog": "test_202x",
"schema": "ontime20_22",
"table": "ontime_202x"
},
"id": {
"fields": [
{
"type": "INT",
"field": "OriginAirportID",
"alias": "puppy_id0"
}
]
}
},
{
"source": {
"catalog": "test_202x",
"schema": "ontime20_22",
"table": "ontime_202x"
},
"id": {
"fields": [
{
"type": "INT",
"field": "DestAirportID",
"alias": "puppy_id0"
}
]
}
}
]
}
}
],
"edges": [
{
"label": "flight",
"fromVertex": "airport",
"toVertex": "airport",
"tableSource": {
"catalog": "test_202x",
"schema": "ontime20_22",
"table": "ontime_202x"
},
"id": {
"fields": [
{"type": "DATE","field": "FlightDate","alias": "puppy_id_FlightDate"},
{"type": "STRING","field": "IATA_CODE_Reporting_Airline","alias": "puppy_id_IATA_CODE_Reporting_Airline"},
{"type": "STRING","field": "Flight_Number_Reporting_Airline","alias": "puppy_id_Flight_Number_Reporting_Airline"},
{"type": "INT","field": "OriginAirportID","alias": "puppy_id_OriginAirportID"},
{"type": "INT","field": "DestAirportID","alias": "puppy_id_DestAirportID"}
]
},
"fromId": {
"fields": [
{"type": "INT", "field": "OriginAirportID", "alias": "puppy_from_OriginAirportID"}
]
},
"toId": {
"fields": [
{ "type": "INT", "field": "DestAirportID", "alias": "puppy_to_DestAirportID"
}
]
},
"attributes": [
{"type": "SHORT", "field": "Year", "alias": "Year"},
{"type": "BYTE", "field": "Quarter", "alias": "Quarter"},
{"type": "BYTE", "field": "Month", "alias": "Month"},
{"type": "BYTE", "field": "DayofMonth", "alias": "DayofMonth"},
{"type": "BYTE", "field": "DayOfWeek", "alias": "DayOfWeek"},
{"type": "DATE", "field": "FlightDate", "alias": "FlightDate"},
{"type": "STRING", "field": "Reporting_Airline", "alias": "Reporting_Airline"},
{"type": "INT", "field": "DOT_ID_Reporting_Airline", "alias": "DOT_ID_Reporting_Airline"},
{"type": "STRING", "field": "IATA_CODE_Reporting_Airline", "alias": "IATA_CODE_Reporting_Airline"},
{"type": "STRING", "field": "Tail_Number", "alias": "Tail_Number"},
{"type": "STRING", "field": "Flight_Number_Reporting_Airline", "alias": "Flight_Number_Reporting_Airline"},
{"type": "INT", "field": "OriginAirportID", "alias": "OriginAirportID"},
{"type": "INT", "field": "OriginAirportSeqID", "alias": "OriginAirportSeqID"},
{"type": "INT", "field": "OriginCityMarketID", "alias": "OriginCityMarketID"},
{"type": "STRING", "field": "Origin", "alias": "Origin"},
{"type": "STRING", "field": "OriginCityName", "alias": "OriginCityName"},
{"type": "STRING", "field": "OriginCityName", "alias": "OriginCityName"},
{"type": "STRING", "field": "OriginState", "alias": "OriginState"},
{"type": "STRING", "field": "OriginStateFips", "alias": "OriginStateFips"},
{"type": "STRING", "field": "OriginStateName", "alias": "OriginStateName"},
{"type": "INT", "field": "OriginWac", "alias": "OriginWac"},
{"type": "INT", "field": "DestAirportID", "alias": "DestAirportID"},
{"type": "INT", "field": "DestAirportSeqID", "alias": "DestAirportSeqID"},
{"type": "INT", "field": "DestCityMarketID", "alias": "DestCityMarketID"},
{"type": "STRING", "field": "Dest", "alias": "Dest"},
{"type": "STRING", "field": "DestCityName", "alias": "DestCityName"},
{"type": "STRING", "field": "DestState", "alias": "DestState"},
{"type": "STRING", "field": "DestStateFips", "alias": "DestStateFips"},
{"type": "STRING", "field": "DestStateName", "alias": "DestStateName"},
{"type": "INT", "field": "DestWac", "alias": "DestWac"},
{"type": "INT", "field": "CRSDepTime", "alias": "CRSDepTime"},
{"type": "INT", "field": "DepTime", "alias": "DepTime"},
{"type": "INT", "field": "DepDelay", "alias": "DepDelay"},
{"type": "INT", "field": "DepDelayMinutes", "alias": "DepDelayMinutes"},
{"type": "INT", "field": "DepDel15", "alias": "DepDel15"},
{"type": "STRING", "field": "DepartureDelayGroups", "alias": "DepartureDelayGroups"},
{"type": "STRING", "field": "DepTimeBlk", "alias": "DepTimeBlk"},
{"type": "INT", "field": "TaxiOut", "alias": "TaxiOut"},
{"type": "STRING", "field": "WheelsOff", "alias": "WheelsOff"},
{"type": "STRING", "field": "WheelsOn", "alias": "WheelsOn"},
{"type": "INT", "field": "TaxiIn", "alias": "TaxiIn"},
{"type": "INT", "field": "CRSArrTime", "alias": "CRSArrTime"},
{"type": "INT", "field": "ArrTime", "alias": "ArrTime"},
{"type": "INT", "field": "ArrDelay", "alias": "ArrDelay"},
{"type": "INT", "field": "ArrDelayMinutes", "alias": "ArrDelayMinutes"},
{"type": "INT", "field": "ArrDel15", "alias": "ArrDel15"},
{"type": "INT", "field": "ArrivalDelayGroups", "alias": "ArrivalDelayGroups"},
{"type": "STRING", "field": "ArrTimeBlk", "alias": "ArrTimeBlk"},
{"type": "BYTE", "field": "Cancelled", "alias": "Cancelled"},
{"type": "STRING", "field": "CancellationCode", "alias": "CancellationCode"},
{"type": "BYTE", "field": "Diverted", "alias": "Diverted"},
{"type": "INT", "field": "CRSElapsedTime", "alias": "CRSElapsedTime"},
{"type": "INT", "field": "ActualElapsedTime", "alias": "ActualElapsedTime"},
{"type": "INT", "field": "AirTime", "alias": "AirTime"},
{"type": "INT", "field": "Flights", "alias": "Flights"},
{"type": "INT", "field": "Distance", "alias": "Distance"},
{"type": "BYTE", "field": "DistanceGroup", "alias": "DistanceGroup"},
{"type": "INT", "field": "CarrierDelay", "alias": "CarrierDelay"},
{"type": "INT", "field": "WeatherDelay", "alias": "WeatherDelay"},
{"type": "INT", "field": "NASDelay", "alias": "NASDelay"},
{"type": "INT", "field": "SecurityDelay", "alias": "SecurityDelay"},
{"type": "INT", "field": "LateAircraftDelay", "alias": "LateAircraftDelay"}
]
}
]
}
}
Queries
Select a query below to view its logic and description.
Q1. Direct Destinations from an Airport
This query lists airports directly reachable from a selected origin airport by one outgoing FLIGHT relationship during a selected year.
-- Q1. Direct Destinations from an Airport MATCH (:airport)-[f:flight]->(:airport) WHERE f.Origin = 'IND' AND f.Year = 2020 RETURN DISTINCT f.DestAirportID AS airport_id, f.Dest AS code, f.DestCityName AS city, f.DestState AS state ORDER BY code;
Equivalent query in GQL standard:
MATCH (origin:Airport {airport_id: $originAirportId})-[f:FLIGHT]->(dest:Airport) WHERE f.Year = $year RETURN DISTINCT dest.airport_id AS airport_id, dest.code AS code, dest.city AS city, dest.state AS state ORDER BY code LIMIT $limit;
Q2. Airports Reachable within K Hops
This query returns airports reachable from a selected airport within three directed flight legs.
-- Q2. Airports Reachable within K Hops MATCH (origin:airport) WHERE elementId(origin) = 'airport[12339]' MATCH p = (origin)-[flights:flight*1..3]->(:airport) WHERE all(flight IN flights WHERE flight.Year = 2020) RETURN DISTINCT last(flights).DestAirportID AS airport_id, last(flights).Dest AS code, last(flights).DestCityName AS city, length(p) AS hop_count ORDER BY hop_count, code;
Equivalent query in GQL standard:
MATCH p = (:Airport {airport_id: $originAirportId})-[f:FLIGHT]->{1,3}(dest:Airport) WHERE all(flight IN f WHERE flight.year = $Year) RETURN DISTINCT dest.airport_id AS airport_id, dest.code AS code, dest.city AS city, length(p) AS hop_count ORDER BY hop_count, code LIMIT $limit;
Q3. Airports Exactly Three Flights Away
This query finds airports exactly three outgoing flight legs away from a selected origin airport.
-- Q3. Airports Exactly Three Flights Away MATCH (origin:airport) WHERE elementId(origin) = 'airport[12339]' MATCH p = (origin)-[flights:flight*3]->(:airport) WHERE all(flight IN flights WHERE flight.Year = 2020) RETURN DISTINCT last(flights).DestAirportID AS airport_id, last(flights).Dest AS code, last(flights).DestCityName AS city, length(p) AS hop_count ORDER BY code;
Equivalent query in GQL standard:
MATCH p = (:Airport {airport_id: $originAirportId})-[f:FLIGHT]->{3,3}(dest:Airport) WHERE all(flight IN f WHERE flight.year = $Year) RETURN DISTINCT dest.airport_id AS airport_id, dest.code AS code, dest.city AS city, length(p) AS hop_count ORDER BY code LIMIT $limit;
Q4. Delayed One-Hop Destinations
This query filters one-hop destinations by departure delay on the FLIGHT relationship.
-- Q4. Delayed One-Hop Destinations MATCH (:airport)-[f:flight]->(:airport) WHERE f.Origin = 'IND' AND f.Year = 2020 AND f.DepDelayMinutes >= 120 RETURN f.DestAirportID AS airport_id, f.Dest AS code, f.FlightDate AS flight_date, f.IATA_CODE_Reporting_Airline AS airline, f.DepDelayMinutes AS departure_delay ORDER BY departure_delay DESC;
Equivalent query in GQL standard:
MATCH (origin:Airport {airport_id: $originAirportId})-[f:FLIGHT]->(dest:Airport) WHERE f.Year = $year AND f.DepDelayMinutes >= $minDelayMinutes RETURN dest.airport_id AS airport_id, dest.code AS code, f.FlightDate AS flight_date, f.IATA_CODE_Reporting_Airline AS airline, f.DepDelayMinutes AS departure_delay ORDER BY departure_delay DESC LIMIT $limit;
Q5. Airports by State and Code Prefix
This query filters Airport nodes by imported node properties without traversing relationships.
-- Q5. Airports by State and Code Prefix MATCH (:airport)-[f:flight]->(:airport) WHERE f.OriginState = 'CA' AND f.Origin STARTS WITH 'S' RETURN DISTINCT f.OriginAirportID AS airport_id, f.Origin AS code, f.OriginCityName AS city, f.OriginStateName AS state_name ORDER BY code;
Equivalent query in GQL standard:
MATCH (airport:Airport) WHERE airport.state = $state AND airport.code STARTS WITH $codePrefix RETURN airport.airport_id AS airport_id, airport.code AS code, airport.city AS city, airport.stateName AS state_name ORDER BY code LIMIT $limit;
Q6. Long Non-Cancelled Flights
This query filters FLIGHT relationships by relationship properties loaded from the OnTime CSV files.
-- Q6. Long Non-Cancelled Flights MATCH (:airport)-[f:flight]->(:airport) WHERE f.Year = 2020 AND f.Cancelled = 0 AND f.Distance >= 2000 RETURN f.Origin AS origin, f.Dest AS dest, f.FlightDate AS flight_date, f.IATA_CODE_Reporting_Airline AS airline, f.Distance AS distance ORDER BY distance DESC;
Equivalent query in GQL standard:
MATCH (origin:Airport)-[f:FLIGHT]->(dest:Airport) WHERE f.Year = $year AND f.Cancelled = 0 AND f.Distance >= $minDistance RETURN origin.code AS origin, dest.code AS dest, f.FlightDate AS flight_date, f.IATA_CODE_Reporting_Airline AS airline, f.Distance AS distance ORDER BY distance DESC LIMIT $limit;
Q7. Reachability between Two Airports
This query checks whether a destination airport is reachable from a source airport within three directed flight legs.
-- Q7. Reachability between Two Airports MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH (target:airport) WHERE elementId(target) = 'airport[12478]' MATCH p = (source)-[:flight*1..3]->(target) RETURN count(p) > 0 AS reachable LIMIT 1;
Equivalent query in GQL standard:
MATCH p = ANY (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,3}(:Airport {airport_id: $targetAirportId}) RETURN true AS reachable;
Q8. Shortest Airport Route
This query finds the shortest directed airport-to-airport route up to four flight legs.
-- Q8. Shortest Airport Route MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH (target:airport) WHERE elementId(target) = 'airport[12478]' MATCH p = shortestPath((source)-[:flight*1..4]->(target)) RETURN p, length(p) AS hop_count;
Equivalent query in GQL standard:
MATCH p = SHORTEST (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,4}(:Airport {airport_id: $targetAirportId}) RETURN p, length(p) AS hop_count;
Q9. Simple Routes up to Three Legs
This query enumerates simple directed routes between two airports up to three flight legs.
-- Q9. Simple Routes up to Three Legs MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH (target:airport) WHERE elementId(target) = 'airport[12478]' MATCH p = (source)-[:flight*1..3]->(target) WHERE all(n IN nodes(p) WHERE single(m IN nodes(p) WHERE m = n)) RETURN p, length(p) AS hop_count ORDER BY hop_count;
Equivalent query in GQL standard:
MATCH p = ACYCLIC (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,3}(:Airport {airport_id: $targetAirportId}) RETURN p, length(p) AS hop_count ORDER BY hop_count LIMIT $limit;
Q10. Top Weighted Routes by Distance
This query ranks bounded airport routes by cumulative flight distance.
-- Q10. Top Weighted Routes by Distance MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH (target:airport) WHERE elementId(target) = 'airport[12478]' MATCH p = (source)-[:flight*1..3]->(target) WHERE all(n IN nodes(p) WHERE single(m IN nodes(p) WHERE m = n)) WITH p, reduce(total = 0, f IN relationships(p) | total + coalesce(f.Distance, 0)) AS total_distance RETURN p, length(p) AS hop_count, total_distance ORDER BY total_distance ASC;
Equivalent query in GQL standard:
MATCH p = ACYCLIC (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,3}(:Airport {airport_id: $targetAirportId}) WITH p, reduce(total = 0, f IN relationships(p) | total + coalesce(f.Distance, 0)) AS total_distance RETURN p, length(p) AS hop_count, total_distance ORDER BY total_distance ASC LIMIT $limit;
Q11. Directed Triangle of Airports
This query detects directed three-airport cycles in the flight graph.
-- Q11. Directed Triangle of Airports MATCH (a:airport)-[ab:flight]->(b:airport)-[bc:flight]->(c:airport)-[:flight]->(a) WHERE ab.OriginAirportID < ab.DestAirportID AND ab.DestAirportID < bc.DestAirportID RETURN ab.Origin AS airport_a, ab.Dest AS airport_b, bc.Dest AS airport_c;
Equivalent query in GQL standard:
MATCH (a:Airport)-[:FLIGHT]->(b:Airport)-[:FLIGHT]->(c:Airport)-[:FLIGHT]->(a) WHERE a.airport_id < b.airport_id AND b.airport_id < c.airport_id RETURN a.code AS airport_a, b.code AS airport_b, c.code AS airport_c LIMIT $limit;
Q12. Four-Airport Cycle
This query detects directed four-airport cycles in the flight graph.
-- Q12. Four-Airport Cycle MATCH p = (a:airport)-[ab:flight]->(b:airport)-[bc:flight]->(c:airport)-[cd:flight]->(d:airport)-[:flight]->(a) WHERE elementId(a) <> elementId(b) AND elementId(a) <> elementId(c) AND elementId(a) <> elementId(d) AND elementId(b) <> elementId(c) AND elementId(b) <> elementId(d) AND elementId(c) <> elementId(d) RETURN ab.Origin AS airport_a, ab.Dest AS airport_b, bc.Dest AS airport_c, cd.Dest AS airport_d;
Equivalent query in GQL standard:
MATCH (a:Airport)-[:FLIGHT]->(b:Airport)-[:FLIGHT]->(c:Airport)-[:FLIGHT]->(d:Airport)-[:FLIGHT]->(a) WHERE a <> b AND a <> c AND a <> d AND b <> c AND b <> d AND c <> d RETURN a.code AS airport_a, b.code AS airport_b, c.code AS airport_c, d.code AS airport_d LIMIT $limit;
Q13. Hub Airports by Distinct Destinations
This query identifies hub airports with many distinct outgoing destination neighbors.
-- Q13. Hub Airports by Distinct Destinations MATCH (center:airport)-[f:flight]->(dest:airport) WHERE f.Year = 2020 WITH center, f.OriginAirportID AS airport_id, f.Origin AS code, f.OriginCityName AS city, count(DISTINCT dest) AS destination_count WHERE destination_count >= 190 RETURN airport_id, code, city, destination_count ORDER BY destination_count DESC;
Equivalent query in GQL standard:
MATCH (center:Airport)-[f:FLIGHT]->(dest:Airport) WHERE f.Year = $year WITH center, count(DISTINCT dest) AS destination_count WHERE destination_count >= $minDegree RETURN center.airport_id AS airport_id, center.code AS code, center.city AS city, destination_count ORDER BY destination_count DESC LIMIT $limit;
Q14. Four-Airport Clique
This query finds sets of four airports where every pair has at least one flight relationship in either direction.
-- Q14. Four-Airport Clique MATCH (a:airport)-[:flight]-(b:airport), (a)-[:flight]-(c:airport), (a)-[:flight]-(d:airport), (b)-[:flight]-(c), (b)-[:flight]-(d), (c)-[:flight]-(d) WHERE elementId(a) < elementId(b) AND elementId(b) < elementId(c) AND elementId(c) < elementId(d) RETURN elementId(a) AS airport_a, elementId(b) AS airport_b, elementId(c) AS airport_c, elementId(d) AS airport_d;
Equivalent query in GQL standard:
MATCH (a:Airport)-[:FLIGHT]-(b:Airport), (a)-[:FLIGHT]-(c:Airport), (a)-[:FLIGHT]-(d:Airport), (b)-[:FLIGHT]-(c), (b)-[:FLIGHT]-(d), (c)-[:FLIGHT]-(d) WHERE a.airport_id < b.airport_id AND b.airport_id < c.airport_id AND c.airport_id < d.airport_id RETURN a.code AS airport_a, b.code AS airport_b, c.code AS airport_c, d.code AS airport_d LIMIT $limit;
Q15. Two-Hop Airport Connections
This query finds two-leg connections through an intermediate airport for a selected source airport.
-- Q15. Two-Hop Airport Connections MATCH (source:airport)-[first:flight]->(:airport)-[second:flight]->(dest:airport) WHERE elementId(source) = 'airport[12339]' AND first.Year = 2020 AND second.Year = 2020 AND elementId(source) <> elementId(dest) RETURN first.Dest AS connection_airport, second.Dest AS destination, count(*) AS connection_count ORDER BY connection_count DESC;
Equivalent query in GQL standard:
MATCH (source:Airport {airport_id: $sourceAirportId})-[first:FLIGHT]->(middle:Airport)-[second:FLIGHT]->(dest:Airport) WHERE first.Year = $year AND second.Year = $year AND source <> dest RETURN middle.code AS connection_airport, dest.code AS destination, count(*) AS connection_count ORDER BY connection_count DESC LIMIT $limit;
Q16. Three-Hop Line Pattern
This query matches a four-airport line connected by three consecutive FLIGHT relationships.
-- Q16. Three-Hop Line Pattern MATCH p = (a:airport)-[ab:flight]->(b:airport)-[bc:flight]->(c:airport)-[cd:flight]->(d:airport) WHERE elementId(a) <> elementId(b) AND elementId(a) <> elementId(c) AND elementId(a) <> elementId(d) AND elementId(b) <> elementId(c) AND elementId(b) <> elementId(d) AND elementId(c) <> elementId(d) RETURN ab.Origin AS airport_a, ab.Dest AS airport_b, bc.Dest AS airport_c, cd.Dest AS airport_d, p;
Equivalent query in GQL standard:
MATCH p = (a:Airport)-[:FLIGHT]->(b:Airport)-[:FLIGHT]->(c:Airport)-[:FLIGHT]->(d:Airport) WHERE a <> b AND a <> c AND a <> d AND b <> c AND b <> d AND c <> d RETURN a.code AS airport_a, b.code AS airport_b, c.code AS airport_c, d.code AS airport_d, p LIMIT $limit;
Q17. Airport Degree Summary
This query computes global statistics over each airport's distinct outgoing destination count.
-- Q17. Airport Degree Summary MATCH (airport:airport) OPTIONAL MATCH (airport)-[:flight]->(dest:airport) WITH airport, count(DISTINCT dest) AS out_degree RETURN count(airport) AS airport_count, avg(out_degree) AS avg_out_degree, min(out_degree) AS min_out_degree, max(out_degree) AS max_out_degree;
Equivalent query in GQL standard:
MATCH (airport:Airport) OPTIONAL MATCH (airport)-[:FLIGHT]->(dest:Airport) WITH airport, count(DISTINCT dest) AS out_degree RETURN count(airport) AS airport_count, avg(out_degree) AS avg_out_degree, min(out_degree) AS min_out_degree, max(out_degree) AS max_out_degree;
Q18. State-Level Degree Summary
This query groups airport degree statistics by the airport state loaded on Airport nodes.
-- Q18. State-Level Degree Summary MATCH (airport:airport) OPTIONAL MATCH (airport)-[f:flight]->(dest:airport) WITH airport, f.OriginState AS state, count(DISTINCT dest) AS out_degree RETURN state, count(airport) AS airport_count, avg(out_degree) AS avg_out_degree, min(out_degree) AS min_out_degree, max(out_degree) AS max_out_degree ORDER BY avg_out_degree DESC;
Equivalent query in GQL standard:
MATCH (airport:Airport) OPTIONAL MATCH (airport)-[:FLIGHT]->(dest:Airport) WITH airport, count(DISTINCT dest) AS out_degree RETURN airport.state AS state, count(airport) AS airport_count, avg(out_degree) AS avg_out_degree, min(out_degree) AS min_out_degree, max(out_degree) AS max_out_degree ORDER BY avg_out_degree DESC LIMIT $limit;
Q19. Flight Edge Distance Summary
This query summarizes numeric properties over all matching FLIGHT relationships.
-- Q19. Flight Edge Distance Summary MATCH (:airport)-[f:flight]->(:airport) WHERE f.Year = 2020 RETURN count(f) AS flight_count, avg(f.Distance) AS avg_distance, min(f.Distance) AS min_distance, max(f.Distance) AS max_distance, avg(f.ArrDelayMinutes) AS avg_arrival_delay;
Equivalent query in GQL standard:
MATCH (:Airport)-[f:FLIGHT]->(:Airport) WHERE f.Year = $year RETURN count(f) AS flight_count, avg(f.Distance) AS avg_distance, min(f.Distance) AS min_distance, max(f.Distance) AS max_distance, avg(f.ArrDelayMinutes) AS avg_arrival_delay;
Q20. State Pair Flight Statistics
This query groups FLIGHT relationship statistics by origin and destination state.
-- Q20. State Pair Flight Statistics MATCH (:airport)-[f:flight]->(:airport) WHERE f.Year = 2020 RETURN f.OriginState AS origin_state, f.DestState AS dest_state, count(f) AS flight_count, avg(f.Distance) AS avg_distance, avg(f.ArrDelayMinutes) AS avg_arrival_delay ORDER BY flight_count DESC;
Equivalent query in GQL standard:
MATCH (origin:Airport)-[f:FLIGHT]->(dest:Airport) WHERE f.Year = $year RETURN origin.state AS origin_state, dest.state AS dest_state, count(f) AS flight_count, avg(f.Distance) AS avg_distance, avg(f.ArrDelayMinutes) AS avg_arrival_delay ORDER BY flight_count DESC LIMIT $limit;
Q21. Route Counts by Path Length
This query counts bounded paths from a selected airport grouped by path length.
-- Q21. Route Counts by Path Length MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH p = (source)-[:flight*1..3]->(target:airport) RETURN length(p) AS path_length, count(p) AS path_count, count(DISTINCT target) AS distinct_target_count ORDER BY path_length;
Equivalent query in GQL standard:
MATCH p = (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,3}(target:Airport) RETURN length(p) AS path_length, count(p) AS path_count, count(DISTINCT target) AS distinct_target_count ORDER BY path_length;
Q22. Route Counts by Length and Target State
This query extends path-length counts by also grouping endpoints by destination airport state.
-- Q22. Route Counts by Length and Target State MATCH (source:airport) WHERE elementId(source) = 'airport[12339]' MATCH p = (source)-[flights:flight*1..3]->(target:airport) RETURN length(p) AS path_length, last(flights).DestState AS target_state, count(p) AS path_count, count(DISTINCT target) AS distinct_target_count ORDER BY path_length, path_count DESC;
Equivalent query in GQL standard:
MATCH p = (:Airport {airport_id: $sourceAirportId})-[:FLIGHT]->{1,3}(target:Airport) RETURN length(p) AS path_length, target.state AS target_state, count(p) AS path_count, count(DISTINCT target) AS distinct_target_count ORDER BY path_length, path_count DESC LIMIT $limit;
Q23. Airports Serving Two Destination States
This query finds origin airports that serve both of two selected destination states.
-- Q23. Airports Serving Two Destination States MATCH (origin:airport)-[to_a:flight]->(:airport) MATCH (origin:airport)-[to_b:flight]->(:airport) WHERE to_a.DestState = 'IN' AND to_b.DestState = 'IL' RETURN DISTINCT to_a.OriginAirportID AS airport_id, to_a.Origin AS code, to_a.OriginCityName AS city, to_a.OriginState AS state ORDER BY code;
Equivalent query in GQL standard:
MATCH (origin:Airport)-[:FLIGHT]->(destA:Airport {state: $stateA}) MATCH (origin)-[:FLIGHT]->(destB:Airport {state: $stateB}) RETURN DISTINCT origin.airport_id AS airport_id, origin.code AS code, origin.city AS city, origin.state AS state ORDER BY code LIMIT $limit;
Q24. Top Airports by Degree Centrality
This query ranks airports by the number of distinct airports they connect to in either direction.
-- Q24. Top Airports by Degree Centrality MATCH (airport:airport)-[:flight]-(neighbor:airport) WITH airport, count(DISTINCT neighbor) AS degree MATCH (airport)-[meta:flight]->(:airport) RETURN DISTINCT meta.OriginAirportID AS airport_id, meta.Origin AS code, meta.OriginCityName AS city, degree ORDER BY degree DESC;
Q25. Airport PageRank
This query runs PageRank on the airport flight graph using Neo4j Graph Data Science.
-- Q25. Airport PageRank CALL algo.paral.pagerank({ labels: ['airport'], relationshipTypes: ['flight'] }) YIELD id, score RETURN id AS airport_id, score ORDER BY score DESC;
Q26. Weakly Connected Airport Components
This query finds weakly connected components after treating flight direction as undirected.
-- Q26. Weakly Connected Airport Components CALL algo.wcc({ labels: ['airport'], relationshipTypes: ['flight'] }) YIELD id, componentId RETURN componentId, count(*) AS component_size ORDER BY component_size DESC;
Q27. Same-day Connections
This query finds same-day two-leg flight connections from LAX to LGA through an intermediate airport, keeps only connections with at least a scheduled one-hour layover, and returns the monthly count for the selected year.
-- Q27. Same-day Connections MATCH p = (a:airport)-[r1:flight]->(:airport)-[r2:flight]->(c:airport) WHERE r1.FlightDate = r2.FlightDate AND r1.CRSArrTime + 100 <= r2.CRSDepTime AND elementId(a) = 'airport[12892]' AND elementId(c) = 'airport[12953]' AND r1.Year = 2020 WITH r1.Month AS month, count(p) AS path_count RETURN month, path_count ORDER BY month;
Equivalent query in GQL standard:
MATCH p = (a:Airport)-[r1:FLIGHT]->(b:Airport)-[r2:FLIGHT]->(c:Airport) WHERE r1.FlightDate = r2.FlightDate AND r1.CRSArrTime + 100 <= r2.CRSDepTime AND a.airport_id = 12892 AND c.airport_id = 12953 AND r1.Year = $year WITH r1.Month AS month, count(p) AS path_count RETURN month, path_count ORDER BY month;
Q28. Missed Connecting Flights
This query finds scheduled two-leg LAX-to-LGA connections through an intermediate airport, filters to cases where the first actual arrival is at or after the second actual departure, and returns the monthly missed-connection count for the selected year.
-- Q28. Missed Connecting Flights MATCH p = (a:airport)-[r1:flight]->(:airport)-[r2:flight]->(c:airport) WHERE r1.FlightDate = r2.FlightDate AND r1.CRSArrTime + 100 <= r2.CRSDepTime AND r1.ArrTime >= r2.DepTime AND elementId(a) = 'airport[12892]' AND elementId(c) = 'airport[12953]' AND r1.Year = 2020 WITH r1.Month AS month, count(p) AS path_count RETURN month, path_count ORDER BY month;
Equivalent query in GQL standard:
MATCH p = (a:Airport)-[r1:FLIGHT]->(b:Airport)-[r2:FLIGHT]->(c:Airport) WHERE r1.FlightDate = r2.FlightDate AND r1.CRSArrTime + 100 <= r2.CRSDepTime AND r1.ArrTime >= r2.DepTime AND a.airport_id = 12892 AND c.airport_id = 12953 AND r1.Year = $year WITH r1.Month AS month, count(p) AS path_count RETURN month, path_count ORDER BY month;
Q29. Top 10 Aircraft Turnarounds
This query finds airports where the same aircraft arrives and then departs on the same date, counts distinct tail numbers that form valid scheduled turnarounds, and returns the top 10 airports for the selected date.
-- Q29. Top 10 Aircraft Turnarounds MATCH (:airport)-[r1:flight]->(n:airport)-[r2:flight]->(:airport) WHERE r1.FlightDate = date('2020-12-25') 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;
Equivalent query in GQL standard:
MATCH (:Airport)-[r1:FLIGHT]->(n:Airport)-[r2:FLIGHT]->(:Airport) 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;
Q30. Three-Leg Flight Sequences
This query follows three consecutive flight legs on the same date for the same aircraft, enforces scheduled arrival-before-departure ordering between legs, and counts distinct tail numbers that complete the sequence.
-- Q30. Three-Leg Flight Sequences MATCH (:airport)-[r1:flight]->(:airport)-[r2:flight]->(:airport)-[r3:flight]->(:airport) WHERE r1.FlightDate = date('2020-12-25') 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;
Equivalent query in GQL standard:
MATCH (:Airport)-[r1:FLIGHT]->(:Airport)-[r2:FLIGHT]->(:Airport)-[r3:FLIGHT]->(:Airport) 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;