4 Entities, relationships and attributes
Snooze attaches ERA metadata to persistent struct types, providing information on how to serialize and deserialize structures and create aliases for use in queries:
each persistent struct type has an associated entity that provides structural information on structures of that type;
each entity contains, among other things, a list of attributes that provides information on the fields of the struct type;
support for relationships between entities is planned for a future version of Snooze.
4.1 Entities
define-persistent-struct binds the struct name to the entity metadata for each persistent struct type:
| |||
| > person | |||
#<entity:person> |
(connection? persistent-struct? -> persistent-struct?)
Examples: |
| > (struct-has-entity? struct:person) |
#t |
| > (struct-has-entity? (make-person "Dave" 30)) |
#t |
Examples: |
| > (struct-entity struct:person) |
#<entity:person> |
| > (struct-entity (make-person "Dave" 30)) |
#<entity:person> |
4.2 Relationships
Snooze does not currently have explicit support for relationships. These are planned for a future release. For now, the recommended way of creating a relationship between two structures is by using a foreign key field of type:integer:
| (define-persistent-struct person |
| ([name type:string])) |
| (define-persistent-struct pet |
| ([name type:string] |
| [owner-id type:integer])) |
A caveat to this approach is that you have to make sure the target structure is saved before you reference its ID for the foreign key:
| ; This is incorrect. |
| ; The person will not have an ID because it has not been saved: |
| (make-pet "Garfield" (person-id (make-person "Jon"))) |
| ; This is correct. |
| ; The call to save! allocates an ID for the person: |
| (make-pet "Garfield" (person-id (save! (make-person "Jon")))) |
4.3 Attributes
Examples: |
| > (attr person id) |
#<attr:person-id> |
| > (attr person revision) |
#<attr:person-revision> |
| > (attr person name) |
#<attr:person-name> |
| > (attr person age) |
#<attr:person-age> |
| |||||||||||||||||||||||||||||||||||
| name : symbol? | |||||||||||||||||||||||||||||||||||
| column-name : symbol? | |||||||||||||||||||||||||||||||||||
| entity : entity? | |||||||||||||||||||||||||||||||||||
| index : natural? | |||||||||||||||||||||||||||||||||||
| accessor : (persistent-struct? -> any) | |||||||||||||||||||||||||||||||||||
| mutator : (persistent-struct? any -> void?) | |||||||||||||||||||||||||||||||||||
| type : type? |
| (entity-has-attribute? entity attribute) → boolean? |
| entity : entity? |
| attribute : (U attribute? symbol?) |
Examples: |
| > (entity-has-attribute? person (attr person name)) |
#t |
| > (entity-has-attribute? person 'name) |
#t |
Examples: |
| > (entity-attribute person (attr person name)) |
#<attr:person-name> |
| > (entity-attribute person 'name) |
#<attr:person-name> |
| > (entity-attribute person 'nom) |
Attribute not found: #<entity:person> nom |
4.4 Attribute types
Each attribute has an associated type that determines the type of column used in the database. Types come in several flavours, described below. Note that the reflection of a type may be different in different DBMS types. For example, SQLite does not support the SQL TIMESTAMP data type, so Snooze uses integers to serialize time values.
| (struct type (allows-null? default)) |
| allows-null? : boolean? |
| default : any |
4.5 Shorthand types
Snooze provides a number of short-hand types. The types below all allow and default to NULL, and type:string and type:symbol allow data of arbitrary length:
4.6 Persistent structure utilities
Examples: | ||
| ||
| > (define dave (make-person "Dave" 30)) | ||
| > (struct-attributes dave) | ||
(#f #f "Dave" 30) |
| (struct-has-attribute? struct name) → boolean? |
| struct : persistent-struct? |
| name : (U attribute? symbol?) |
Examples: |
| > (struct-has-attribute? dave (attr person name)) |
#t |
| > (struct-has-attribute? dave 'age) |
#t |
| > (struct-has-attribute? dave 'nom) |
#f |
Examples: |
| > (struct-attribute dave (attr person name)) |
"Dave" |
| > (struct-attribute dave 'age) |
30 |
| > (struct-attribute dave 'nom) |
Attribute not found: #<entity:person> nom |
| (set-struct-attribute! struct name value) → void? |
| struct : persistent-struct? |
| name : (U attribute? symbol?) |
| value : any |
Examples: |
| > (set-struct-attribute! dave (attr person name) "Noel") |
| > (set-struct-attribute! dave 'age #f) |
| > (struct-attributes dave) |
(#f #f "Noel" #f) |
Examples: |
| > (set-struct-attributes! dave (list #f #f "Dave" 30)) |
| > (struct-attributes dave) |
(#f #f "Dave" 30) |
| ||||||||||||||||||||
| → persistent-struct? | ||||||||||||||||||||
| entity : entity? | ||||||||||||||||||||
| attr : (U attribute? symbol?) | ||||||||||||||||||||
| value : any | ||||||||||||||||||||
| attr2 : (U attribute? symbol?) | ||||||||||||||||||||
| value2 : any |
Each attr must be followed by a corresponding value.
Example: | ||
| ||
#(struct:person #f #f "Noel" #f) |
| |||||||||||||||||||||||||||||||||||
| struct : persistent-struct? | |||||||||||||||||||||||||||||||||||
| attr : (U attribute? symbol?) | |||||||||||||||||||||||||||||||||||
| value : any | |||||||||||||||||||||||||||||||||||
| attr2 : (U attribute? symbol?) | |||||||||||||||||||||||||||||||||||
| value2 : any |
Each attr must be followed by a corresponding value.
| ||||||||
| → persistent-struct? | ||||||||
| struct : persistent-struct? | ||||||||
| copy : persistent-struct? |
Example: |
| > (copy-persistent-struct dave (attr person name) "Noel") |
#(struct:person #f #f "Noel" 30) |