Skip to main content

vs SurrealDB

LoraDB vs SurrealDB

LoraDB is 48.6× faster overall. SurrealDB is an multi-model database server. Numbers from the same suite as the overview — identical fixtures, identical seed, identical iteration count.

65 comparable workloads16 omissions11 groups
48.6×slower
SurrealDB vs LoraDB overall
Geometric-mean slowdown across the full suite.
42/ 65
Workloads won by LoraDB
SurrealDB wins 1; 22 go to a third engine.
1close calls
Within ~10% on both sides
Workloads where neither engine ran away with the row.

Overall

LoraDB sets the row; SurrealDB sits at 48.6× slower.

Geometric-mean slowdown across every workload both engines run. Empty rows happen when the language can’t express the workload — they’re called out below, not hidden.

Per group

Wins by group.

For each group, the workload count, who wins it, and how the per-row tally breaks down between LoraDB, SurrealDB, and any third engine that took a row.

  • writes6 workloads · winner Grafeo
    LoraDB0.61× slower
    SurrealDB28.3× slower
    Rows · 62·0·4
  • scans5 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB121× slower
    Rows · 54·0·1
  • predicates12 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB21.3× slower
    Rows · 128·1·3
  • strings5 workloads · winner Kuzu
    LoraDB1.22× slower
    SurrealDB34.1× slower
    Rows · 50·0·5
  • numerics5 workloads · winner Kuzu
    LoraDB1.08× slower
    SurrealDB39.9× slower
    Rows · 51·0·4
  • aggregates7 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB44.7× slower
    Rows · 76·0·1
  • pipeline6 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB36.8× slower
    Rows · 63·0·3
  • lists2 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB32.3× slower
    Rows · 21·0·1
  • sort2 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB31.8× slower
    Rows · 22·0
  • traversals11 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB110× slower
    Rows · 1111·0
  • patterns4 workloads · winner LoraDB
    LoraDBfastest
    SurrealDB190× slower
    Rows · 44·0

Per workload

Raw timings, workload by workload.

Two columns — LoraDB and SurrealDB — across every workload they share. Rows with no comparable SurrealDBrun are hidden here; they’re listed in the omissions block at the bottom.

writes(6)

WinnerGrafeo
WorkloadSizeLoraDBSurrealDBWinner
bulk_set_match1000552.39 µs1.84× slower6.15 ms20.5× slowerKuzu
delete_node1000355.70 µs1.47× slower5.56 ms23.0× slowerGrafeo
set_multiple_props100021.15 µsfastest5.54 ms262× slowerLoraDB
update_set100018.01 µsfastest6.04 ms335× slowerLoraDB
write_bulk10001.46 ms1.93× slower26.66 ms35.3× slowerGrafeo
write_single100014.90 µs2.20× slower252.56 µs37.3× slowerGrafeo

scans(5)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
lookup_by_id1000716.64 nsfastest5.26 ms7340× slowerLoraDB
lookup_by_id_indexed1000684.00 nsfastest32.35 µs47.3× slowerLoraDB
range_filter1000199.70 µs1.04× slower9.29 ms48.2× slowerKuzu
scan_filtered1000149.08 µsfastest5.88 ms39.5× slowerLoraDB
scan_label1000124.96 µsfastest5.01 ms40.1× slowerLoraDB

predicates(12)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
where_compound_and_or1000213.07 µsfastest10.23 ms48.0× slowerLoraDB
where_contains1000145.04 µsfastest4.96 ms34.2× slowerLoraDB
where_ends_with1000143.60 µsfastest5.06 ms35.2× slowerLoraDB
where_id_in_range1000142.71 µs2.02× slower7.54 ms107× slowerGrafeo
where_in_list1000162.42 µsfastest5.59 ms34.4× slowerLoraDB
where_modulo_eq1000127.32 µsfastest128.48 µs1.01× slowerLoraDB
where_not1000166.05 µs1.01× slower8.27 ms50.4× slowerKuzu
where_or1000147.16 µsfastest8.43 ms57.3× slowerLoraDB
where_starts_with1000146.91 µsfastest5.12 ms34.9× slowerLoraDB
where_string_gte1000180.97 µs1.07× slower6.05 ms35.9× slowerKuzu
where_subexpr1000227.59 µs1.69× slower134.71 µsfastestSurrealDB
where_two_props1000152.69 µsfastest6.46 ms42.3× slowerLoraDB

strings(5)

WinnerKuzu
WorkloadSizeLoraDBSurrealDBWinner
string_concat1000186.55 µs1.27× slower5.86 ms39.8× slowerKuzu
string_size1000172.17 µs1.10× slower5.23 ms33.5× slowerKuzu
string_substring1000210.20 µs1.21× slower5.58 ms32.1× slowerKuzu
string_to_lower1000201.00 µs1.33× slower5.03 ms33.3× slowerKuzu
string_to_upper1000185.41 µs1.22× slower4.91 ms32.3× slowerKuzu

numerics(5)

WinnerKuzu
WorkloadSizeLoraDBSurrealDBWinner
numeric_abs1000174.85 µs1.13× slower5.86 ms37.8× slowerKuzu
numeric_ceil1000171.69 µs1.10× slower5.81 ms37.4× slowerKuzu
numeric_floor1000175.48 µs1.10× slower5.99 ms37.5× slowerKuzu
numeric_pow1000166.22 µs1.16× slower8.42 ms58.8× slowerKuzu
numeric_round1000180.68 µsfastest5.87 ms32.5× slowerLoraDB

aggregates(7)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
aggregate_avg100081.43 µsfastest6.22 ms76.4× slowerLoraDB
aggregate_collect100078.90 µsfastest6.79 ms86.0× slowerLoraDB
aggregate_count100059.50 µs2.77× slower209.05 µs9.75× slowerGrafeo
aggregate_max100079.97 µsfastest6.08 ms76.0× slowerLoraDB
aggregate_min100079.67 µsfastest5.95 ms74.7× slowerLoraDB
aggregate_sum100078.09 µsfastest6.23 ms79.8× slowerLoraDB
top_k1000186.50 µsfastest6.40 ms34.3× slowerLoraDB

pipeline(6)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
case_when1000173.33 µsfastest7.38 ms42.6× slowerLoraDB
coalesce_existing1000161.92 µsfastest7.51 ms46.4× slowerLoraDB
computed_in_return1000151.15 µs1.07× slower7.35 ms52.1× slowerKuzu
predicate_via_function1000238.39 µs1.38× slower7.45 ms43.2× slowerKuzu
with_pipeline1000186.41 µsfastest5.98 ms32.1× slowerLoraDB
with_two_chained1000313.64 µs1.41× slower8.12 ms36.5× slowerKuzu

lists(2)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
list_in_construction1000177.94 µs1.05× slower6.14 ms36.4× slowerKuzu
list_unwind_explicit10001.11 µsfastest33.52 µs30.2× slowerLoraDB

sort(2)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
order_by_id_asc1000167.95 µsfastest5.24 ms31.2× slowerLoraDB
skip_limit1000162.19 µsfastest5.24 ms32.3× slowerLoraDB

traversals(11)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
direct_record_traversal500831.70 nsfastest62.98 µs75.7× slowerLoraDB
recursive_depth2500968.69 nsfastest110.22 µs114× slowerLoraDB
recursive_depth35001.04 µsfastest147.78 µs142× slowerLoraDB
recursive_depth55001.16 µsfastest225.94 µs195× slowerLoraDB
relation_filter500117.78 µsfastest18.70 ms159× slowerLoraDB
traversal_count_one_hop50059.36 µsfastest106.91 µs1.80× slowerLoraDB
traversal_filter_one_hop500138.99 µsfastest21.51 ms155× slowerLoraDB
traversal_one_hop500125.98 µsfastest25.78 ms205× slowerLoraDB
traversal_reverse500123.67 µsfastest24.71 ms200× slowerLoraDB
traversal_three_hop500236.10 µsfastest60.59 ms257× slowerLoraDB
traversal_two_hop500165.58 µsfastest44.18 ms267× slowerLoraDB

patterns(4)

WinnerLoraDB
WorkloadSizeLoraDBSurrealDBWinner
edge_subquery_clause500213.55 µsfastest18.19 ms85.2× slowerLoraDB
star_fanout1000138.96 µsfastest29.94 ms215× slowerLoraDB
star_fanout_count100061.36 µsfastest20.35 ms332× slowerLoraDB
star_fanout_filter1000112.53 µsfastest24.13 ms214× slowerLoraDB

Honest omissions

Workloads SurrealDB couldn’t run like-for-like.

Omissions

Workloads omitted only where the engine has no like-for-like equivalent — a missing scalar function, a different MERGE semantics, a reserved word. The reason is recorded with the workload, not silently dropped.

  • merge_existing
    SurrealDB
    • SurrealDB's UPSERT semantics diverge from Cypher MERGE on which fields are matched vs set.
  • merge_create
    SurrealDB
    • See merge_existing — UPSERT semantics differ.
  • bulk_edges
    SurrealDB
    • UNWIND-driven bulk RELATE requires scripted FOR loops; not a like-for-like comparison.
  • distinct
    SurrealDB
    • `value` is a reserved word in SurrealQL's SELECT VALUE clause; no clean equivalent.
  • numeric_modulo
    SurrealDB
    • SurrealQL parser rejects bare `%` inside SELECT projections (same parse limit as grouped_aggregation).
  • aggregate_count_distinct
    SurrealDB
    • count(DISTINCT) has no direct SurrealQL aggregate; requires nested SELECT + array::distinct.
  • grouped_aggregation
    SurrealDB
    • SurrealQL rejects `%` inside a SELECT projection that's then used as a GROUP BY key (parse error).
  • with_distinct_then_count
    SurrealDB
    • DISTINCT on `value` needs SELECT VALUE, where `value` is reserved (same parse limit as the `distinct` workload).
  • with_aggregate_then_filter
    SurrealDB
    • Groups on `value % 10`; SurrealQL rejects `%` in a projection used as a GROUP BY key (same parse limit as grouped_aggregation).
  • distinct_with_order
    SurrealDB
    • DISTINCT + ORDER BY on `value`, which is reserved in both SELECT VALUE and ORDER BY (see distinct, order_by_multi_key).
  • range_function
    SurrealDB
    • SurrealQL has no row-generating numeric range (no UNWIND/range equivalent); the explicit-list unwind is covered by list_unwind_explicit.
  • order_by_multi_key
    SurrealDB
    • `value` is reserved in SurrealQL ORDER BY clauses (parse error).
  • traversal_undirected
    SurrealDB
    • SurrealDB graph edges are directional; the undirected `-[:NEXT]-` pattern has no single like-for-like arrow form (forward and reverse are covered by traversal_one_hop and traversal_reverse).
  • variable_length_path
    SurrealDB
    • SurrealQL recursive traversal takes a fixed depth `@{n}` (see recursive_depth2/3/5); a `1..3` range expanded from every start node has no like-for-like form.
  • varlen_2_to_5
    SurrealDB
    • Range-bounded variable-length expansion from every start node; see variable_length_path.
  • varlen_exact_5
    SurrealDB
    • Fixed-depth expansion from every start node; the anchored single-source equivalent is benched as recursive_depth5.