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
Metadata describing a particular struct-type. Pipeline procedures are of type:
(connection? persistent-struct? -> persistent-struct?)
While the entity itself is useful to application programmers in a number of circumstances, most of the information inside the entity does not need to be accessed form application code. define-persistent-struct automatically binds an identifier of the form entity:foo to the entity for each persistent struct type:
| |||
> entity:person | |||
#<entity:person> |
prop:entity : property? |
A structure type property used to attach entity metadata to persistent structure types.
(struct-has-entity? struct) → boolean? |
struct : any |
Returns #t if struct is a structure or structure type that has a value for prop:entity.
Examples: |
> (struct-has-entity? struct:person) |
#t |
> (struct-has-entity? (make-person "Dave" 30)) |
#t |
(struct-entity struct) → entity? |
struct : (U struct? struct-type?) |
Returns the value of prop:entity that is associated with struct.
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
| |||||||||||||||||||||||||||||||||||
name : symbol? | |||||||||||||||||||||||||||||||||||
column-name : symbol? | |||||||||||||||||||||||||||||||||||
entity : entity? | |||||||||||||||||||||||||||||||||||
index : natural? | |||||||||||||||||||||||||||||||||||
accessor : (persistent-struct? -> any) | |||||||||||||||||||||||||||||||||||
mutator : (persistent-struct? any -> void?) | |||||||||||||||||||||||||||||||||||
type : type? |
Metadata describing a particular attribute (or field or column) of an entity.
define-persistent-struct automatically binds an identifier of the form attr:foo-bar for each attribute of each persistent struct type:
> attr:person-id |
#<attr:person-id> |
> attr:person-revision |
#<attr:person-revision> |
> attr:person-name |
#<attr:person-name> |
> attr:person-age |
#<attr:person-age> |
(entity-has-attribute? entity attribute) → boolean? |
entity : entity? |
attribute : (U attribute? symbol?) |
Returns #t if entity has the supplied attribute, #f otherwise. attribute can be an attribute structure or an attribute name.
Examples: |
> (entity-has-attribute? entity:person attr:person-name) |
#t |
> (entity-has-attribute? entity:person 'name) |
#t |
(entity-attribute entity name) → attribute? |
entity : entity? |
name : (U attribute? symbol?) |
Determines if entity has the supplied attribute. Returns the attribute exists; raises exn:fail otherwise.
Examples: |
> (entity-attribute entity:person attr:person-name) |
#<attr:person-name> |
> (entity-attribute entity:person 'name) |
#<attr:person-name> |
> (entity-attribute entity: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 |
(struct (boolean-type type) ()) |
Stores #t and #f values. There is no direct Scheme representation of NULL; NULL values in the database are mapped to #f in Scheme.
(struct (integer-type type) ()) |
Stores integer values. NULL values in the database are mapped to #f in Scheme.
(struct (real-type type) ()) |
Stores real number values. NULL values in the database are mapped to #f in Scheme.
(struct (string-type type) (max-length)) |
max-length : integer? |
Stores string values as VARCHARs or arbitrary length TEXTs. The value of max-length determines the SQL type. NULL values in the database are mapped to #f in Scheme.
(struct (symbol-type type) (max-length)) |
max-length : integer? |
Like string-type but for symbol values.
(struct (time-utc-type type) (max-length)) |
max-length : integer? |
Stores SRFI 19 UTC times as GMT TIMESTAMP WITHOUT TIME ZONEs (or INTEGERs in SQLite). NULL values in the database are mapped to #f in Scheme.
(struct (time-tai-type type) (max-length)) |
max-length : integer? |
Like time-utc-type but for SRFI 19 TAI times.
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:
type:boolean : type? |
type:integer : type? |
type:real : type? |
type:string : type? |
type:symbol : type? |
type:time-utc : type? |
type:time-tai : type? |
4.6 Persistent structure utilities
(struct-attributes struct) → list? |
struct : persistent-struct? |
Returns a list of the values of the attributes of struct (including its ID and revision).
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?) |
Returns #t if struct has an attribute with the supplied name, #f otherwise.
Examples: |
> (struct-has-attribute? dave attr:person-name) |
#t |
> (struct-has-attribute? dave 'age) |
#t |
> (struct-has-attribute? dave 'nom) |
#f |
(struct-attribute struct name) → any |
struct : persistent-struct? |
name : (U attribute? symbol?) |
Returns the value from struct of the attribute with the supplied name. Raises exn:fail:contract if struct does not have a corresponding attribute.
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 |
Mutates struct, setting the value of the attribute with the supplied name.
Examples: |
> (set-struct-attribute! dave attr:person-name "Noel") |
> (set-struct-attribute! dave 'age #f) |
> (struct-attributes dave) |
(#f #f "Noel" #f) |
(set-struct-attributes! struct vals) → void? |
struct : persistent-struct? |
vals : list? |
Mutates struct, setting the values of all its attributes. vals must contain an ID, a revision, and one value for each attribute of struct in the order specified inthe define-persistent-struct form.
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 |
Returns a new persistent of the supplied type, with default values for all attributes except those specified in the arguments. Used in the implementation of the keyword constructor created by define-persistent-struct.
Each attr must be followed by a corresponding value.
Examples: | ||
| ||
#(struct:person #f #f "Noel" #f) |
| |||||||||||||||||||||||||||||||||||
struct : persistent-struct? | |||||||||||||||||||||||||||||||||||
attr : (U attribute? symbol?) | |||||||||||||||||||||||||||||||||||
value : any | |||||||||||||||||||||||||||||||||||
attr2 : (U attribute? symbol?) | |||||||||||||||||||||||||||||||||||
value2 : any |
Returns a new persistent structure with the same type and attributes of struct, except where new values are supplied in the arguments. Used in the implentation of the copy procedure created by define-persistent-struct.
Each attr must be followed by a corresponding value.
| ||||||||
→ persistent-struct? | ||||||||
struct : persistent-struct? | ||||||||
copy : persistent-struct? |
Mutates struct to be the same as copy. Used when rolling back cancelled transactions.
Examples: |
> (copy-persistent-struct dave attr:person-name "Noel") |
#(struct:person #f #f "Noel" 30) |