From 4ebe96c37922f920c357cb05df12d35617bef07a Mon Sep 17 00:00:00 2001 From: prrao87 Date: Sat, 7 Nov 2026 09:35:08 -0500 Subject: [PATCH 1/2] docs: warn early that string and UUID keys slow traversal Add an integer-key tip where the first graph is defined, give the key section in Graph schema a performance-oriented heading, and spell out the integer-key case and the benchmark key types on the CSR page. --- docs/csr.md | 7 ++++--- docs/getting-started.md | 15 +++++++++++++++ docs/graph-schema.md | 7 ++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/csr.md b/docs/csr.md index 53bbace..6727828 100644 --- a/docs/csr.md +++ b/docs/csr.md @@ -529,9 +529,10 @@ every query on the engine. | Queries traverse relationships, especially 2+ hops or variable-length | **Turn it on** | | Queries read whole relationships (no filter on the start node) and only count | **Turn it on**; counts are taken per node ([when it helps](#when-it-helps)) | | Queries read whole relationships and return or group by each edge's properties | About even with the join path; measure both | -| Integer node keys and edge endpoints | **Turn it on**; you get the fused target lookup too | -| String or UUID keys | Little benefit; add an integer id column if traversal matters | +| Integer node keys and edge endpoints | **Turn it on**. This is the fast case: each hop reads only its start nodes' neighbours, and each neighbour's properties are read straight from its row instead of through a second join ([the fused target lookup](#the-fused-target-lookup)) | +| String or UUID keys | Little benefit: the CSR can't index them, so every hop still joins whole tables. Add an integer id column if traversal matters | | Only node lookups and aggregations, no hops | Leave it off; it costs build time and memory for nothing | | Very large edge tables and tight memory | Budget ~95 bytes per edge of peak memory before turning it on | -The [benchmarks](benchmarks.md) were all run with `use_csr=True`. +The [benchmarks](benchmarks.md) were all run with `use_csr=True`, on GDC SNB data whose node keys and edge +endpoints are integers. diff --git a/docs/getting-started.md b/docs/getting-started.md index 46923bb..02c8ddc 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -106,6 +106,21 @@ Each key of `datasets` is a node label or relationship type. Now describe how th --8<-- "crates/lance-csr/examples/embedded_query.rs:gs_config" ``` +!!! tip "Use integer keys" + + We recommend integer node keys and integer `src`/`dst` columns, as in this example. String keys, + including UUIDs, give the same results but slow down every query that traverses relationships. + + With the [CSR index](#go-multi-hop-with-the-csr) turned on, a hop from a node reads only that node's + neighbours from an in-memory adjacency list. That list stores each key as a 64-bit integer, so string keys + can't go in it. Their hops fall back to two hash joins: one over the whole relationship table and one over + the whole target node table. That's whole-table work at every hop, however few nodes the query starts + from, and it grows with the number of hops and the size of the tables. The engine doesn't warn when it falls + back. Path variables (`MATCH p = ...`) need integer keys too, and raise an error without them. + + If your source data uses UUIDs, add an integer id column to use as the key and keep the UUID as an ordinary + property. See [Key types and traversal performance](graph-schema.md#key-types-and-traversal-performance). + ## Run a query `CypherEngine` registers the datasets once and runs as many queries as you like against them: diff --git a/docs/graph-schema.md b/docs/graph-schema.md index 68bfc1b..0bfdb3b 100644 --- a/docs/graph-schema.md +++ b/docs/graph-schema.md @@ -159,12 +159,13 @@ Key columns are properties too: `p.id` returns the node's key. `MATCH (p:Person)-[:LIKES_POST|LIKES_COMMENT]->(m) RETURN m.content` returns content from both tables. The engine plans one branch per relationship type, each joined to the right target table. -## Keys and the CSR +## Key types and traversal performance Any column type works as a key for joins. The [CSR index](csr.md) is stricter: it needs **integer** relationship endpoints (`int8` to `int64`, `uint8` to `uint32`), and its fused target lookup needs **unique -integer** node keys. If your keys are strings or UUIDs, queries still work, using joins; consider adding a -dense integer id column if traversal speed matters. +integer** node keys. If your keys are strings or UUIDs, queries still return the same results, but every hop +uses hash joins over whole tables instead of the CSR, without a warning. Add an integer id column if traversal +speed matters; the ids don't have to be consecutive. ## Removed builder methods From aaf3e0c1825da622a0b55e1b576e7182b4a7f3ae Mon Sep 17 00:00:00 2001 From: prrao87 Date: Sat, 7 Nov 2026 09:39:38 -0500 Subject: [PATCH 2/2] docs: shorten the integer-key tip --- docs/getting-started.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 02c8ddc..3c3185e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -111,12 +111,9 @@ Each key of `datasets` is a node label or relationship type. Now describe how th We recommend integer node keys and integer `src`/`dst` columns, as in this example. String keys, including UUIDs, give the same results but slow down every query that traverses relationships. - With the [CSR index](#go-multi-hop-with-the-csr) turned on, a hop from a node reads only that node's - neighbours from an in-memory adjacency list. That list stores each key as a 64-bit integer, so string keys - can't go in it. Their hops fall back to two hash joins: one over the whole relationship table and one over - the whole target node table. That's whole-table work at every hop, however few nodes the query starts - from, and it grows with the number of hops and the size of the tables. The engine doesn't warn when it falls - back. Path variables (`MATCH p = ...`) need integer keys too, and raise an error without them. + With integer keys, the [CSR index](#go-multi-hop-with-the-csr) jumps straight from a node to its + neighbours. It can't store string keys, so their queries scan whole tables at every hop instead, without + a warning. Path variables (`MATCH p = ...`) need integer keys too. If your source data uses UUIDs, add an integer id column to use as the key and keep the UUID as an ordinary property. See [Key types and traversal performance](graph-schema.md#key-types-and-traversal-performance).