Datum supports multiple merge strategies that control how values from different hierarchy layers are combined. This document covers all merge behaviours, data types, and configuration options.
When merging, Datum classifies values into four types:
| Type | Description | Examples |
|---|---|---|
| BaseType | Scalar/simple values | string, int, bool, DateTime, PSCredential |
| Hashtable | Hashtable or OrderedDictionary | @{ Key = 'Value' } |
| baseType_array | Array of scalars | @('a', 'b', 'c') |
| hash_array | Array of hashtables | @(@{ Name = 'x' }, @{ Name = 'y' }) |
An array is classified as hash_array if it can be cast as [hashtable[]]; otherwise it is a baseType_array.
Datum provides three named presets that set defaults for all merge behaviours:
| Preset | Aliases | merge_hash | merge_baseType_array | merge_hash_array | knockout_prefix |
|---|---|---|---|---|---|
| MostSpecific | First |
MostSpecific | MostSpecific | MostSpecific | (none) |
| hash | MergeTopKeys |
hash | MostSpecific | MostSpecific | -- |
| deep | MergeRecursively |
deep | Unique | DeepTuple | -- |
Returns the first value found in the hierarchy — the most specific layer wins. No merging occurs.
Merges top-level keys of hashtables. If the same key exists in multiple layers, the most specific value wins. Keys that only exist in less-specific layers are included.
# Role (generic)
NetworkConfig:
DNSServer: 10.0.0.1
Gateway: 10.0.0.254
SubnetMask: 255.255.255.0
# Node (specific)
NetworkConfig:
DNSServer: 192.168.1.1With hash strategy, the result is:
NetworkConfig:
DNSServer: 192.168.1.1 # from Node (override)
Gateway: 10.0.0.254 # from Role (kept)
SubnetMask: 255.255.255.0 # from Role (kept)Like hash, but recursively merges nested hashtables and merges arrays. This is the most comprehensive merge strategy.
Base types (scalars) always use MostSpecific — the first value found wins.
# Role
Timezone: 'UTC'
# Node
Timezone: 'Pacific Standard Time'Result: Pacific Standard Time (from Node, the most specific layer).
| Strategy | Behaviour |
|---|---|
| MostSpecific | Return the entire hashtable from the most specific layer |
| hash | Merge top-level keys; most specific value wins per key |
| deep | Recursively merge all nested keys |
| Strategy | Behaviour |
|---|---|
| MostSpecific | Return the array from the most specific layer |
| Unique | Combine arrays, removing duplicates |
| Sum | Concatenate arrays (may contain duplicates) |
Example with Unique:
# Role
WindowsFeatures:
- Telnet-Client
- File-Services
- Web-Server
# Node
WindowsFeatures:
- Web-Server
- SMTP-ServerResult: Telnet-Client, File-Services, Web-Server, SMTP-Server (union, duplicates removed).
Hash arrays — arrays of hashtables — have more sophisticated merge options using tuple keys to match items across layers.
| Strategy | Behaviour |
|---|---|
| MostSpecific | Return the array from the most specific layer |
| Sum | Concatenate arrays |
| UniqueKeyValTuples | Combine arrays, de-duplicating by tuple key values |
| DeepTuple | Match items by tuple keys and deep-merge their properties |
When using UniqueKeyValTuples or DeepTuple, you must specify which keys identify matching items:
lookup_options:
Packages:
merge_hash_array: DeepTuple
merge_options:
tuple_keys:
- Name# Role
Packages:
- Name: NotepadPlusplus
Version: '7.0'
Ensure: Present
- Name: Putty
Ensure: Present
# Node
Packages:
- Name: NotepadPlusplus
Version: '8.0'With DeepTuple and tuple_keys: [Name], items with the same Name are matched and deep-merged:
Packages:
- Name: NotepadPlusplus
Version: '8.0' # overridden by Node
Ensure: Present # kept from Role
- Name: Putty
Ensure: Present # kept from Role (no Node override)Like DeepTuple, but matched items are replaced entirely rather than deep-merged. Items from the most specific layer take priority.
Set the default strategy for all lookups in Datum.yml:
default_lookup_options: MostSpecificOverride the strategy for specific keys:
lookup_options:
Configurations: Unique
NetworkConfig: hash
Packages:
merge_hash_array: DeepTuple
merge_options:
tuple_keys:
- NameFor fine-grained control, specify individual merge behaviours:
lookup_options:
MyKey:
merge_hash: deep
merge_basetype_array: Unique
merge_hash_array: DeepTuple
merge_options:
knockout_prefix: '--'
tuple_keys:
- Name
- VersionKeys starting with ^ are treated as regex patterns:
lookup_options:
^LCM_Config\\.*: deep
^Security\\.*:
merge_hash: deep
merge_basetype_array: UniqueExact key matches are always preferred over regex matches.
The knockout prefix (default: --) removes items during merge. It is enabled by the hash and deep presets, or can be configured per key.
# Role (generic)
WindowsFeatures:
- Telnet-Client
- File-Services
- Web-Server
# Node (specific, with knockout)
WindowsFeatures:
- --Telnet-ClientWith Unique or Sum merge strategy, the result excludes Telnet-Client:
WindowsFeatures:
- File-Services
- Web-ServerBoth the knockout item (--Telnet-Client) and the matching original (Telnet-Client) are removed from the result.
Prefix a key with -- to remove it during hash merge:
# Role
Settings:
FeatureA: enabled
FeatureB: enabled
FeatureC: enabled
# Node
Settings:
--FeatureB:Result: FeatureA and FeatureC remain; FeatureB is removed.
When merging hash arrays with tuple-based strategies, prefix the tuple key value:
# Role
Packages:
- Name: NotepadPlusplus
- Name: Putty
- Name: Git
# Node
Packages:
- Name: --PuttyResult: NotepadPlusplus and Git remain; Putty is removed.
Merge strategies apply at the lookup level. If you want merged data within a nested key, you must declare strategies at each level:
lookup_options:
SoftwareBaseline: hash # merge top-level keys of SoftwareBaseline
SoftwareBaseline\Packages: # also merge the nested Packages array
merge_hash_array: DeepTuple
merge_options:
tuple_keys:
- NameWhy this matters:
Without the SoftwareBaseline: hash entry, a lookup of SoftwareBaseline returns the most specific value without merging — and the Packages merge rule is never reached.
However, a direct lookup of SoftwareBaseline\Packages works because it bypasses the top-level and looks up the nested key directly.
# These can return different results depending on merge configuration:
Lookup 'SoftwareBaseline' # governed by SoftwareBaseline strategy
(Lookup 'SoftwareBaseline').Packages # no deep merge applied to Packages
Lookup 'SoftwareBaseline\Packages' # governed by SoftwareBaseline\Packages strategy