-
Notifications
You must be signed in to change notification settings - Fork 257
Description
Feature request checklist
- There are no issues that match the desired change
Change
Add deep merge support for nested map structures in the CEL maps library.
Currently, map.merge only performs a shallow merge: when two maps share the same key whose value is also a map, the value from the second map completely replaces the value from the first map. This makes it hard to work with configuration-like structures that naturally contain nested maps and lists (for example, Kubernetes-style objects).
The requested change is to provide a deep merge operation, either as:
- A new function (e.g.
mergeDeep,deepMerge, or similar), or - An additional mode/parameter on the existing
mergefunction,
with the following behavior:
-
Non-map values: values from the second map overwrite values from the first map (same behavior as current shallow
merge). -
Map values: if both maps contain map values for the same key, recursively merge those nested maps instead of replacing them entirely.
-
List values containing maps: support merging list items that represent configuration objects, by matching on an identifying key (e.g.
namefor Kubernetes containers).- When two list elements share the same identifier, deep merge those elements.
- When identifiers differ, keep all elements (concatenate the lists).
-
Lists of primitive values: behavior could be configurable (concatenate vs. replace) or follow a simple, documented default.
This feature would significantly improve ergonomics when expressing configuration overlays and patches directly in CEL.
Example
Nested maps:
// Current behavior (shallow merge)
{'a': {'x': 1, 'y': 2}}.merge({'a': {'y': 3, 'z': 4}})
// => {'a': {'y': 3, 'z': 4}}
// Desired deep merge behavior
{'a': {'x': 1, 'y': 2}}.mergeDeep({'a': {'y': 3, 'z': 4}})
// => {'a': {'x': 1, 'y': 3, 'z': 4}}
Lists of maps (e.g. Kubernetes containers):
// Current behavior (shallow merge)
{'containers': [{'name': 'app', 'image': 'v1'}]}.merge({
'containers': [
{
'name': 'app',
'env': [{'name': 'DEBUG', 'value': 'true'}],
},
],
})
// => {'containers': [{'name': 'app', 'env': [{'name': 'DEBUG', 'value': 'true'}]}]}
// Desired deep merge behavior
{'containers': [{'name': 'app', 'image': 'v1'}]}.mergeDeep({
'containers': [
{
'name': 'app',
'env': [{'name': 'DEBUG', 'value': 'true'}],
},
],
})
// => {'containers': [
// {'name': 'app', 'image': 'v1',
// 'env': [{'name': 'DEBUG', 'value': 'true'}],
// },
// ]}
Alternatives considered
- Performing deep merge logic manually in CEL expressions using combinations of
has,map,filter, andfold– this quickly becomes verbose and error-prone for non-trivial structures. - Implementing deep merging outside of CEL (e.g. in the host language) before or after evaluation – this reduces the benefit of CEL as a configuration/patching language and complicates pipelines.
- Encoding nested configuration in a flatter structure to avoid deep merges – this makes expressions and schemas less natural and harder to read/maintain.