ycl eav, by PYK, is an entity-attribute-value system for Tcl, built upon sqlite, that features a Tcl-ish interface and query capabilities, traces for maintaining constraints on data and reacting in other ways to record updates, and the ability to traverse logical hierarchies in the data.
In an entity-attribute-value system, structure that might otherwise be found in the database schema is in the data instead. ycl eav provides the features needed to store data in this fashion and to use it as if it were structured into a more traditional relational schema.
An entity-attribute value system easily accomodates hierarchical data such as that represented by XML documents. Nodes in logical hierarchies within a dataset can be discovered using eav find. See below for more information.
As a rule, eav routines process records in the order they were entered. This makes it possible to view a snapshot of the data by limiting results to records that preceded some particular record.
eav myeav filename /path/to/some/file
If no filename is provided, the database is created in memory
eav myeav
To create a new entity, assign one or more attributes and provide the empty string as the entity identifier:
set hydrogen [myeav set {} name hydrogen protons 1] set oxygen [myeav set {} name oxygen protons 8 phase gas] set sodium [myeav set {} name sodium protons 11 phase solid]
To remove an entity:
myeav unset $oxygen
To copy an entity:
myeav set {} {*}[myeav get $oxygen]
To set an additional attribute on an entity:
myeav set $hydrogen phase gas
To add multiple records for an attribute, use insert instead of set
set water [myeav set {} component $hydrogen] $water insert component $oxygen
To unset all records of $oxygen where the attribute is "protons":
myeav unset $oxygen protons
To change the value of the last record for some attribute:
myeav set $entity $attribute $value
To increment by 3 the value of the last record for some attribute:
myeav incr $entity $attribute 3
To retrieve the value of an attribute, use set
myeav set $hydrogen component
To retrieve a dictionary of selected values of a entity:
myeav get $hydrogen protons phase
To check if an attribute exists for some entity:
myeav exists $hydrogen phase
If an entity has multiple values for an attribute, only the last value is returned.
To retrieve a list of selected values for an attribute:
myeav list $hydrogen protons phase
To ensure that an entity having a certain profile (set of attributes and associated values) exists:
set nitrogen [myeav ensure name nitrogen protons 7]
dset, dget, and dexists make it possible to process eav records as nested dictionaries:
set Cronus [myeav set {} name Cronus] set Apollo [myeav set {} name Apollo] myeav dset [list $Cronus sons Zeus sons] Apollo $Apollo puts [myeav dset [list $Cronus sons Zeus sons Apollo] name]
The first argument to find is a list of attributes to include in the results, and the remaining arguments are operators and their arguments. Each operator consumes a certain number of arguments, and any subsequent argument is another operator. If all operations are true for some entity, it is included in the result set.
To find the entity identifiers for all entities that have a "phase" attribute:
myeav find {} exists phase
To retrieve a dictionary, keyed by identifier, of all entities that have a "phase" attribute:
myeav find * exists phase
To retrieve only the "protons" and "phase" attributes of such entities:
myeav find {protons phase} exists phase exists protons
But since any attribute mentioned in the first argument implies an "exists" requirement, the previous command is equivalent to:
myeav find {protons phase}
To find entities that have at least 3 protons:
myeav find {} > protons 3
To find the names of entities that have at least 3 protons and a phase of "solid":
myeav find name > protons 3 == phase solid
To find entities that are missing some attribute:
myeav find name > protons 3 is missing phase
To limit results to those that were inserted prior to some record, use the id operator:
myeav find {} id < $id
To find members of logical hierarchies of entities, use the descend operator, which takes the name of an entity to build the hierarchy on, and either an == or an entity operation that selects the entry points into the hierarchy, as well as provides the criteria for selecting children of the current node in the hierarchy. For example with an eav instance named organization:
organization find {} descend boss entity == $myboss
To use a set of criteria instead of an entity to specifiy the entry point(s) into the hierarchy:
organization find {} descend boss name == Neo
find features an eval operator that functions just like the one in the Tcli interface to sqlite:
organization find {} entity == $myboss descend boss eval record { puts [array get $record] }
find usually processes data in the order of insertion. To change this order, use the order operation:
myeav find {} order value
union takes a list of arguments that might be passed to find, and wraps them up into a single database query. for operators that only make sense when given once, the last occurrence is used.
Example:
myeav union {* protons > 3} {* missing phase eval record { puts [array get record] }}
Both write and unset actions can be traced. A trace can specify an attribute, an entity, or both. This results in four kinds of traces:
All traces that match fire, with more general matches firing first. To set a trace:
myeav trace write $id $attribute_name $cmdPrefix
The command prefix has the following signature:
operation entity attribute value
$operation is one of write or unset.