1 Introduction
1.1 Overview
std.lib.schema is part of the standard foundation library set. This page collects the public API reference for the namespace.
2 Walkthrough
2.1 Scope expansion
Schemas tag columns with scopes such as :-/id, :-/data, and :-/info. The wildcard scopes : are expanded by expand-scopes into concrete scope keywords.
expand wildcard scopes
(schema/expand-scopes :*/data)
=> #{:-/info :-/id :-/data :-/key}
(schema/expand-scopes :*/info)
=> #{:-/info :-/id :-/key}
2.2 Ordering and defaults
order-keys sorts a list of columns by their declared :order. get-defaults collects SQL defaults such as auto-generated UUIDs and boolean flags.
order keys according to schema
(let [tsch {:cache [{:order 3}]
:status [{:order 1}]
:id [{:order 0}]
:name [{:order 2}]}]
(schema/order-keys tsch [:cache :status :id :WRONG]))
=> '(:id :status :name :cache :WRONG)
collect default values
(let [tsch {:__deleted__ [{:sql {:default false} :order 8}]
:id [{:sql {:default '(uuid)} :order 0}]}]
(schema/get-defaults tsch))
=> '{:__deleted__ false :id (uuid)}
2.3 Validation checks
Validate incoming data against a schema: check-valid-columns rejects unknown columns, check-missing-columns ensures required columns are present, and check-fn-columns runs custom :check predicates.
check column validity
(let [tsch {:id [{:required true :order 0}]
:status [{:required true :order 1}]
:name [{:required true :order 2}]}]
(schema/check-valid-columns tsch [:id :status])
=> [true]
(schema/check-valid-columns tsch [:id :WRONG])
=> (contains-in [false {:not-allowed #{:WRONG}}]))
check required columns
(let [tsch {:id [{:required true :order 0}]
:status [{:required true :order 1}]
:name [{:required true :order 2}]}]
(schema/check-missing-columns tsch [:status] :required)
=> [false {:missing #{:id :name}
:required #{:id :status :name}}]
(schema/check-missing-columns tsch [:status :name :id] :required)
=> [true])
run per-column check functions
(let [tsch {:status [{:check keyword? :order 1}]}]
(schema/check-fn-columns tsch {:status 'a})
=> {})
2.4 Returning columns
get-returning builds the column list for a RETURNING clause. It accepts explicit columns and wildcard scopes, and returns ordered attribute entries.
collect returning columns
(let [tsch {:__deleted__ [{:scope :-/hidden :order 8}]
:id [{:scope :-/id :order 0}]
:status [{:scope :-/info :order 1}]
:name [{:scope :-/data :order 2}]
:cache [{:scope :-/ref :order 3}]}]
(->> (schema/get-returning tsch [:*/data :cache])
(map first)))
=> '(:id :status :name :cache)
2.5 End-to-end: validate input and compute a return set
A typical insert flow validates the requested columns, checks required fields, and then expands a scope-based return list.
validate data and build a RETURNING clause
(let [tsch {:id [{:scope :-/id :required true :order 0}]
:status [{:scope :-/info :required true :order 1}]
:name [{:scope :-/data :required true :order 2}]
:time-created [{:scope :-/system :order 3}]
:__deleted__ [{:scope :-/hidden :sql {:default false} :order 4}]}
input [:id :status :name]]
(schema/check-valid-columns tsch input)
=> [true]
(schema/check-missing-columns tsch input :required)
=> [true]
(->> (schema/get-returning tsch [:*/data :*/id])
(map first))
=> '(:id :status :name))
3 API
- check-fn-columns
- check-missing-columns
- check-scope
- check-valid-columns
- expand-scopes
- get-defaults
- get-returning
- linked-primary
- order-keys
- schema
- schema?
- with:ref-fn
v 4.0
(defn check-fn-columns
([tsch data]
(let [checks (coll/keep-vals (fn [[{:keys [check]}]]
check)
tsch)]
(->> data
(keep (fn [[k v]]
(if-let [check (get checks k)]
(if (not (h/suppress (check v)))
[k {:key k
:value v
:check check
:message "Check failed for input" }]))))
(into {})))))
link
(schema/check-fn-columns -tsch- {:status 'a}) => {}
v 4.0
(defn check-missing-columns
([tsch columns required-fn]
(let [required (set (keep (fn [[k [attrs]]]
(if (required-fn attrs) k))
tsch))
diff (clojure.set/difference required
(set columns))]
(if (empty? diff)
[true]
[false {:missing diff
:required required}]))))
link
(schema/check-missing-columns -tsch- [:status] :required) => [false {:missing #{:name :cache}, :required #{:name :cache :status}}] (schema/check-missing-columns -tsch- [:status :name :cache] :required) => [true]
v 4.0
(defn check-scope
[scope]
(or (get (:*/everything +scope+) scope)
(h/error "Scope not Valid" {:value scope
:allowed (:*/everything +scope+)})))
link
(base/check-scope :-/info) => :-/info (base/check-scope :-/WRONG) => (throws)
v 4.0
(defn check-valid-columns
([tsch columns]
(let [diff (clojure.set/difference (set (filter keyword? columns))
(set (keys tsch)))]
(if (empty? diff)
[true]
[false {:not-allowed diff
:valid (set (keys tsch))}]))))
link
(schema/check-valid-columns -tsch- [:id :status]) => [true] (schema/check-valid-columns -tsch- [:id :WRONG]) => (contains-in [false {:not-allowed #{:WRONG}}])
v 4.0
(defn expand-scopes
[k]
(get base/+scope+ k))
link
(schema/expand-scopes :*/data) => #{:-/info :-/id :-/data :-/key} (schema/expand-scopes :*/info) => #{:-/info :-/id :-/key}
v 4.0
(defn get-defaults
([tsch]
(coll/keep-vals (fn [[{:keys [sql]}]] (:default sql))
(comp not nil?)
tsch)))
link
(schema/get-defaults -tsch-) => '{:__deleted__ false, :id (postgres.core/uuid-generate-v4)}
v 4.0
(defn get-returning
([tsch returning]
(let [scope-ks (filter (fn [x] (and (keyword? x) (namespace x))) returning)
col-ks (set (filter (fn [x] (and (keyword? x) (nil? (namespace x)))) returning))
[_ err] (check-valid-columns tsch col-ks)
_ (if err (h/error "Not valid." (assoc err :data returning)))
union-fn (fn union-fn [s]
(apply clojure.set/union
(map (fn [k]
(if (= "*" (namespace k))
(union-fn (expand-scopes k))
#{k}))
s)))
all-fn (union-fn scope-ks)]
(->> (seq tsch)
(sort-by (fn [[k [{:keys [order]}]]]
[(or order Integer/MAX_VALUE) k]))
(filter (fn [[k [{:keys [scope]}]]]
(or (all-fn scope)
(col-ks k))))))))
link
(->> (schema/get-returning -tsch- [:*/data :cache]) (map first)) => '(:id :status :name :cache :time-created :time-updated)
v 4.0
(defn linked-primary
([tsch k schema]
(let [link (-> tsch k first :ref :link)
rid (keyword (name (:id link)))
rsch (get (:tree schema) rid)
[k [attrs]] (first (filter (fn [[k [{:keys [primary] :as m}]]]
(if primary [k m]))
rsch))]
(if (nil? attrs)
(h/error "Column not found" {:link link
:key k
:tree rsch})
attrs))))
link
(schema/linked-primary -tsch- :cache +schema+) => '{:type :uuid, :cardinality :one, :primary true, :web {:example "AUD"}, :sql {:default (postgres.core/uuid-generate-v4)}, :scope :-/id, :order 0, :ident :TaskCache/id}
v 4.0
(defn order-keys
([tsch ks]
(sort-by (fn [k] (or (-> tsch k (first) :order)
Integer/MAX_VALUE))
ks)))
link
(schema/order-keys -tsch- [:cache :status :id :WRONG]) => '(:id :status :cache :WRONG)
v 3.0
(defn schema
([x]
(schema x (base/all-auto-defaults base/base-meta)))
([x defaults]
(cond (vector? x)
(let [schema (-> (vec->map x)
(schema-map defaults))]
(assoc schema :vec x))
(map? x)
(schema-map x defaults))))
link
(-> (schema [:account [:name {} :email {:type :ref :ref {:ns :email}}]]) :flat simplify) => {:email {:accounts :&account<*>} :account {:email :&email :name :string}}
4 std.lib.schema: A Comprehensive Summary
The std.lib.schema module provides a powerful and flexible framework for defining, manipulating, and validating data schemas. It is designed to handle complex, nested data structures, particularly those used in database modeling or API definitions. The module offers functionalities for schema creation, flattening, nesting, reference handling, and various assertion checks, making it a central component for data integrity and consistency within the foundation-base project.
The module is organized into several sub-namespaces:
4.1 std.lib.schema.base
This namespace defines fundamental concepts and utilities for schema processing, including scope definitions, default attribute values, and type checking.
+scope-brief+: A map defining hierarchical scopes (e.g.,:*/min,:*/info,:*/data) for schema attributes, allowing for grouping and filtering of columns based on their relevance.nexpand-scopes [m & [expanded]]: Expands globbed scope keywords (e.g.,:*/data) into their constituent concrete scope keywords (e.g.,:-/info,:-/id,:-/data).n+scope+: A pre-expanded version of+scope-brief+, providing a complete mapping of globbed scopes to their concrete keywords.ncheck-scope [scope]: Validates if a given scope keyword is a valid, known scope.nbase-meta: A map defining metadata for base schema attributes (e.g.,:ident,:type,:cardinality), including their required status, default values, and validation checks.nattr-add-ident [[k [attr :as v]]]: Adds the keykas the:identattribute to a schema property.nattr-add-defaults [[k [attr :as v]] dfts]: Adds default values to schema properties based on a list of default attribute definitions.ndefaults [](#k prop): Extracts default and auto-generation properties from a schema attribute definition.nall-auto-defaults [& [meta]]: Collects all attributes that have an:autodefault value frombase-metaor a custom meta.nall-defaults [& [meta]]: Collects all attributes that have a:defaultvalue frombase-metaor a custom meta.ntype-checks [t k]: A multimethod for retrieving type-checking functions based on a categorytand typek(e.g.,:default :stringreturnsstring?).
4.2 std.lib.schema.impl
This namespace provides the core implementation for creating and manipulating Schema objects, including flattening, nesting, and creating lookup structures.
simplify [flat]: A helper function to simplify a flattened schema for easier display, converting complex attribute maps into concise keyword representations.nSchemaRecord: The central record for a schema, holding:nflat: A flattened representation of the schema (dot-separated keys).ntree: A nested (tree-like) representation of the schema.nlu: A lookup map for reverse references.nvec: An optional vector representation of the schema.n It implementsObject/toStringfor informative display.ncreate-lookup [fschm]: Creates a lookup map from a flattened schema, primarily used for resolving reverse references.ncreate-flat-schema [m & [defaults]]: Creates a flattened schema from an input mapm, applying default attribute values. It handles adding:identand default attributes, and processes reference attributes.nvec->map [v]: Converts a vector-based schema definition into a map-based representation.nschema-map [m & [defaults]]: Creates aSchemaobject from a map-based schema definition.nschema [x & [defaults]]: The main function for creating aSchemaobject. It can accept either a map or a vector representation of the schema.nschema? [obj]: A predicate to check if an object is aSchemainstance.
4.3 std.lib.schema.ref
This namespace focuses on handling reference attributes within a schema, including creating forward and reverse references and managing their properties.
*ref-fn*: A dynamic var that can be bound to a function for adding additional parameters to reverse reference attributes.nwith:ref-fn [[ref-fn] & body]: A macro to bind*ref-fn*for a block of code.nkeyword-reverse [k]: Reverses a keyword by adding or removing an underscore prefix (e.g.,:a/b->:a/_b,:a/_b->:a/b).nkeyword-reversed? [k]: Checks if a keyword is in its "reversed" form (starts with an underscore).nis-reversible? [attr]: Determines if a reference attribute is eligible for automatic reverse reference generation.ndetermine-rval [entry]: Determines the:rval(reverse value) for a reference attribute, handling pluralization and naming conventions.nforward-ref-attr [](#attr): Creates the:refschema attribute for a forward reference, populating its properties (e.g.,:type,:key,:val,:rkey,:rident).nreverse-ref-attr [](#attr): Creates the reverse:refschema attribute for a backward reference, defining its properties and linking it back to the forward reference.nforward-ref-attr-fn [entry]: A helper function forforward-ref-attr.nattr-ns-pair [](#attr): Constructs a[:ns :ident-root]pair for a schema attribute.nmark-multiple [nsgroups & [output]]: Marks groups of namespace/ident pairs that have multiple entries, used in reference processing.n*ref-attrs [fschm]: Creates both forward and reverse reference attributes for a flattened schema, automatically generating reverse references where applicable.
4.4 std.lib.schema (Facade Namespace)
This namespace acts as a facade, re-exporting key functions from its sub-namespaces and providing additional utilities for schema introspection and validation.
h/intern-in: Internsimpl/schema,impl/schema?,base/check-scope, andref/with:ref-fninto this namespace.nexpand-scopes [k]: Expands globbed scope keywords (e.g.,:*/data) into their constituent concrete scope keywords usingbase/+scope+.nlinked-primary [tsch k schema]: Retrieves the primary key attribute of a linked table within the schema.norder-keys [tsch ks]: Orders a list of keys based on their:orderattribute defined in the schema.nget-defaults [tsch]: Collects default values from thesqlattributes of schema columns.ncheck-valid-columns [tsch columns]: Checks if a given set ofcolumnsare valid (exist) within the schema.ncheck-missing-columns [tsch columns required-fn]: Checks if any required columns (as determined byrequired-fn) are missing from a given set ofcolumns.ncheck-fn-columns [tsch data]: Performs validation checks on data using:checkfunctions defined in the schema attributes.nget-returning [tsch returning]: Collects a list of columns to be returned, expanding scope keywords (e.g.,:*/data) and validating column names.
Overall Importance:
The std.lib.schema module is a critical component for managing data definitions and ensuring data integrity within the foundation-base project. Its key contributions include:
- Centralized Schema Definition: Provides a unified way to define and manage complex data schemas, including relationships and validation rules.n Data Transformation: Offers tools for flattening and nesting schema representations, facilitating data processing and API interactions.n Automated Reference Handling: Simplifies the creation and management of forward and reverse references between tables/entities, crucial for relational data.n Robust Validation: Provides mechanisms for checking column validity, identifying missing required columns, and performing custom data validation.n Extensibility: The modular design allows for easy extension with new attribute types, validation rules, and processing logic.n* Code Generation Support: The structured schema representation is ideal for generating database schemas, API documentation, or client-side data models.
By offering these comprehensive schema management capabilities, std.lib.schema significantly enhances the foundation-base project's ability to handle diverse data models accurately and consistently, which is vital for its multi-language development ecosystem.