Supply chain blast radius of a vendor failure
Objective
When a vendor like Acme Steel halts shipments, ops teams need to know — within minutes — every downstream product, finished good, and customer at risk. Modeling the supply chain as a graph turns this from an operations nightmare into a single MATCH that walks the full dependency tree.
Step 1: Create the graph
// Vendors (raw material suppliers)
MERGE (acme:Vendor {name: "Acme Steel", country: "US"})
MERGE (helio:Vendor {name: "Helios Plastics", country: "MX"})
MERGE (lumen:Vendor {name: "Lumen Electronics", country: "TW"})
// Materials supplied
MERGE (steel:Material {name: "Cold-Rolled Steel"})
MERGE (alum:Material {name: "Aluminum Sheet"})
MERGE (poly:Material {name: "ABS Polymer"})
MERGE (chip:Material {name: "Microcontroller"})
MERGE (acme)-[:SUPPLIES]->(steel)
MERGE (acme)-[:SUPPLIES]->(alum)
MERGE (helio)-[:SUPPLIES]->(poly)
MERGE (lumen)-[:SUPPLIES]->(chip)
// Components made from materials
MERGE (frame:Component {name: "Chassis Frame"})
MERGE (housing:Component {name: "Outer Housing"})
MERGE (board:Component {name: "Control Board"})
MERGE (panel:Component {name: "Display Panel"})
MERGE (frame)-[:USES]->(steel)
MERGE (frame)-[:USES]->(alum)
MERGE (housing)-[:USES]->(poly)
MERGE (board)-[:USES]->(chip)
MERGE (panel)-[:USES]->(chip)
MERGE (panel)-[:USES]->(poly)
// Finished products
MERGE (washer:Product {sku: "WSH-200", name: "EcoWash Washer"})
MERGE (fridge:Product {sku: "FRG-300", name: "ColdSaver Fridge"})
MERGE (oven:Product {sku: "OVN-100", name: "BreezeBake Oven"})
MERGE (washer)-[:CONTAINS]->(frame)
MERGE (washer)-[:CONTAINS]->(board)
MERGE (fridge)-[:CONTAINS]->(frame)
MERGE (fridge)-[:CONTAINS]->(panel)
MERGE (oven)-[:CONTAINS]->(housing)
MERGE (oven)-[:CONTAINS]->(board)
// Customers and orders
MERGE (c1:Customer {name: "BigBox Retail"})
MERGE (c2:Customer {name: "HomeStyle Co"})
MERGE (c3:Customer {name: "MetroAppliance"})
MERGE (c1)-[:ORDERED {qty: 2400}]->(washer)
MERGE (c1)-[:ORDERED {qty: 1800}]->(fridge)
MERGE (c2)-[:ORDERED {qty: 950}]->(oven)
MERGE (c3)-[:ORDERED {qty: 600}]->(fridge);
Step 2: Acme Steel goes offline — what is at risk?
// Walk Vendor -> Material -> Component -> Product -> Customer.
MATCH (vendor:Vendor {name: "Acme Steel"})-[:SUPPLIES]->(:Material)
<-[:USES]-(:Component)<-[:CONTAINS]-(prod:Product)<-[:ORDERED]-(cust:Customer)
RETURN cust.name AS customer_at_risk,
prod.sku AS product,
prod.name AS product_name,
count(*) AS dependency_paths
ORDER BY customer_at_risk, product_name;
What's happening
- A 4-hop pattern walks through every layer of the bill-of-materials in one expression.
count(*)shows how many independent material paths each (customer, product) pair depends on — a product with 3 paths through Acme is more exposed than one with 1.- Real BOMs in SQL are fact tables glued together with surrogate keys; this query would be a 5-way join with multiple GROUP BYs. The graph is the dependency tree.
- Adding an
:ALTERNATE_FORrelationship between materials would let you re-run the same query to find substitutions — graphs make "what-if" analysis a property mutation, not a schema change. - Combine this with a watchlist of fragile vendors to power a real-time supply-risk dashboard.
Try this next
MATCH (v:Vendor {name: "Acme Steel"})-[:SUPPLIES]->(m)<-[:USES]-(c)<-[:CONTAINS]-(p:Product)
RETURN p.sku, p.name, collect(DISTINCT m.name) AS dependent_materials;
MATCH (v:Vendor)-[:SUPPLIES]->(m:Material)<-[:USES]-(:Component)<-[:CONTAINS]-(p:Product)
RETURN v.name AS vendor, count(DISTINCT p) AS products_affected
ORDER BY products_affected DESC;
MATCH (cust:Customer)-[o:ORDERED]->(p:Product)-[:CONTAINS]->(c:Component)-[:USES]->(m:Material)<-[:SUPPLIES]-(:Vendor {name: "Acme Steel"})
RETURN cust.name, sum(o.qty) AS units_at_risk;