#### Transfer & sync across databases [image: .md][image]

This guide shows how to sync objects from a source database to your
default database.

If you don't have a database, create one with the modules you need on
the target. Here we pass "bionty" because we'll transfer biological
entities:

 lamin init --modules bionty

### Using "sync"

You can sync an object from any database to your current database:

-[ CLI ]-

 lamin io sync https://lamin.ai/laminlabs/lamindata/record/gL3TbX2qZQmCwTAU

-[ Python ]-

 import lamindb as ln

 ln.core.sync(
 registry=ln.Record,
 uid="gL3TbX2qZQmCwTAU",
 source_db="laminlabs/lamindata",
 )

To sync annotations in addition to the bare object, pass the "--
transfer" / "transfer" argument:

* ""sqlrecord"": the object and its foreign keys

* ""notes"": its associated notes

* ""annotations"": its annotations

You can also pass a "--depth" argument for "HasType" objects, which
indicates how deeply you want to recurse through the type hierarchy.
For details, see "sync()".

"space", "branch", and "created_on" are handled differently from every
other foreign key. Other foreign keys, such as "created_by",
"storage", "schema", and "type", are copied, and a missing row is
created on the target.

* **Space.** Mapped by uid. When that space is not attached to the
  target database, sync raises. Attach the space, then sync again.
  Sync does not create a space.

* **Branch and "created_on".** Each is mapped by uid when that branch
  already exists on the target. "main", "archive", and "trash" always
  do. When a uid is absent, that field is set to the current branch of
  the transfer. Sync does not create a branch.

What the high-level "sync" command does is wrapping the lower-level
"SQLRecord.save()" API. Let's walk through it!

### Using "save"

Query the object on the source, then call ".save()":

 import lamindb as ln

 # optionally track the run
 ln.track()
 # instantiate a database object for your source database
 db = ln.DB("laminlabs/lamindata")
 # query the artifact on the source database
 artifact = db.Artifact.get(key="example_datasets/mini_immuno/dataset1.h5ad")
 # sync the artifact to the current database
 artifact.save()

To transfer annotations, pass "transfer="annotations"":

 # query again so that `artifact` points to the object on the source database
 artifact = db.Artifact.get(key="example_datasets/mini_immuno/dataset1.h5ad")
 # sync with annotations
 artifact.save(transfer="annotations")

The artifact now has all feature & label annotations:

 artifact.describe()

The sync is zero-copy: the data itself remains in the original storage
location.

 artifact.path

Data lineage indicates the source database of the sync:

 artifact.view_lineage()

The run that initiated the transfer is linked via "initiated_by_run":

 artifact.run.initiated_by_run.transform

Upon calling ".save()" again, "lamindb" identifies that the object
already exists in the target database and simply maps it:

 artifact = db.Artifact.get(key="example_datasets/mini_immuno/dataset1.h5ad")
 artifact.save()

A **data record** can be synced only after its type is already in the
target database. EXP-RNA-032 belongs to the RNA-seq record frame, so
transfer that frame first:

 rna_seq_frame = db.Record.get("gL3TbX2qZQmCwTAU")
 rna_seq_frame.save(transfer="annotations")

Now transfer the experiment record:

 record = db.Record.get("mNDJgWFrkWQVW3ox")
 record.save(transfer="annotations")
 record.describe()

-[ How do I know if an object is in the default database or elsewhere?
]-

Every "SQLRecord" object has an attribute "._state.db" which can take
the following values:

* "None": the object has not yet been saved to any database

* ""default"": the object is saved on the default database instance

* ""account/name"": the object is saved on a non-default database
  instance referenced by "account/name" (e.g., "laminlabs/lamindata")

 assert artifact.run.transform.description == "Transfer from `laminlabs/lamindata`"
 assert artifact.run.transform.key == "__lamindb_transfer__/4XIuR0tvaiXM"
 assert artifact.run.transform.uid == "4XIuR0tvaiXM0000"
 assert artifact.run.initiated_by_run.transform.description.startswith("Transfer & sync")
 assert artifact.features.slots
 for schema in artifact.features.slots.values():
 _ = schema.index

 rna_seq = ln.Record.get("gL3TbX2qZQmCwTAU")
 assert rna_seq.is_type
 assert rna_seq._state.db == "default"

 source = db.Record.get("mNDJgWFrkWQVW3ox")
 expected = source.features.get_values()
 got = record.features.get_values()
 assert record._state.db == "default"
 assert set(got) == set(expected)
 for key in (
 "date_of_experiment",
 "organism",
 "assay",
 "project",
 "n_samples",
 "notes",
 "name",
 "description",
 "owner",
 ):
 assert got[key] == expected[key]

 again = db.Record.get("mNDJgWFrkWQVW3ox").save(transfer="annotations")
 assert again.id == record.id