Docs
View as markdown

relate clone

The relate clone command that copies a store into a new one, between directory and .rl layouts and between codecs, with its flags, refusals and exit codes.

cli

relate clone SOURCE DEST copies a store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary into a new store and exits. Each side is a directory store A store kept as a folder of snapshot files, a WAL folder and lock files; the layout for any --database path that is not :memory: and does not end in .rl. Read more in the glossary, or a single-file store A store kept in one .rl file that holds the snapshot and the WAL together; the layout for a --database path ending in .rl. Read more in the glossary when its path ends in .rl, so one command copies, packs a directory into a .rl file, or unpacks one. The destination keeps the source's codec The on-disk encoding of a store's snapshot: Parquet (the default), JSON or markdown, fixed when the store is created. Read more in the glossary unless --to-snapshot-format names another. clone relate clone: the command that copies a store into a new one, optionally in another codec, reading the source without writing it. Read more in the glossary opens the source read-only, copies the state visible when it opened, and never writes the source; it creates the destination, which must not exist yet, and prints nothing on success.

Synopsis

relate clone [--wal-dir DIR] [--to-snapshot-format F] [--markdown-body-properties LIST] SOURCE DEST
ArgumentMeaning
SOURCEThe store to copy: a directory store, or a path ending in .rl. Required. :memory: is refused
DESTThe new store: a path ending in .rl makes a single-file store, any other path a directory store. Required. It must not exist, and its parent directory must. :memory: is refused

A directory store can be cloned while another process writes it: the clone takes the state committed when it opened and does not follow later commits. A .rl source is refused while another process holds it for writing. The destination's write-ahead log (WAL) starts at <DEST>/wal for a directory destination; a .rl destination holds its own.

Options

FlagArgumentDefaultScopeSinceDescription
--wal-dirDIR<SOURCE>/walrelate clone—Source WAL of a directory source; fails with a .rl source.
--to-snapshot-formatparquet|json|markdownthe source codecrelate clone—Destination snapshot codec (parquet, json or markdown); transcodes the published checkpoint.
--markdown-body-propertiesLISTthe source's recorded configuration, else bodyrelate clone—Body properties of a markdown destination (LIST or Label=LIST; entries separated by ;).

A — under Since: the data does not record the release that added the flag.

relate clone takes only these three flags. A query-run flag after clone, such as --read-only or --json, exits 2 with Error: unexpected argument to clone: <flag>; --read-only before clone exits 2 with Error: clone cannot be combined with a query or an access flag (--read-only).

Layouts

Every pairing of the two durable layouts works; the in-memory store is refused on either side. One pairing depends on the codec: in relate 0.1.2 a .rl destination refuses markdown body properties, so a markdown source reaches a .rl path only with --to-snapshot-format parquet or json (Codecs).

SourceDestinationResult
Directory storeDirectory storeA copy
Directory store.rl pathThe store packed into one file
.rl fileDirectory storeThe file unpacked into a folder
.rl file.rl pathA copy
:memory:AnyExit code 1: Failed to clone database: storage error: clone source must be a native directory or .rl bundle path
Any:memory:Exit code 1: Failed to clone database: storage error: clone destination must be a native directory or .rl bundle path

These examples run in a new, empty directory. This first command writes: it creates the directory store ./team with three people, two projects and three WORKS_ON relationships:

relate --database ./team "CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
       (grace:Person {name: 'Grace', role: 'engineer'}),
       (noor:Person {name: 'Noor', role: 'maintainer'}),
       (relate:Project {name: 'relate', language: 'Rust'}),
       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
       (ada)-[:WORKS_ON {since: 2024}]->(relate),
       (grace)-[:WORKS_ON {since: 2023}]->(relate),
       (grace)-[:WORKS_ON {since: 2022}]->(tck),
       (noor)-[:MAINTAINS]->(relate),
       (ada)-[:KNOWS]->(grace)"

The four layout pairings, then a read-only count of the relationships in each copy:

relate clone ./team ./team-copy
relate clone ./team ./team.rl
relate clone ./team.rl ./team-unpacked
relate clone ./team.rl ./team-copy.rl
for store in team-copy team.rl team-unpacked team-copy.rl; do
  relate --database "./$store" --read-only --mode jsonl "MATCH (:Person)-[w:WORKS_ON]->(:Project) RETURN '$store' AS store, count(w) AS works"
done
{"store":"team-copy","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.387583,"execute_ms":0.2,"lex_parse_ms":0.030125,"lower_ms":0.045166,"parse_ms":0.11820800000000001,"plan_ms":0.059417,"result_generation_ms":0.000292,"rewrite_ms":0.008624999999999999}}
{"store":"team.rl","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.39399999999999996,"execute_ms":0.208584,"lex_parse_ms":0.03025,"lower_ms":0.04575,"parse_ms":0.116167,"plan_ms":0.060291000000000004,"result_generation_ms":0.00025,"rewrite_ms":0.007833}}
{"store":"team-unpacked","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.33675000000000005,"execute_ms":0.181292,"lex_parse_ms":0.025791,"lower_ms":0.038542,"parse_ms":0.09875,"plan_ms":0.049499999999999995,"result_generation_ms":0.000292,"rewrite_ms":0.006875}}
{"store":"team-copy.rl","works":3}
{"planCache":"miss","timings":{"elapsed_ms":0.3995,"execute_ms":0.206292,"lex_parse_ms":0.033583,"lower_ms":0.049917,"parse_ms":0.126208,"plan_ms":0.058332999999999996,"result_generation_ms":0.000292,"rewrite_ms":0.008459}}

Codecs

Without --to-snapshot-format, the destination keeps the source's codec. With it, clone writes the state visible when it opened, which is the last checkpoint plus the WAL commits after it, in the parquet, json or markdown codec; the layout and the codec are chosen independently.

Source codec--to-snapshot-formatLayouts in the example
parquetjsondirectory to directory, .rl to .rl
parquetmarkdowndirectory to directory, .rl to directory
parquetparquetdirectory to directory
jsonparquetdirectory to directory
markdownparquetdirectory to directory, directory to .rl

Any other value exits 2 with Error: invalid --to-snapshot-format: storage error: unknown snapshot codec '<value>' (expected parquet, json, markdown or rkyv). rkyv is not compiled into the release binary: --to-snapshot-format rkyv exits 1 with Failed to clone database: storage error: rkyv snapshot codec is not compiled into this build (enable the "rkyv-codec" feature). Storage formats describes each codec's files.

Each pairing in the table, with the codec relate info reports for every result:

relate clone --to-snapshot-format json ./team ./team-json
relate clone --to-snapshot-format markdown ./team ./team-vault
relate clone --to-snapshot-format parquet ./team ./team-parquet
relate clone --to-snapshot-format parquet ./team-json ./from-json
relate clone --to-snapshot-format parquet ./team-vault ./from-vault
relate clone --to-snapshot-format parquet ./team-vault ./team-vault.rl
relate clone --to-snapshot-format json ./team.rl ./team-json.rl
relate clone --to-snapshot-format markdown ./team.rl ./vault-from-rl
for store in team-json team-vault team-parquet from-json from-vault team-vault.rl team-json.rl vault-from-rl; do
  printf '%s: ' "$store"
  relate info --database "./$store" | grep '^codec:'
done
team-json: codec: json
team-vault: codec: markdown
team-parquet: codec: parquet
from-json: codec: parquet
from-vault: codec: parquet
team-vault.rl: codec: parquet
team-json.rl: codec: json
vault-from-rl: codec: markdown

The graph survives a round trip through the text codecs:

relate --database ./from-vault --read-only --mode jsonl "MATCH (p:Person)-[w:WORKS_ON]->(r:Project) RETURN p.name AS person, r.name AS project, w.since AS since ORDER BY since"
{"person":"Grace","project":"opencypher-tck","since":2022}
{"person":"Grace","project":"relate","since":2023}
{"person":"Ada","project":"relate","since":2024}
{"planCache":"miss","timings":{"elapsed_ms":0.579875,"execute_ms":0.39808299999999996,"lex_parse_ms":0.030583000000000003,"lower_ms":0.044875,"parse_ms":0.115042,"plan_ms":0.058415999999999996,"result_generation_ms":0.000417,"rewrite_ms":0.008333}}

Markdown into a .rl file

In relate 0.1.2 a .rl destination refuses markdown body properties, whether they come from --markdown-body-properties or from the configuration a markdown source records. Cloning a markdown store into a .rl path therefore needs --to-snapshot-format parquet or json; without it, or with --to-snapshot-format markdown, the clone exits 1 and creates nothing. The message names parquet whatever codec the command asked for:

relate clone ./team-vault ./vault-kept.rl
echo "exit=$?"
relate clone --to-snapshot-format markdown ./team-vault ./vault-md.rl
echo "exit=$?"
Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored
exit=1
Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored
exit=1

A parquet or json source with no body properties clones into a markdown .rl file: relate clone --to-snapshot-format markdown ./team.rl ./team-md.rl succeeds, and relate info reports codec: markdown for it. A markdown store clones into a directory store with any codec.

Markdown body properties

--markdown-body-properties names the properties a markdown destination writes as each note's body instead of its frontmatter: a comma-separated list for every label, or Label=LIST for one label, with several entries separated by ; (Person=role,bio;Project=language). The first listed property is written as the bare body and the rest as ## sections. Without the flag, the destination uses the source's recorded configuration, else the single property body. On a directory destination whose codec is not markdown, it exits 1 with Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored (the codec named is the destination's).

relate clone --to-snapshot-format markdown --markdown-body-properties 'Person=role' ./team ./team-notes
cat team-notes/Person/Ada.md
---
type: Person
name: Ada
KNOWS:
  - "[[Person/Grace]]"
WORKS_ON:
  - since: 2024
    target: "[[Project/relate]]"
---

engineer

WAL directory

--wal-dir DIR names the source's WAL when it is not at <SOURCE>/wal. It applies to a directory source only; with a .rl source, which holds its own WAL, it exits 1 with Failed to clone database: storage error: wal_dir is not supported for .rl single-file databases (ADR-0074). It does not move the destination's WAL.

What clone refuses

InputExit codeMessage
DEST exists1Failed to clone database: storage error: clone destination already exists
DEST's parent directory does not exist1Failed to clone database: storage error: clone destination parent does not exist: <path>
A directory SOURCE that does not exist1Failed to clone database: storage error: read-only open requires an existing database at <path>
A .rl SOURCE that does not exist1Failed to clone database: storage error: database not found: <path>
A .rl SOURCE another process holds for writing1Failed to clone database: storage locked: <path>
:memory: on either side1clone source must be a native directory or .rl bundle path, or clone destination …, after Failed to clone database: storage error:
A .rl DEST with markdown body properties: a markdown SOURCE without --to-snapshot-format parquet or json, or --markdown-body-properties1Failed to clone database: semantic error: markdown body properties are set but the store's snapshot codec is parquet, not markdown; the configuration would be ignored
--wal-dir with a .rl source1Failed to clone database: storage error: wal_dir is not supported for .rl single-file databases (ADR-0074)
Fewer than two paths2Error: clone requires SOURCE and DEST
A third path, or a flag clone does not take2Error: unexpected argument to clone: <argument>

A runtime refusal (exit code 1) prints one line on stderr and no error object. A usage error (exit code 2) prints the error line, the usage line and Run 'relate clone --help' for more information.

Errors and exit codes

Exit codeMeaning for relate clone
0The destination was created; nothing is printed
1The copy did not run or did not finish: a refusal in the table, or a storage or I/O error
2Usage error: a missing or extra argument, an unknown flag, or a bad --to-snapshot-format value

Examples

An existing destination is never overwritten:

relate clone ./team ./team-copy
echo "exit=$?"
Failed to clone database: storage error: clone destination already exists
exit=1

See also