Skip to content

NornicDB (ANTLR parser) — Northwind Benchmark Report ​

Run: 2026-10-09T09:30:52.442158-07:00 → 2026-10-09T09:31:17.72512-07:00Endpoint: bolt://localhost:17687 (database nornic)

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,354.84 ms
  • Wipe duration: 22.65 ms
  • Index setup duration: 10.05 ms
  • Ingestion duration (row generation and writes): 5,322.12 ms
  • Ingestion nodes/sec: 18,308.49
  • Ingestion relationships/sec: 58,632.64
  • 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.300.180.180.210.230.140.230.025,020.33
customer_category_distinct_ordersFour-hop customer-to-category traversal with distinct-order aggregation.300.180.170.220.290.150.320.035,488.64
optional_match_orders_countOptional product-to-order traversal with zero-match preservation and top-100 sorting.300.190.180.230.250.160.260.034,807.92
revenue_by_productRelationship-property arithmetic and revenue aggregation grouped by product.300.160.150.180.200.130.200.026,224.71
products_by_supplierSupplier-to-product traversal with top-N aggregation and deterministic ties.300.140.140.160.170.130.180.016,700.67
orders_by_customerCustomer-to-order traversal grouped into a top-25 order-count ranking.300.130.130.150.150.120.160.017,268.54
revenue_by_categoryThree-hop category revenue aggregation from order-line quantities and product prices.300.200.200.240.250.180.250.024,367.55
revenue_by_supplierSupplier-to-product-to-order traversal with revenue aggregation and top-25 sorting.300.160.160.190.200.140.210.025,985.98
revenue_by_customerCustomer-order-product traversal with relationship-property revenue aggregation.300.160.150.180.180.140.180.016,132.30
order_line_sales_by_countryOrder-line scan grouped by shipping country with line-count and unit aggregation.300.140.140.170.180.120.190.026,835.79
low_stock_productsSelective numeric property filter followed by a stable top-100 product sort.300.200.190.220.240.180.240.014,488.30
products_in_categorySelective category lookup and adjacent product traversal with a top-100 result.300.190.190.200.210.170.210.014,635.35
order_line_quantity_distributionFull relationship-property scan grouped by line quantity.300.140.130.170.170.110.170.027,048.87
customer_order_detailsSelective customer lookup followed by order-line expansion and computed row projection.300.210.210.220.230.190.230.014,188.51
  • Overall mean latency: 0.17 ms
  • Measured query operations: 420
  • End-to-end query-loop throughput: 21.08 ops/sec
  • Query-latency-only aggregate throughput: 5,908.17 ops/sec
  • Query-loop duration: 19.923 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): 27.372 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: 26 (~1s each)
  • Sampled duration: 26.48 s
  • Avg CPU power: 9,073.6 mW
  • Avg GPU power: 172.5 mW
  • Avg package power: 9,246.1 mW
  • Estimated energy (benchmark window): 244.88 J

Memory Pressure ​

  • Samples collected: 30 (~1s each)
  • Avg used (active + wired + compressor): 23.4 GiB
  • Peak used: 23.9 GiB
  • Avg free: 7.3 GiB
  • Min free: 6.8 GiB
  • Avg compressed (logical): 7.2 GiB
  • Peak compressed: 7.2 GiB

Storage ​

  • Raw data files: 148.2 MiB (155,422,720 bytes)
  • Indexes/stats: 0 B (0 bytes)
  • Write-ahead logs: 260.0 KiB (266,240 bytes)
  • Metadata/bookkeeping: 8.0 KiB (8,192 bytes)
  • Preallocated scratch (excluded): 1.0 MiB (1,048,576 bytes)
  • Unclassified (other): 0 B (0 bytes)
  • Full data directory du: 149.5 MiB (156,745,728 bytes)
  • Classified sum: 149.5 MiB (156,745,728 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
000002.sst49.9 MiB
000003.sst49.4 MiB
000004.sst48.9 MiB
000001.sst4.0 KiB
000001.vlog4.0 KiB
000002.vlog4.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