A declarative, type-safe Python DSL for mapping complex nested JSON to relational database schemas
Project description
Etielle
Declarative, type-safe Python DSL for mapping complex nested JSON to relational database schemas.
- Repository: Promptly-Technologies-LLC/etielle
- PyPI:
etielle - Python: ≥ 3.13
Why Etielle?
- Declarative relational mapping DSL: Define tables, fields, and join keys explicitly—no stringly-typed query languages.
- Context-aware transforms: Access root, parent, current key/index, and path to compute values robustly.
- Flexible traversal: Navigate outer/inner paths and choose whether to iterate dict items or list values.
- Row merging by composite keys: Multiple traversals can contribute to the same logical row via
join_keys. - Multi-table output: One run can emit rows for multiple tables with correct foreign keys.
Unlike automatic “flatteners” (e.g., AWS Glue-like relationalize), Etielle is relational-first and schema-driven. Compared with general restructuring tools like glom, Etielle is purpose-built for producing clean, relational outputs.
Installation
Prefer installing with uv for speed and reproducibility. pip works as well.
Install with uv (recommended)
Project usage (adds to your project’s dependencies):
uv add etielle
Ad-hoc/one-off usage:
uv pip install etielle
Install with pip
pip install etielle
Quick start
The core concepts you’ll use:
- TraversalSpec: how to reach and iterate parts of your JSON (outer and optional inner paths).
- TableEmit: how to produce rows for a table from the current traversal context, including composite
join_keys. - Field: a named value computed via a transform.
- Transforms: small functions that read from the current
Context(e.g.,get(),get_from_parent()).
Minimal example
from etielle.core import (
MappingSpec,
TraversalSpec,
TableEmit,
Field
)
from etielle.transforms import get, get_from_parent
from etielle.executor import run_mapping
data = {
"users": [
{
"id": "u1",
"name": "Ada",
"posts": [
{"id": "p1", "title": "Hello"},
{"id": "p2", "title": "World"},
],
},
{"id": "u2", "name": "Linus", "posts": []},
]
}
# 1) Emit the users table
users_traversal = TraversalSpec(
path=["users"],
iterate_items=False, # iterate list values
emits=[
TableEmit(
table="users",
join_keys=[get("id")], # single join key; becomes id if not set explicitly
fields=[
Field("id", get("id")),
Field("name", get("name")),
],
)
],
)
# 2) Emit the posts table (inner traversal under each user)
posts_traversal = TraversalSpec(
path=["users"],
iterate_items=False,
inner_path=["posts"],
inner_iterate_items=False, # iterate list values
emits=[
TableEmit(
table="posts",
join_keys=[get("id")],
fields=[
Field("id", get("id")),
Field("user_id", get_from_parent("id")), # FK to users.id
Field("title", get("title")),
],
)
],
)
spec = MappingSpec(traversals=[users_traversal, posts_traversal])
result = run_mapping(data, spec)
print(result)
# {
# 'users': [
# {'id': 'u1', 'name': 'Ada'},
# {'id': 'u2', 'name': 'Linus'},
# ],
# 'posts': [
# {'id': 'p1', 'user_id': 'u1', 'title': 'Hello'},
# {'id': 'p2', 'user_id': 'u1', 'title': 'World'},
# ]
# }
Transform cheatsheet
- get(path): read a value relative to the current node;
pathaccepts dot notation or a list (ints allowed for list indices). - get_from_parent(path, depth=1): read a value from the parent node (or ancestor via
depth). - get_from_root(path): read a value from the root payload.
- key() / index(): return the current dict key or list index if iterating.
- literal(value), concat(...), format_id(..., sep="_"), coalesce(...), len_of(inner): helpers for building values.
Notes:
- Rows are merged per-table by the composite
join_keys. If a table has a single join key and you don’t set anidfield,run_mapping()will populateidfrom that join key. - If any join key part is missing/empty, the row is skipped.
How this differs from other tools
- Not just flattening: You control the schema, relationships, and keys—useful for analytics-ready models.
- Relational-first: Purpose-built for JSON → relational mappings; compare to
glom(general restructuring) or automatic flatteners (less control). - Composable and testable: Transforms are regular Python callables and type-friendly.
Roadmap ideas
- Optional adapters for inserting into ORMs/DB layers (e.g., SQLAlchemy, SQLModel).
- Benchmarks for large datasets; performance guidance.
- Expanded cookbook of mapping recipes.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file etielle-0.0.0.tar.gz.
File metadata
- Download URL: etielle-0.0.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3145da8aeeaebb0055fad9aaf9a4176c0505df503f6f3156f72dce39a45c4b1
|
|
| MD5 |
11b57e74a8d05e6d04c084790bc7a511
|
|
| BLAKE2b-256 |
c6ffb4ca124d2eae22859c285847827f000d44f24b3623bf621e061ba517ba32
|
Provenance
The following attestation bundles were made for etielle-0.0.0.tar.gz:
Publisher:
release.yml on Promptly-Technologies-LLC/etielle
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
etielle-0.0.0.tar.gz -
Subject digest:
d3145da8aeeaebb0055fad9aaf9a4176c0505df503f6f3156f72dce39a45c4b1 - Sigstore transparency entry: 600074908
- Sigstore integration time:
-
Permalink:
Promptly-Technologies-LLC/etielle@5b2a23c0fd16c3add2d26e05a565e2918a52575a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Promptly-Technologies-LLC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5b2a23c0fd16c3add2d26e05a565e2918a52575a -
Trigger Event:
push
-
Statement type:
File details
Details for the file etielle-0.0.0-py3-none-any.whl.
File metadata
- Download URL: etielle-0.0.0-py3-none-any.whl
- Upload date:
- Size: 7.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2f46bb91d5eabc4d9a028c74f3acc6c1241460bae783541c659525db8e7d120
|
|
| MD5 |
451849b31a853afaafa1050facf94c43
|
|
| BLAKE2b-256 |
934950a1c2d025ed59f25c7975f7aa13574d84fccc29a289da13b7b9ab81dbc4
|
Provenance
The following attestation bundles were made for etielle-0.0.0-py3-none-any.whl:
Publisher:
release.yml on Promptly-Technologies-LLC/etielle
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
etielle-0.0.0-py3-none-any.whl -
Subject digest:
b2f46bb91d5eabc4d9a028c74f3acc6c1241460bae783541c659525db8e7d120 - Sigstore transparency entry: 600074911
- Sigstore integration time:
-
Permalink:
Promptly-Technologies-LLC/etielle@5b2a23c0fd16c3add2d26e05a565e2918a52575a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Promptly-Technologies-LLC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5b2a23c0fd16c3add2d26e05a565e2918a52575a -
Trigger Event:
push
-
Statement type: