Manage records & ontologies .md .md

This guide walks you from managing notes to managing structured relational metadata. You can build your ELN & LIMS, your own ontologies, and even a knowledge graph. If you instead want to build on public biological ontologies, read: Manage biological ontologies .

If you haven’t connected a database, create one in a development directory:

lamin init
Hide code cell output
→ connected lamindb: testuser1/docs

Save markdown notes

You can save a markdown note as a record:

lamin save <topic>/<my-note.md>

To load a record back as a markdown file, run:

lamin load https://lamin.ai/laminlabs/lamindata/record/mNDJgWFrkWQVW3ox

Create a data record

A data record stores values for Feature objects.

import lamindb as ln

gc_content = ln.Feature(name="gc_content", dtype=float).save()
sample1 = ln.Record(name="Sample 1", features={"gc_content": 0.5}).save()
sample1.describe()
Hide code cell output
→ connected lamindb: testuser1/docs
! tip: pass `type` to map feature into a type hierarchy
! tip: pass `type` to map record into a type hierarchy
Record: Sample 1
├── uid: kVikCcq7LnHKAxZh  type:                              
│   is_type: False         schema:                            
│   reference:             branch: main                       
│   space: all             created_at: 2026-10-21 07:42:45 UTC
│   created_by: testuser1                                     
└── Features
    └── gc_content                     float                                0.5                                    

Organize records by type

To create and use a record type call:

experiment_type = ln.Record(name="Experiments", is_type=True).save()
experiment1 = ln.Record(name="Experiment 1", type=experiment_type).save()
Hide code cell output
! tip: pass `type` to map record into a type hierarchy
→ returning record with same name: 'Experiments'
! you are trying to create a record with name='Experiment 1' but a record with similar name exists: 'Experiment1'. Did you mean to load it?

You can define a feature whose dtype is a record type:

experiment = ln.Feature(name="experiment", dtype=experiment_type).save()
Hide code cell output
! tip: pass `type` to map feature into a type hierarchy
→ returning feature with same name: 'experiment'

to_dataframe() exports:

experiment_type.to_dataframe()
Hide code cell output
→ exporting 2 records of 'Experiments'
__lamindb_record_uid__ __lamindb_record_name__
__lamindb_record_id__
2 0yVDmOCdMfT3UsEX Experiment1
6 dJszBTKaF9hEPC5i Experiment 1

Constrain records with a schema

A Schema turns the type into a frame and validates the features of its data records:

schema = ln.Schema(
    [experiment, gc_content.with_config(optional=True)],
).save()
samplesheet = ln.Record(name="Samples", is_type=True, schema=schema).save()
Hide code cell output
! tip: pass `type` to map record into a type hierarchy
! you are trying to create a record with name='Samples' but a record with similar name exists: 'Sample 1'. Did you mean to load it?

experiment is required, so setting only gc_content raises ValidationError:

try:
    sample2 = ln.Record(
        name="Sample 2",
        type=samplesheet,
        features={gc_content: 0.6},
    ).save()
except ln.errors.ValidationError as error:
    print(error)
Hide code cell output
Cannot set type to 'Samples' on Record 'Sample 2': missing required features 'experiment'

Also moving sample into samplesheet raises since it only has gc_content:

try:
    sample1.type = samplesheet
except ln.errors.ValidationError as error:
    print(error)

# set required featured on sample1
sample1.features.set_values({
    experiment: experiment1,
    gc_content: 0.5,
})

# move sample1 into the samplesheet
sample1.type = samplesheet
sample1.save()
Hide code cell output
Cannot set type to 'Samples' on Record 'Sample 1': missing required features 'experiment'
Record(uid='kVikCcq7LnHKAxZh', is_type=False, name='Sample 1', description=None, reference=None, reference_type=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, type_id=7, schema_id=None, run_id=None, created_at=2026-10-21 07:42:45 UTC, is_locked=False)

To replace selected feature values while preserving other annotations, use update_values():

sample1.features.update_values({gc_content: 0.6})

Use a feature as an index

You can set an index for a schema via index:

sample_abbr = ln.Feature(name="sample_abbr", dtype=str).save()
score = ln.Feature(name="score", dtype=float).save()
schema_with_index = ln.Schema(features=[score], index=sample_abbr).save()
frame_with_index = ln.Record(
    name="Indexed samples", is_type=True, schema=schema_with_index
).save()
record = ln.Record(
    features={"sample_abbr": "S-001", "score": 1.5},
    type=frame_with_index,
).save()
Hide code cell output
! tip: pass `type` to map feature into a type hierarchy
! tip: pass `type` to map feature into a type hierarchy
! tip: pass `type` to map record into a type hierarchy

Inspect the dataframe:

frame_with_index.to_dataframe()
Hide code cell output
→ exporting 1 records of 'Indexed samples'
score
sample_abbr
S-001 1.5

Import records from a dataframe

from_dataframe() creates one data record per row. A new type name creates a record frame and infers its schema:

import pandas as pd

records = ln.Record.from_dataframe(
    pd.DataFrame({"gc_content": [0.1, 0.2]}),
    type="my_df",
).save()
records
Hide code cell output
! tip: pass `type` to map record into a type hierarchy
! tip: pass `type` to map schema into a type hierarchy
! tip: pass `type` to map schema into a type hierarchy
SQLRecordList([Record(uid='olocaDyz5AZgcRXy', is_type=False, name=None, description=None, reference=None, reference_type=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, type_id=11, schema_id=None, run_id=None, created_at=2026-10-21 07:42:47 UTC, is_locked=False),
               Record(uid='GFJ0cGGd6o8xzAgd', is_type=False, name=None, description=None, reference=None, reference_type=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, type_id=11, schema_id=None, run_id=None, created_at=2026-10-21 07:42:47 UTC, is_locked=False)])

Pass an existing record frame as type to import into it. With no schema index, a __lamindb_record_name__ column sets the record name:

ln.Record.from_dataframe(
    pd.DataFrame(
        {
            "__lamindb_record_name__": ["Imported sample"],
            "experiment": ["Experiment 1"],
            "gc_content": [0.45],
        }
    ),
    type=samplesheet,
).save()
Hide code cell output
SQLRecordList([Record(uid='0vMwRYCBODsDgduU', is_type=False, name='Imported sample', description=None, reference=None, reference_type=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, type_id=7, schema_id=None, run_id=None, created_at=2026-10-21 07:42:47 UTC, is_locked=False)])

Query records

Filter with a feature expression, or by a field such as type:

ln.Record.filter(gc_content == 0.5).to_dataframe(include="features")
Hide code cell output
! falling back to generic Record queryset export because the queryset does not resolve to exactly one non-null `type_id`
uid id name
ln.Record.filter(gc_content > 0.4).to_dataframe(include="features")
Hide code cell output
→ exporting 2 records of 'Samples'
experiment gc_content __lamindb_record_uid__ __lamindb_record_name__
__lamindb_record_id__
5 Experiment 1 0.60 kVikCcq7LnHKAxZh Sample 1
14 Experiment 1 0.45 0vMwRYCBODsDgduU Imported sample
ln.Record.filter(type=samplesheet).to_dataframe(include="features")
Hide code cell output
→ exporting 2 records of 'Samples'
experiment gc_content __lamindb_record_uid__ __lamindb_record_name__
__lamindb_record_id__
5 Experiment 1 0.60 kVikCcq7LnHKAxZh Sample 1
14 Experiment 1 0.45 0vMwRYCBODsDgduU Imported sample

Export record frames as artifacts

Click on “Export to Artifact” to save all data records under a type as an artifact.

In the API, call to_artifact() on a record frame:

experiment_type.to_artifact()
Hide code cell output
→ exporting 2 records of 'Experiments'
Artifact(uid='hfNa1zRtdDUdqdfr0000', key='lamindb_record_exports/Experiments.csv', description='Export of record type WBI2SiqOQyuhFE9t', suffix='.csv', kind='dataset', otype='DataFrame', size=106, hash='7AVpQqJmqYbs3xsXCQe-rg', n_files=None, n_observations=2, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=14, schema_id=None, created_by_id=1, created_at=2026-10-21 07:42:47 UTC, is_locked=False, version_tag=None, is_latest=True)