lamindb.core.sync .md

lamindb.core.sync(*, registry, uid, source_db, transfer=None, depth=0)

Sync one object from a source database into the current database.

This function underlies lamin io sync and wraps the lower-level .save() API:

import lamindb as ln

db = ln.DB("laminlabs/lamindata")
db.Record.get("gL3TbX2qZQmCwTAU").save(transfer="annotations")

Guide: Transfer & sync across databases

Parameters:
  • registry (Registry) – Registry class, for example ln.Artifact or ln.Record.

  • uid (str) – UID of the object to sync.

  • source_db (str) – Source database slug, for example laminlabs/lamindata.

  • transfer (Literal['sqlrecord', 'notes', 'annotations'] | None, default: None) – A TransferMode. Omit it to use the registry default. Schema defaults to annotations.

  • depth (int, default: 0) – How many levels of the type tree to transfer. 0 transfers only this object, plus the related objects selected by transfer. Only record, feature, schema, project, ulabel, and reference accept depth > 0.

Return type:

SQLRecord

Returns:

The saved SQLRecord object on the current database.

Traversing relationships

The transfer argument determines which related objects are transferred. If "sqlrecord", only the object with its required foreign keys are copied. If "notes", the object and its notes are copied. If "annotations", the object and its annotations are copied, that is, the object’s features, labels, and, for a schema, its members.

How foreign keys land on the target

Most foreign keys are copied. When the related row is missing on the target, transfer creates it. A missing type is inserted as a stub. A Record data record is the exception: transfer its type first, because that type carries the schema. .run and .transform point at the transfer run on the target.

space, branch, and created_on are handled differently from those foreign keys.

  • space is mapped by uid. Transfer raises NoWriteAccess when that space is not attached to the target database. Attach the space, then re-run the sync. Transfer does not create a space.

  • branch and created_on are each mapped by uid when that branch already exists on the target. The built-in branches main, archive, and trash always do. When a uid is absent, that field is set to the current branch of the transfer. Transfer does not create a branch.

Example: transfer="sqlrecord" on an artifact copies these foreign keys. .run in the diagram is the transfer run on the target, not the source run. space, branch, and created_on follow the rules above.

        flowchart TD
  artifact("Artifact"):::focal -->|storage| storage("Storage")
  artifact -->|created_by| user("User")
  artifact -->|run| run("Run")
  artifact -->|schema| schema("Schema")
    

If you pass transfer="annotations" upon transferring an artifact, its many-to-many relationships are transferred. Those contain all label & feature annotations but also inferred schemas via .schemas. Some of the many-to-many relationships of an artifact are shown below:

        flowchart TD
  artifact("Artifact"):::focal -->|ulabels| ulabels("ULabel")
  artifact -->|records| records("Record")
  artifact -->|projects| projects("Project")
  artifact -->|users| users("User")
  artifact -->|artifacts| linked("Artifact")
  artifact -->|schemas| schemas("Schema")
  artifact -->|json_values| json_values("JsonValue")
    

The related objects themselves are transferred without their own annotations to avoid an infinite recursion. You have to transfer the related object itself if you want to transfer it with its own annotations.

Re-syncing

A sync operation is safe to repeat. UIDs already on the target database are mapped. A Record that arrived earlier as a stub is filled in when you transfer that object itself. Links are replaced, not duplicated. So a first run with transfer="sqlrecord" and a second run with transfer="annotations" completes annotations.

Every row this transfer writes has .run set to a run of the transform __lamindb_transfer__/{source_database_uid}. To undo it, find that run and delete the objects whose .run is that run. Objects that were already on the target and only got mapped are not part of that run.