Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pkg/ottl] Add contains function #33078

Closed

Conversation

lkwronski
Copy link
Contributor

Description: Adds a new contains function to check if slice contains item.

Link to tracking Issue: Fixes #30420

Testing: Added unit test

Documentation: TODO

)

type ContainsArguments[K any] struct {
Target []ottl.StringLikeGetter[K]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this won't allow you to pass a slice, consider this use case
contains(attributes["tags"], "staging")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm wrong, but I tried to write e2e test and it seems that the following statement works and I can pass the slices:

set(attributes["test"], "pass") where Contains(["hello", "world"], "hello")

Did I miss anything?

Copy link
Contributor

@michalpristas michalpristas May 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the thing is you're just assuming StringLikeGetter,
when I want to use config like this

 - context: log
        statements: 
          - set(attributes["tags"], ["staging", "hello", "world", "work"])
          - set(attributes["staging"], "true") where Contains(attributes["tags"], "staging")

Contains fail because first attribute is not an array.
so i need to fix it like this

    log_statements:
      - context: log
        statements: 
          - set(attributes["tags"], ["staging", "hello", "world", "work"])
          - set(attributes["staging"], "true") where Contains([attributes["tags"]], "staging")

by wrapping attributes["tags"] with [] to make it an array.
when i do that, your StringLikeGetter will get the attributes["tags"] and finds out it's a Slice. So it makes a string out of slice by marshaling the array.
Now you have marshaled array as a string["staging", "hello", "world", "work"] that does not equal staging in my condition.

What you need to do is to use Getter and work with different types you may get. You may get Slice, Map or a string wrapped in pcommon.Value. You should handle all of these differently.

While you're at it changing behavior to match what i described i would be the same way to add support for ints, floats and boolean as you were asking in the other comment

It will also fix UX.
I want to use Contains(["a", "b"...] and Contains(attributes["tags"] at the same time without wrapping workaround

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really appreciate the detailed answer that helps me understand the flow. Thanks.

I added e2e with the scenario you described. I hope it is better now.

@lkwronski lkwronski force-pushed the lkwronski.issue-30420-contains branch 2 times, most recently from a43631c to 4ef84a9 Compare May 19, 2024 08:51
@lkwronski lkwronski force-pushed the lkwronski.issue-30420-contains branch from 4ef84a9 to 2809a2f Compare May 19, 2024 09:07
}

// converts a generic slice to pcommon.Slice. It uses reflection to handle any slice type, creating a pcommon.Slice from it.
func convertToSlice(val any) (pcommon.Slice, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure i like that, especially when we have so few types we support

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I removed the reflection and it simplified things a bit.

pkg/ottl/expression.go Show resolved Hide resolved
pkg/ottl/expression.go Show resolved Hide resolved
pkg/ottl/ottlfuncs/README.md Outdated Show resolved Hide resolved
pkg/ottl/ottlfuncs/func_contains.go Outdated Show resolved Hide resolved
pkg/ottl/ottlfuncs/func_contains.go Outdated Show resolved Hide resolved
Copy link
Contributor

github-actions bot commented Jun 5, 2024

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added the Stale label Jun 5, 2024
@evan-bradley evan-bradley removed the Stale label Jun 5, 2024
Copy link
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added the Stale label Jun 20, 2024
@lkwronski
Copy link
Contributor Author

@evan-bradley Is there anything else to do in this PR?

@evan-bradley
Copy link
Contributor

@lkwronski I'm sorry, I lost track of this. It looks good to me.

@TylerHelmuth Please take a look.

Comment on lines +472 to +477
case strings.HasPrefix(name, "PSliceGetter"):
arg, err := p.newGetter(argVal)
if err != nil {
return nil, err
}
return StandardPSliceGetter[K]{Getter: arg.Get}, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the new Getter requires some new tests in functions_test.go that hit this case

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
)

func Test_contains(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test for the item being a pcommon.Value, pcommon.Map, and pcommon.Slice. I suspect those cases are not going to work bc we take the item as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added the Stale label Jul 16, 2024
if val == item {
return true, nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although functionally correct and generic enough to cover multiple data types, this approach can become prohibitively expensive for performance sensitive applications under load. For example, if this approach was used for supporting routing-connector in open-telemetry collector to inspect 100+ string values while processing 100,000 spans per second kind of workload. A map would be much more efficient for such kind of conditions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to clarify, when i say multiple data types.... I meant to appreciate that it can handle integer, strings, booleans alike.... However I am concerned that outer type has to be a slice only... which may not be the optimal choice for heavy workloads of say, simple string based look up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your concern, but I'm not sure how to do it with a map. If we want to handle Contains that accept a slice as input, I don't see how to convert it to a map in linear time.

If we accepted map as input in Contains, it would be easy to get an efficient search, but I'm not sure how to do it with slice.

@github-actions github-actions bot removed the Stale label Jul 17, 2024
},
item: ottl.StandardGetSetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "unknow", nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if it was intentional to make this a typo error... you probably meant to write unknown

@rpasche
Copy link

rpasche commented Jul 22, 2024

Looking forward to become this function "alive". I also have a use case for this

@francois07
Copy link

Any news on this function @lkwronski ? I'm really looking forward to using it

Copy link
Contributor

github-actions bot commented Aug 9, 2024

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added the Stale label Aug 9, 2024
@rpasche
Copy link

rpasche commented Aug 9, 2024

Any news on this? This function will be very handy.

@github-actions github-actions bot removed the Stale label Aug 10, 2024
Copy link
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added the Stale label Aug 24, 2024
Copy link
Contributor

github-actions bot commented Sep 8, 2024

Closed as inactive. Feel free to reopen if this PR is still being worked on.

@github-actions github-actions bot closed this Sep 8, 2024
@lkwronski
Copy link
Contributor Author

Sorry for the long response time, but I finally have time to finalize this PR. @TylerHelmuth Can you re-open this PR? I have already pushed commit to my branch, but it isn't visible here due to closed PR lkwronski@417027d.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ottl: Efficiently test if a value is in a list or map
8 participants