Skip to content

Commit 0e51fec

Browse files
edmocostadd-jasminesun
authored andcommitted
[pkg/ottl] Add new ottl.ValueComparator API and support for slices comparison (open-telemetry#40370)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This PR introduces two related changes and contains 2 change logs because of that. **1 - Comparator API** Exposes the internal OTTL comparators logic as a new API (`ottl.ValueComparator`), which can be used by API consumers to compare raw values following the same OTTL [comparison rules ](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/ottl/LANGUAGE.md#comparison-rules). **Why?** Existing and new functions that needs to compare values can be leveraging this new API to compare them, and keep it consistent with the OTTL comparison logic. For example, the new [`Contains`](open-telemetry#40193) function for slices, can be using this API to determine whether an slice contains a particular value, following the same comparison logic the grammar does. ```go // Exported interface: type ValueComparator interface { // Equal compares two values for equality, returning true if they are equals // according to the OTTL comparison rules. Equal(a any, b any) bool // NotEqual compares two values for equality, returning true if they are different // according to the OTTL comparison rules. NotEqual(a any, b any) bool // Less compares two values, returning true if the first value is less than the second // value, using the OTTL comparison rules. Less(a any, b any) bool // LessEqual compares two values, returning true if the first value is less or equal // to the second value, using the OTTL comparison rules. LessEqual(a any, b any) bool // Greater compares two values, returning true if the first value is greater than the // second value, using the OTTL comparison rules. Greater(a any, b any) bool // GreaterEqual compares two values, returning true if the first value is greater or // equal to the second value, using the OTTL comparison rules. GreaterEqual(a any, b any) bool } // Usage: comp := ottl.NewValueComparator() ``` **2 - Add ability to compare slices** We currently don't have the ability to compare slices, which means conditions like `attributes["slice"] == attributes["slice"]` returns false. This PR also adds the ability to compare slices/pcommon.Slices, similar to the maps support (open-telemetry#38611). <!--Describe what testing was performed and which tests were added.--> #### Testing Manual and unit tests <!--Describe the documentation added.--> #### Documentation Updated LANGUAGE.md
1 parent 0e6a850 commit 0e51fec

File tree

6 files changed

+272
-58
lines changed

6 files changed

+272
-58
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add ability to compare slices/pcommon.Slice in OTTL Boolean Expressions
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [40370]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/ottl
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Add `ottl.ValueComparator` API to allow comparing values using the OTTL comparison rules"
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [40370]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
This change introduces a new API (`ottl.NewValueComparator`) that allows users to
20+
compare raw values using the OTTL comparison rules. It is useful for implementing
21+
custom logic in OTTL functions where value comparison and consistency is desired.
22+
23+
# If your change doesn't affect end users or the exported elements of any package,
24+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
25+
# Optional: The change log or logs in which this entry should be included.
26+
# e.g. '[user]' or '[user, api]'
27+
# Include 'user' if the change is relevant to end users.
28+
# Include 'api' if there is a change to a library API.
29+
# Default: '[user]'
30+
change_logs: [api]

pkg/ottl/LANGUAGE.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -274,18 +274,20 @@ A `not equal` notation in the table below means that the "!=" operator returns t
274274

275275
The `time.Time` and `time.Duration` types are compared using comparison functions from their respective packages. For more details on how those comparisons work, see the [Golang Time package](https://pkg.go.dev/time).
276276

277-
| base type | bool | int64 | float64 | string | Bytes | nil | time.Time | time.Duration | map[string]any | pcommon.Map |
278-
|----------------|-------------|---------------------|---------------------|---------------------------------|--------------------------|------------------------|--------------------------------------|------------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------------|
279-
| bool | normal, T>F | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
280-
| int64 | not equal | compared as largest | compared as float64 | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
281-
| float64 | not equal | compared as float64 | compared as largest | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
282-
| string | not equal | not equal | not equal | normal (compared as Go strings) | not equal | not equal | not equal | not equal | not equal | not equal |
283-
| Bytes | not equal | not equal | not equal | not equal | byte-for-byte comparison | []byte(nil) == nil | not equal | not equal | not equal | not equal |
284-
| nil | not equal | not equal | not equal | not equal | []byte(nil) == nil | true for equality only | not equal | not equal | not equal | not equal |
285-
| time.Time | not equal | not equal | not equal | not equal | not equal | not equal | uses `time.Equal()`to check equality | not equal | not equal | not equal |
286-
| time.Duration | not equal | not equal | not equal | not equal | not equal | not equal | not equal | uses `time.Before()` and `time.After` for comparison | not equal | not equal |
287-
| map[string]any | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | uses reflect.DeepEqual for comparison | convert to raw map and uses reflect.DeepEqual for comparison |
288-
| pcommon.Map | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | convert to raw map and uses reflect.DeepEqual for comparison | uses pcommon.Map Equal for comparison |
277+
| base type | bool | int64 | float64 | string | Bytes | nil | time.Time | time.Duration | map[string]any | pcommon.Map | []any | pcommon.Slice |
278+
|----------------|-------------|---------------------|---------------------|---------------------------------|--------------------------|------------------------|--------------------------------------|------------------------------------------------------|--------------------------------------------------------------|--------------------------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------|
279+
| bool | normal, T>F | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
280+
| int64 | not equal | compared as largest | compared as float64 | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
281+
| float64 | not equal | compared as float64 | compared as largest | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
282+
| string | not equal | not equal | not equal | normal (compared as Go strings) | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal |
283+
| Bytes | not equal | not equal | not equal | not equal | byte-for-byte comparison | []byte(nil) == nil | not equal | not equal | not equal | not equal | not equal | not equal |
284+
| nil | not equal | not equal | not equal | not equal | []byte(nil) == nil | true for equality only | not equal | not equal | not equal | not equal | not equal | not equal |
285+
| time.Time | not equal | not equal | not equal | not equal | not equal | not equal | uses `time.Equal()`to check equality | not equal | not equal | not equal | not equal | not equal |
286+
| time.Duration | not equal | not equal | not equal | not equal | not equal | not equal | not equal | uses `time.Before()` and `time.After` for comparison | not equal | not equal | not equal | not equal |
287+
| map[string]any | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | uses reflect.DeepEqual for comparison | convert to raw map and uses reflect.DeepEqual for comparison | not equal | not equal |
288+
| pcommon.Map | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | convert to raw map and uses reflect.DeepEqual for comparison | uses pcommon.Map Equal for comparison | not equal | not equal |
289+
| []any | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | uses reflect.DeepEqual for comparison | convert to raw slice and uses reflect.DeepEqual for comparison |
290+
| pcommon.Slice | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | not equal | convert to raw slice and uses reflect.DeepEqual for comparison | uses pcommon.Slice Equal for comparison |
289291

290292
Examples:
291293
- `name == "a name"`

pkg/ottl/boolean_value.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func (p *Parser[K]) newComparisonEvaluator(comparison *comparison) (BoolExpr[K],
8484
return BoolExpr[K]{}, err
8585
}
8686

87+
comparator := NewValueComparator()
8788
// The parser ensures that we'll never get an invalid comparison.Op, so we don't have to check that case.
8889
return BoolExpr[K]{func(ctx context.Context, tCtx K) (bool, error) {
8990
a, leftErr := left.Get(ctx, tCtx)
@@ -94,7 +95,7 @@ func (p *Parser[K]) newComparisonEvaluator(comparison *comparison) (BoolExpr[K],
9495
if rightErr != nil {
9596
return false, rightErr
9697
}
97-
return p.compare(a, b, comparison.Op), nil
98+
return comparator.compare(a, b, comparison.Op), nil
9899
}}, nil
99100
}
100101

0 commit comments

Comments
 (0)