Skip to content

Neo4j — Northwind Benchmark Report ​

Run: 2026-10-09T09:31:27.408652-07:00 → 2026-10-09T09:31:54.389442-07:00Endpoint: bolt://localhost:7687 (database neo4j)

Workload ​

  • Categories: 96 | Suppliers: 144 | Customers: 1,200
  • Products seeded: 48,000
  • Orders seeded: 48,000 (1..6 lines each)
  • Random seed: 42 (deterministic dataset)
  • Seed nodes: 97,440
  • Seed relationships: 312,050
  • Approx. seed payload (JSON-serialized): 35.4 MiB
  • Seed duration: 5,390.93 ms
  • Wipe duration: 100.14 ms
  • Index setup duration: 492.42 ms
  • Ingestion duration (row generation and writes): 4,798.36 ms
  • Ingestion nodes/sec: 20,306.93
  • Ingestion relationships/sec: 65,032.61
  • Seed batch size: 500 rows
  • Seed parallelism: 4 sessions per phase
  • Query workloads: 14
  • Iterations per query: 30
  • Warmup iterations per query: 5

Query Latency ​

QueryDescriptionSamplesMean (ms)Median (ms)P95 (ms)P99 (ms)Min (ms)Max (ms)StdDev (ms)Ops/sec
products_per_categoryProduct counts grouped by category, with a full result sort.306.336.257.358.655.629.100.68157.49
customer_category_distinct_ordersFour-hop customer-to-category traversal with distinct-order aggregation.30172.48171.01182.50192.73165.32196.697.125.80
optional_match_orders_countOptional product-to-order traversal with zero-match preservation and top-100 sorting.3053.0552.9054.8855.7551.6656.090.9918.84
revenue_by_productRelationship-property arithmetic and revenue aggregation grouped by product.3067.6567.1070.4771.2865.3671.491.5114.78
products_by_supplierSupplier-to-product traversal with top-N aggregation and deterministic ties.306.876.528.759.825.879.991.03145.33
orders_by_customerCustomer-to-order traversal grouped into a top-25 order-count ranking.306.746.666.898.396.468.990.44148.13
revenue_by_categoryThree-hop category revenue aggregation from order-line quantities and product prices.3063.1862.3668.4669.2460.7469.482.3715.82
revenue_by_supplierSupplier-to-product-to-order traversal with revenue aggregation and top-25 sorting.3060.9260.6862.6963.4259.3063.680.9616.41
revenue_by_customerCustomer-order-product traversal with relationship-property revenue aggregation.3069.9769.6072.5973.8268.2174.291.4814.29
order_line_sales_by_countryOrder-line scan grouped by shipping country with line-count and unit aggregation.3048.7948.5150.7851.6748.0551.690.8520.49
low_stock_productsSelective numeric property filter followed by a stable top-100 product sort.307.577.477.798.977.329.450.37131.51
products_in_categorySelective category lookup and adjacent product traversal with a top-100 result.300.950.841.072.580.793.190.431,015.91
order_line_quantity_distributionFull relationship-property scan grouped by line quantity.3022.5822.0525.9826.3621.5526.401.2944.26
customer_order_detailsSelective customer lookup followed by order-line expansion and computed row projection.300.950.831.222.850.673.500.511,018.17
  • Overall mean latency: 42.00 ms
  • Measured query operations: 420
  • End-to-end query-loop throughput: 19.57 ops/sec
  • Query-latency-only aggregate throughput: 23.81 ops/sec
  • Query-loop duration: 21.458 s
  • Query-loop duration includes warmups and per-query setup; only measured iterations count toward the end-to-end rate.
  • Full lifecycle wall-clock (sampled): 44.367 s

Correctness ​

Seed counts (from the database's own count(...) queries):

EntityCount
Category96
Supplier144
Customer1,200
Product48,000
Order48,000
PART_OF edges48,000
SUPPLIES edges48,000
PURCHASED edges48,000
ORDERS edges168,050

Per-query result fingerprints (SHA-256 over canonicalised rows):

QueryRowsHashStable across iterations
products_per_category9691e9f1f063680a6d…Yes
customer_category_distinct_orders105da36214d5163220…Yes
optional_match_orders_count1008950fcdaab16eaeb…Yes
revenue_by_product1060b64c678f4c01fd…Yes
products_by_supplier25af1e9b5d1d663a02…Yes
orders_by_customer25ecff10cfcfa9cc34…Yes
revenue_by_category9623ba39858bf74ace…Yes
revenue_by_supplier2541900ee05a8f994a…Yes
revenue_by_customer25639a286559282c97…Yes
order_line_sales_by_country15a86030b2ba5eede9…Yes
low_stock_products100a8f2f994d920e6d8…Yes
products_in_category100e207262bf51ed857…Yes
order_line_quantity_distribution25e239e5f47878c862…Yes
customer_order_details1009d6ef178ee445057…Yes

No intra-run correctness errors.

Power Consumption ​

  • Samples collected: 43 (~1s each)
  • Sampled duration: 43.98 s
  • Avg CPU power: 5,859.9 mW
  • Avg GPU power: 1.2 mW
  • Avg package power: 5,861.1 mW
  • Estimated energy (benchmark window): 257.77 J

Memory Pressure ​

  • Samples collected: 47 (~1s each)
  • Avg used (active + wired + compressor): 23.3 GiB
  • Peak used: 23.5 GiB
  • Avg free: 7.2 GiB
  • Min free: 6.6 GiB
  • Avg compressed (logical): 7.2 GiB
  • Peak compressed: 7.2 GiB

Storage ​

  • Raw data files: 50.7 MiB (53,207,040 bytes)
  • Indexes/stats: 7.5 MiB (7,913,472 bytes)
  • Write-ahead logs: 144.4 MiB (151,400,448 bytes)
  • Metadata/bookkeeping: 1.1 MiB (1,191,936 bytes)
  • Preallocated scratch (excluded): 4.0 KiB (4,096 bytes)
  • Unclassified (other): 0 B (0 bytes)
  • Full data directory du: 203.8 MiB (213,716,992 bytes)
  • Classified sum: 203.8 MiB (213,716,992 bytes, Δ vs du = +0 bytes)

Raw-data size is the comparison headline. Preallocated memtable/WAL scratch files (8 MiB memtable on Badger, 1 MiB GC discard log, etc.) are excluded because they hold the same bytes regardless of dataset size.

Top raw-data files
FileSize
databases/neo4j/neostore.propertystore.db18.1 MiB
databases/neo4j/neostore.propertystore.db.strings14.9 MiB
databases/neo4j/neostore.relationshipstore.db10.2 MiB
databases/neo4j/neostore.propertystore.db.arrays5.9 MiB
databases/neo4j/neostore.nodestore.db1.4 MiB
databases/neo4j/neostore.relationshipgroupstore.degrees.db48.0 KiB
databases/system/neostore.relationshipgroupstore.degrees.db40.0 KiB
databases/neo4j/neostore8.0 KiB
databases/neo4j/neostore.labeltokenstore.db.names8.0 KiB
databases/neo4j/neostore.relationshiptypestore.db.names8.0 KiB

Queries ​

products_per_category ​

cypher
MATCH (c:Category)<-[:PART_OF]-(p:Product)
			RETURN c.categoryName AS categoryName, count(p) AS productCount
			ORDER BY productCount DESC

customer_category_distinct_orders ​

cypher
MATCH (c:Customer)-[:PURCHASED]->(o:Order)-[:ORDERS]->(p:Product)-[:PART_OF]->(cat:Category)
			RETURN c.companyName AS companyName, cat.categoryName AS categoryName, count(DISTINCT o) AS orders
			ORDER BY orders DESC, companyName ASC, categoryName ASC
			LIMIT 10

optional_match_orders_count ​

cypher
MATCH (p:Product)
			OPTIONAL MATCH (p)<-[r:ORDERS]-(o:Order)
			RETURN p.productName AS productName, count(o) AS orderCount
			ORDER BY orderCount DESC, productName ASC
			LIMIT 100

revenue_by_product ​

cypher
MATCH (p:Product)<-[r:ORDERS]-(:Order)
			WITH p, sum(p.unitPrice * r.quantity) AS revenue
			RETURN p.productName AS productName, revenue
			ORDER BY revenue DESC, productName ASC
			LIMIT 10

products_by_supplier ​

cypher
MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
			RETURN s.companyName AS supplier, count(p) AS products
			ORDER BY products DESC, supplier ASC
			LIMIT 25

orders_by_customer ​

cypher
MATCH (c:Customer)-[:PURCHASED]->(o:Order)
			RETURN c.companyName AS customer, count(o) AS orders
			ORDER BY orders DESC, customer ASC
			LIMIT 25

revenue_by_category ​

cypher
MATCH (c:Category)<-[:PART_OF]-(p:Product)<-[r:ORDERS]-(:Order)
			RETURN c.categoryName AS category, sum(p.unitPrice * r.quantity) AS revenue
			ORDER BY revenue DESC, category ASC

revenue_by_supplier ​

cypher
MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)<-[r:ORDERS]-(:Order)
			RETURN s.companyName AS supplier, sum(p.unitPrice * r.quantity) AS revenue
			ORDER BY revenue DESC, supplier ASC
			LIMIT 25

revenue_by_customer ​

cypher
MATCH (c:Customer)-[:PURCHASED]->(:Order)-[r:ORDERS]->(p:Product)
			RETURN c.companyName AS customer, sum(p.unitPrice * r.quantity) AS revenue
			ORDER BY revenue DESC, customer ASC
			LIMIT 25

order_line_sales_by_country ​

cypher
MATCH (o:Order)-[r:ORDERS]->(:Product)
			RETURN o.shipCountry AS country, count(r) AS orderLines, sum(r.quantity) AS units
			ORDER BY orderLines DESC, country ASC

low_stock_products ​

cypher
MATCH (p:Product)
			WHERE p.unitsInStock < 25
			RETURN p.productName AS product, p.unitsInStock AS unitsInStock, p.unitPrice AS unitPrice
			ORDER BY unitsInStock ASC, product ASC
			LIMIT 100

products_in_category ​

cypher
MATCH (c:Category {categoryID: 7})<-[:PART_OF]-(p:Product)
			RETURN p.productName AS product, p.unitPrice AS unitPrice, p.unitsInStock AS unitsInStock
			ORDER BY product ASC
			LIMIT 100

order_line_quantity_distribution ​

cypher
MATCH ()-[r:ORDERS]->()
			RETURN r.quantity AS quantity, count(r) AS lineCount
			ORDER BY quantity ASC

customer_order_details ​

cypher
MATCH (c:Customer {customerID: 42})-[:PURCHASED]->(o:Order)-[r:ORDERS]->(p:Product)
			RETURN o.orderID AS orderID, p.productName AS product, r.quantity AS quantity,
			       p.unitPrice * r.quantity AS extendedPrice
			ORDER BY orderID ASC, product ASC
			LIMIT 100
Analytics privacy