'''`[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 [graph%|%hierarchies] in the data. ** Description ** 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. ** Creating an Eav ** ====== eav myeav filename /path/to/some/file ====== If no filename is provided, the database is created in memory ====== eav myeav ====== ** Creating Entities ** 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] ====== ** Removing Entities ** To remove an entity: ====== myeav unset $oxygen ====== ** Copying Entities ** To copy an entity: ====== myeav set {} {*}[myeav get $oxygen] ====== ** Adding Attributes ** 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 ====== ** Removing Attributes ** To unset all records of `$oxygen` where the attribute is "protons": ====== myeav unset $oxygen protons ====== ** Modifying Attribute Values ** 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 ====== ** Retrieving Values ** 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 ====== ** Ensuring Such an Entity ** To ensure that an entity having a certain profile (set of attributes and associated values) exists: ====== set nitrogen [myeav ensure name nitrogen protons 7] ====== ** Finding Entities ** 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 ====== ** Finding Members of Logical Hierarchies ** 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 ====== ** The `eval` operator ** `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] } ====== ** Ordering Results ** `find` usually processes data in the order of insertion. To change this order, use the `order` operation: ====== myeav find {} order value ====== ** Union ** `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] }} ====== ** Traces ** 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: * Any attribute of any entity. * Any attribute of a particular entity. * A particular attribute of any entity. * A particular attribute of a particular entity. 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`. <> database | dict