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.
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 GrafeoLoraDB0.61× slowerSurrealDB28.3× slowerRows · 62·0·4
- scans5 workloads · winner LoraDBLoraDBfastestSurrealDB121× slowerRows · 54·0·1
- predicates12 workloads · winner LoraDBLoraDBfastestSurrealDB21.3× slowerRows · 128·1·3
- strings5 workloads · winner KuzuLoraDB1.22× slowerSurrealDB34.1× slowerRows · 50·0·5
- numerics5 workloads · winner KuzuLoraDB1.08× slowerSurrealDB39.9× slowerRows · 51·0·4
- aggregates7 workloads · winner LoraDBLoraDBfastestSurrealDB44.7× slowerRows · 76·0·1
- pipeline6 workloads · winner LoraDBLoraDBfastestSurrealDB36.8× slowerRows · 63·0·3
- lists2 workloads · winner LoraDBLoraDBfastestSurrealDB32.3× slowerRows · 21·0·1
- sort2 workloads · winner LoraDBLoraDBfastestSurrealDB31.8× slowerRows · 22·0
- traversals11 workloads · winner LoraDBLoraDBfastestSurrealDB110× slowerRows · 1111·0
- patterns4 workloads · winner LoraDBLoraDBfastestSurrealDB190× slowerRows · 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| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| bulk_set_match | 1000 | 552.39 µs1.84× slower | 6.15 ms20.5× slower | Kuzu |
| delete_node | 1000 | 355.70 µs1.47× slower | 5.56 ms23.0× slower | Grafeo |
| set_multiple_props | 1000 | 21.15 µsfastest | 5.54 ms262× slower | LoraDB |
| update_set | 1000 | 18.01 µsfastest | 6.04 ms335× slower | LoraDB |
| write_bulk | 1000 | 1.46 ms1.93× slower | 26.66 ms35.3× slower | Grafeo |
| write_single | 1000 | 14.90 µs2.20× slower | 252.56 µs37.3× slower | Grafeo |
scans(5)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| lookup_by_id | 1000 | 716.64 nsfastest | 5.26 ms7340× slower | LoraDB |
| lookup_by_id_indexed | 1000 | 684.00 nsfastest | 32.35 µs47.3× slower | LoraDB |
| range_filter | 1000 | 199.70 µs1.04× slower | 9.29 ms48.2× slower | Kuzu |
| scan_filtered | 1000 | 149.08 µsfastest | 5.88 ms39.5× slower | LoraDB |
| scan_label | 1000 | 124.96 µsfastest | 5.01 ms40.1× slower | LoraDB |
predicates(12)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| where_compound_and_or | 1000 | 213.07 µsfastest | 10.23 ms48.0× slower | LoraDB |
| where_contains | 1000 | 145.04 µsfastest | 4.96 ms34.2× slower | LoraDB |
| where_ends_with | 1000 | 143.60 µsfastest | 5.06 ms35.2× slower | LoraDB |
| where_id_in_range | 1000 | 142.71 µs2.02× slower | 7.54 ms107× slower | Grafeo |
| where_in_list | 1000 | 162.42 µsfastest | 5.59 ms34.4× slower | LoraDB |
| where_modulo_eq | 1000 | 127.32 µsfastest | 128.48 µs1.01× slower | LoraDB |
| where_not | 1000 | 166.05 µs1.01× slower | 8.27 ms50.4× slower | Kuzu |
| where_or | 1000 | 147.16 µsfastest | 8.43 ms57.3× slower | LoraDB |
| where_starts_with | 1000 | 146.91 µsfastest | 5.12 ms34.9× slower | LoraDB |
| where_string_gte | 1000 | 180.97 µs1.07× slower | 6.05 ms35.9× slower | Kuzu |
| where_subexpr | 1000 | 227.59 µs1.69× slower | 134.71 µsfastest | SurrealDB |
| where_two_props | 1000 | 152.69 µsfastest | 6.46 ms42.3× slower | LoraDB |
strings(5)
WinnerKuzu| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| string_concat | 1000 | 186.55 µs1.27× slower | 5.86 ms39.8× slower | Kuzu |
| string_size | 1000 | 172.17 µs1.10× slower | 5.23 ms33.5× slower | Kuzu |
| string_substring | 1000 | 210.20 µs1.21× slower | 5.58 ms32.1× slower | Kuzu |
| string_to_lower | 1000 | 201.00 µs1.33× slower | 5.03 ms33.3× slower | Kuzu |
| string_to_upper | 1000 | 185.41 µs1.22× slower | 4.91 ms32.3× slower | Kuzu |
numerics(5)
WinnerKuzu| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| numeric_abs | 1000 | 174.85 µs1.13× slower | 5.86 ms37.8× slower | Kuzu |
| numeric_ceil | 1000 | 171.69 µs1.10× slower | 5.81 ms37.4× slower | Kuzu |
| numeric_floor | 1000 | 175.48 µs1.10× slower | 5.99 ms37.5× slower | Kuzu |
| numeric_pow | 1000 | 166.22 µs1.16× slower | 8.42 ms58.8× slower | Kuzu |
| numeric_round | 1000 | 180.68 µsfastest | 5.87 ms32.5× slower | LoraDB |
aggregates(7)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| aggregate_avg | 1000 | 81.43 µsfastest | 6.22 ms76.4× slower | LoraDB |
| aggregate_collect | 1000 | 78.90 µsfastest | 6.79 ms86.0× slower | LoraDB |
| aggregate_count | 1000 | 59.50 µs2.77× slower | 209.05 µs9.75× slower | Grafeo |
| aggregate_max | 1000 | 79.97 µsfastest | 6.08 ms76.0× slower | LoraDB |
| aggregate_min | 1000 | 79.67 µsfastest | 5.95 ms74.7× slower | LoraDB |
| aggregate_sum | 1000 | 78.09 µsfastest | 6.23 ms79.8× slower | LoraDB |
| top_k | 1000 | 186.50 µsfastest | 6.40 ms34.3× slower | LoraDB |
pipeline(6)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| case_when | 1000 | 173.33 µsfastest | 7.38 ms42.6× slower | LoraDB |
| coalesce_existing | 1000 | 161.92 µsfastest | 7.51 ms46.4× slower | LoraDB |
| computed_in_return | 1000 | 151.15 µs1.07× slower | 7.35 ms52.1× slower | Kuzu |
| predicate_via_function | 1000 | 238.39 µs1.38× slower | 7.45 ms43.2× slower | Kuzu |
| with_pipeline | 1000 | 186.41 µsfastest | 5.98 ms32.1× slower | LoraDB |
| with_two_chained | 1000 | 313.64 µs1.41× slower | 8.12 ms36.5× slower | Kuzu |
lists(2)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| list_in_construction | 1000 | 177.94 µs1.05× slower | 6.14 ms36.4× slower | Kuzu |
| list_unwind_explicit | 1000 | 1.11 µsfastest | 33.52 µs30.2× slower | LoraDB |
sort(2)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| order_by_id_asc | 1000 | 167.95 µsfastest | 5.24 ms31.2× slower | LoraDB |
| skip_limit | 1000 | 162.19 µsfastest | 5.24 ms32.3× slower | LoraDB |
traversals(11)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| direct_record_traversal | 500 | 831.70 nsfastest | 62.98 µs75.7× slower | LoraDB |
| recursive_depth2 | 500 | 968.69 nsfastest | 110.22 µs114× slower | LoraDB |
| recursive_depth3 | 500 | 1.04 µsfastest | 147.78 µs142× slower | LoraDB |
| recursive_depth5 | 500 | 1.16 µsfastest | 225.94 µs195× slower | LoraDB |
| relation_filter | 500 | 117.78 µsfastest | 18.70 ms159× slower | LoraDB |
| traversal_count_one_hop | 500 | 59.36 µsfastest | 106.91 µs1.80× slower | LoraDB |
| traversal_filter_one_hop | 500 | 138.99 µsfastest | 21.51 ms155× slower | LoraDB |
| traversal_one_hop | 500 | 125.98 µsfastest | 25.78 ms205× slower | LoraDB |
| traversal_reverse | 500 | 123.67 µsfastest | 24.71 ms200× slower | LoraDB |
| traversal_three_hop | 500 | 236.10 µsfastest | 60.59 ms257× slower | LoraDB |
| traversal_two_hop | 500 | 165.58 µsfastest | 44.18 ms267× slower | LoraDB |
patterns(4)
WinnerLoraDB| Workload | Size | LoraDB | SurrealDB | Winner |
|---|---|---|---|---|
| edge_subquery_clause | 500 | 213.55 µsfastest | 18.19 ms85.2× slower | LoraDB |
| star_fanout | 1000 | 138.96 µsfastest | 29.94 ms215× slower | LoraDB |
| star_fanout_count | 1000 | 61.36 µsfastest | 20.35 ms332× slower | LoraDB |
| star_fanout_filter | 1000 | 112.53 µsfastest | 24.13 ms214× slower | LoraDB |
Honest omissions
Workloads SurrealDB couldn’t run like-for-like.
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_existingSurrealDB- SurrealDB's UPSERT semantics diverge from Cypher MERGE on which fields are matched vs set.
merge_createSurrealDB- See merge_existing — UPSERT semantics differ.
bulk_edgesSurrealDB- UNWIND-driven bulk RELATE requires scripted FOR loops; not a like-for-like comparison.
distinctSurrealDB- `value` is a reserved word in SurrealQL's SELECT VALUE clause; no clean equivalent.
numeric_moduloSurrealDB- SurrealQL parser rejects bare `%` inside SELECT projections (same parse limit as grouped_aggregation).
aggregate_count_distinctSurrealDB- count(DISTINCT) has no direct SurrealQL aggregate; requires nested SELECT + array::distinct.
grouped_aggregationSurrealDB- SurrealQL rejects `%` inside a SELECT projection that's then used as a GROUP BY key (parse error).
with_distinct_then_countSurrealDB- DISTINCT on `value` needs SELECT VALUE, where `value` is reserved (same parse limit as the `distinct` workload).
with_aggregate_then_filterSurrealDB- Groups on `value % 10`; SurrealQL rejects `%` in a projection used as a GROUP BY key (same parse limit as grouped_aggregation).
distinct_with_orderSurrealDB- DISTINCT + ORDER BY on `value`, which is reserved in both SELECT VALUE and ORDER BY (see distinct, order_by_multi_key).
range_functionSurrealDB- SurrealQL has no row-generating numeric range (no UNWIND/range equivalent); the explicit-list unwind is covered by list_unwind_explicit.
order_by_multi_keySurrealDB- `value` is reserved in SurrealQL ORDER BY clauses (parse error).
traversal_undirectedSurrealDB- 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_pathSurrealDB- 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_5SurrealDB- Range-bounded variable-length expansion from every start node; see variable_length_path.
varlen_exact_5SurrealDB- Fixed-depth expansion from every start node; the anchored single-source equivalent is benched as recursive_depth5.