Key

The key constraint restricts a value to one of the keys stored in a named index. These indexes are declared using vr_key, as described in Indexes, Keys and References.

This constraint is primarily used to enforce referential integrity within a configuration document—for example, ensuring that a reference in one section corresponds to a defined identifier in another section.

Type Matrix

The following table summarizes how the key constraint applies to different node types.

Node Type

S

Value Type

Details

Integer

Text
ValueList[Text]

Constrains the integer value to reference a key from one of the specified indexes.

Boolean

Float

Text

Text
ValueList[Text]

Constrains the text value to reference a key from one of the specified indexes.

Date

Time

DateTime

Bytes

TimeDelta

RegEx

Value

ValueList

ValueMatrix

Section

SectionList

SectionWithTexts

NotValidated

Rules for Key

  1. Index Reference: The key constraint takes one or more text values. Each value names an index that the node value is allowed to reference.

    # ...
    
    [app.start_filter]
    type: "text"
    key: "filter"
    
    # ...
    
    [app.start_action]
    type: "text"
    key: "local_action_id", "remote_action_id"
    
  2. Index Must Exist: Each referenced index must be defined and visible within the same Validation Rules branch using vr_key.

    *[vr_key]*
    name: "filter"
    key: "filter.vr_entry.identifier"
    
    [filter]
    type: "SectionList"
    
    [filter.vr_entry.identifier]
    type: "text"
    
    [app.start_filter]
    type: "text"
    key: "filter"
    
  3. Multiple Indexes (OR Semantics): If multiple index names are specified, the value is valid if it exists in any of the listed indexes.

    [app.start]
    type: "text"
    key: "remote_action", "local_action"
    
  4. Type Alignment: The type of the referencing node must match the type of the keys stored in the referenced index.

    For example, an index built from text identifiers cannot be referenced by a node of type date.

    *[vr_key]*
    name: "filter"
    key: "filter.vr_entry.identifier"
    
    [filter]
    type: "SectionList"
    
    [filter.vr_entry.identifier]
    type: "text"
    
    [app.start_filter]
    type: "date"
    key: "filter"   # ERROR: Referenced keys are text, not date
    
  5. Case Sensitivity: Key comparison semantics are determined exclusively by the case-sensitivity configuration of the referenced index.

    Any case_sensitive flag defined on the referencing node is ignored for the purpose of key resolution.

    *[vr_key]*
    name: "filter"
    key: "filter.vr_entry.identifier"
    case_sensitive: false
    
    [filter]
    type: "SectionList"
    
    [filter.vr_entry.identifier]
    type: "text"
    
    [app.start_filter]
    type: "text"
    case_sensitive: true
    key: "filter"
    
    *[filter]*
    identifier: "first"
    
    *[filter]*
    identifier: "second"
    
    [app]
    start_filter: "First"
    # VALID: matches "first"
    # The index is case-insensitive.
    # The referencing node's case_sensitive flag is ignored.
    
  6. Index Scope Is Hierarchical: Indexes defined in sibling branches are not visible to the referencing node.

    An index is resolved by searching upward in the Validation Rules tree from the referencing node. Only indexes defined in the same branch or in ancestor branches are visible.

    [server.connections]
    type: "SectionList"
    
    # ...
    
    *[server.vr_key]*
    name: "connection_id"
    key: "connections.vr_entry.id"
    
    [app.main_connection]
    type: "text"
    key: "connection_id"  # ERROR: No such index in this scope
    
  7. Nearest Ancestor Resolution: If multiple indexes with the same name exist in accessible branches, the index defined in the nearest ancestor branch is used.

    Index resolution proceeds upward from the referencing node until a matching index name is found. Once found, that index is used and the search stops.

    In the following example, two indexes named id are defined. The reference resolves to the index defined at server.vr_key, because it is closer in the tree than the index defined at the document root.

    # ...
    
    *[vr_key]*
    name: "id"
    key: "log.vr_entry.id"
    
    [server.connections]
    type: "SectionList"
    
    *[server.vr_key]*
    name: "id"
    key: "connections.vr_entry.id"
    
    # ...
    
    [server.filter.vr_entry.connection_id]
    type: "text"
    key: "id"   # Resolves to 'server.vr_key'
    

Example

In the following example, start_filter must reference one of the identifiers defined in the filter section list:

*[vr_key]*
name: "filter"
key: "filter.vr_entry.identifier"

[filter]
type: "SectionList"

[filter.vr_entry.identifier]
type: "text"

[app.start_filter]
type: "text"
key: "filter"
*[filter]*
identifier: "first"

*[filter]*
identifier: "second"

[app]
start_filter: "first"
*[filter]*
identifier: "first"

*[filter]*
identifier: "second"

[app]
start_filter: "third"  # ERROR: "third" is not defined

Version History

Changed in version 1.3.0: Clarified that key comparison behavior is determined exclusively by the referenced index.

The case_sensitive setting of the referencing node is ignored. Uniqueness checks and key constraint comparisons now consistently follow the case-sensitivity defined by the index.