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..3c3185e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -106,6 +106,18 @@ 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 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). + ## 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