Schema inspection
Inspect fields, types and missing values in Python dictionaries. The parser does not connect to a database: it describes the documents you supply, not validation rules or a guarantee about unseen data.
from bondi.schema import Schema
schema = Schema.from_documents(
[{"city": "Sydney", "stars": 4}, {"city": "Sydney", "stars": None}, {"city": "Melbourne"}],
values="full",
)
print(schema.document_count) # 3
print(schema["stars"].count) # 2
print(schema["stars"].missing_count) # 1
print(schema["city"].types["str"].value_counts) # Sydney: 2, Melbourne: 1
schema.fields exposes top-level fields; schema.flat_fields exposes all observed
paths. Each field has a types mapping whose entries contain counts and optional
values. Null is a present value, distinct from a missing field.
Choose value retention
| Mode | Retained information |
|---|---|
none (default) |
Structure and counts only |
distinct |
Also retain unique primitive values by type |
full |
Also retain each primitive value's frequency |
Use none for structural scans of large datasets. Use distinct when generating
workloads; full also works, but the generator ignores frequencies. Both retaining
modes consume memory proportional to the distinct values retained, not a fixed budget.
Nested fields and incremental ingestion
schema = Schema(values="none")
schema.update([{"items": [{"price": 10}, {}]}])
schema.update([{"items": [{"price": 20}]}])
price = schema["items.[].price"]
print(price.count, price.missing_count) # 2, 1
Missingness is measured against immediate parent objects. Array-element counts can
exceed the number of root documents. Tuple paths using ARRAY_ELEMENT handle keys
that contain literal dots or brackets; see the reference.
Read from a database
Use a bounded query and pass result.documents to Schema.from_documents. MongoDB
supports both MongoFindQuery(target, {}, limit=1000) and a random sample through
MongoAggregateQuery(target, [{"$sample": {"size": 1000}}]).
For Couchbase use SELECT RAW b FROM ... b LIMIT 1000 so rows are the documents,
not wrappers. ORDER BY RANDOM() LIMIT 1000 selects random rows but can scan and
sort a large input; it is not equivalent in cost to MongoDB's $sample.
Executors collect requested rows in memory. For a full large-collection scan, feed a synchronous PyMongo cursor directly into the parser:
from pymongo import MongoClient
with MongoClient("mongodb://127.0.0.1:27017") as client:
with client.demo.business.find({}).batch_size(1000) as documents:
schema = Schema.from_documents(documents, values="none")
The parser consumes iterables incrementally. Follow the dataset inspection tutorial for an executable notebook.