![]() |
libimgdoc2
|
The implementation is providing a hierarchical key-value store. The mapping of hierarchy to table is by following the "Adjacency List" pattern (c.f. here).
The table layout is as follows:
We have the following columns:
| Field | Type | Description |
|---|---|---|
| Pk | INTEGER | The primary key. |
| Name | TEXT NOT NULL | Name of the node. |
| AncestorId | INTEGER | Id identifying the ancestor (or parent) of the node. If NULL this is a top-level node (i.e. the ancestor is the root). |
| TypeDiscriminator | INTEGER | Specifies the type of the node. Currently defined values are given below. |
| ValueDouble | REAL | If indicated by discriminator, data of type 'double' is found here. |
| ValueInteger | INTEGER | If indicated by discriminator, data of type 'integer' is found here. |
| ValueString | TEXT | If indicated by discriminator, data of type 'text' is found here. |
Currently defined discriminator values are:
| Value | Name | Description |
|---|---|---|
| 0 | Null | Undefined |
| 1 | Int32 | 32-bit signed integer |
| 3 | doublefloat | Double precision floating point number |
| 5 | string | UTF8-encoded text |
| 6 | JSON | JSON-formatted text |
What this offers is a way to store a tree of nodes, where each node has a name, a type and a value. The value can be of type 'double', 'integer' or 'string'. The type of the node is stored in the TypeDiscriminator column. The value is stored in the corresponding column. The name of the node is stored in the Name column. The AncestorId column is used to store the id of the parent node. The top-level nodes have AncestorId set to NULL (and can be thought of "child nodes of root", where this conceptual "root node" does not exist in the table).
As an example, consider the following tree:
This would be stored in the following table:
Main motivation for this design is to allow for a hierarchical structure, which allows for a natural way to structure the metadata. Still, it allows for fast fine-grained access to the data, as the data is stored in a single table.
Read access is provided by the IDocumentMetadataRead interface.
The method GetItem allows to retrieve a single item by its primary key. The argument flags allows to specify which pieces of information are to be retrieved. The returned structure DocumentMetadataItem contains the information that was retrieved, and it describes which fields are actual valid. The caller should only assume that the fields that were requested are valid.
For performance reasons, the caller should only request the information that is actually needed.
The structure DocumentMetadataItem contains the following fields:
The field flags informs which of the other fields are valid. complete_path is the path to the item, starting from the (conceptual) root node.
GetItemForPath allows to retrieve a single item by its path. The path is a string that describes the path to the item. The path is a string that is constructed by concatenating the names of the nodes, separated by a slash. The path is relative to the (conceptual) root node. The path is case-sensitive. The path is not allowed to start with a slash. The path is not allowed to end with a slash. The path is not allowed to contain two (or more) consecutive slashes (and an empty string is not allowed as the name of a node).
EnumerateItems is used to enumerate all items that are children of a given parent node. The argument parent specifies the parent node. If parent is std::nullopt, then the root node is used. The argument recursive specifies whether the enumeration should be recursive (i.e. also include all children of the direct children). The argument flags specifies which information should be retrieved for each item. The argument func is a function that is called for each item. The function is called with the primary key of the item and the item itself. If the function returns false, the enumeration is stopped. If the function returns true, the enumeration continues. The enumeration is performed in a depth-first manner.
EnumerateItemsForPath is similar to EnumerateItems, but it takes a path as argument instead of a primary key. The path is a string that describes the path to the item (identifying the parent node for the enumeration). An empty string is used to specify the root node.
Write access is provided by the IDocumentMetadataWrite interface.
UpdateOrCreateItem allows to update or create a single item. The argument parent specifies the parent node. If parent is std::nullopt, then the root node is used. The node to be inserted or updated is specified by the argument 'name' - it is the name of a node which is a direct child of the node specified by parent. The argument create_node_if_not_exists controls whether the node should be created if it does not exist. If the node does not exist, and create_node_if_not_exists is false, then an exception is thrown. If the node does not exist, and create_node_if_not_exists is true, then the node is created.
UpdateOrCreateItemForPath is similar to UpdateOrCreateItem, but it takes a path as argument instead of a primary key. It has an additional argument create_path_if_not_exists which controls whether the path (or parts of the path) should be created if they do not exist. In this case nodes with a type DocumentMetadataType::kNull are created.
DeleteItem is used to delete one or more items. If the boolean argument recursively is false, then only the item specified by primary_key is deleted, and it is only deleted if it has no children (otherwise an exception is thrown). If the boolean argument recursively is true, then the item specified by primary_key are deleted and all children (direct and indirect) are deleted as well.
DeleteItemForPath is similar to DeleteItem, but it takes a path as argument instead of a primary key.