I'm a bit stumped on this problem. Before I jump into the issue, there's a couple of restrictions:
I'm attempting to filter out all elements of a list which match the first element, leaving only the elements which are not a match. Here is an example which does work:
| makeresults
| eval base = split("A75CD,A75AB,A75CD,A75BA,A75DE",",")
| eval mv_to_search=mvindex(base,1,mvcount(base)-1)
| eval search_value=mvindex(base,0)
| eval COMMENT = "ABOVE IS ALL SETUP, BELOW IS ATTEMPTED SOLUTIONS"
| eval filtered_mv=mvfilter(!match(base, "A75CD"))
| eval var_type = typeof(search_value)
| table base, mv_to_search, search_value, filtered_mv, var_type
However, when I attempt to switch it out for something like the following, it does not work:
| makeresults
| eval base = split("A75CD,A75AB,A75CD,A75BA,A75DE",",")
| eval mv_to_search=mvindex(base,1,mvcount(base)-1)
| eval search_value=mvindex(base,0)
| eval COMMENT = "ABOVE IS ALL SETUP, BELOW IS ATTEMPTED SOLUTIONS"
| eval filtered_mv=mvfilter(!match(base, mvindex(base,0)))
| eval var_type = typeof(search_value)
| table base, mv_to_search, search_value, filtered_mv, var_type
I have even attempted to solve it using a foreach command, but was also unsuccessful:
| makeresults
| eval base = split("A75CD,A75AB,A75CD,A75BA,A75DE",",")
| eval mv_to_search=mvindex(base,1,mvcount(base)-1)
| eval search_value=mvindex(base,0)
| eval COMMENT = "ABOVE IS ALL SETUP, BELOW IS ATTEMPTED SOLUTIONS"
| foreach mode=multivalue base [eval filtered_mv = if('<<ITEM>>'!=mvindex(base,0), mvappend(filtered_mv,'<<ITEM>>'), filtered_mv)]
| eval var_type = typeof(search_value)
| table base, mv_to_search, search_value, filtered_mv, var_type
I'm open to any other ideas which might accomplish this better or more efficiently. Not sure where I'm going wrong with this one, or whether this idea is even possible.
If this is practical, you can do it with replace, i.e.
| eval elements = mvjoin(mv_to_search, "####")
| eval non_matches = replace(elements, search_value, ""), non_matches=split(non_matches, "####"), non_matches=mvfilter(isnotnull(non_matches))
which joins the elements with a known string, gets rid of all the matches, then splits again and removes nulls.
@BG_Splunk I you don't have mvmap, you won't have foreach mode=multivalue, but you can use foreach like this
| foreach 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [ | eval e=mvindex(base, <<FIELD>>), filtered_mv=mvappend(filtered_mv, if(!match(e, search_value), e, null())) ]
| eval var_type = typeof(search_value)
| table base, mv_to_search, search_value, filtered_mv, var_type
where you just give incrementing numbers which are templated as <<FIELD>> so you can mvindex using that.
mvfilter can't handle more than one field, so the mvindex(base, 0) won't work inside the filter expression.
I'm still using 7.3 in one environment so the above works in that and I used to use that technique before mvmap came along. It does require you to know the max size of the list in advance, but it doesn't have limits I have come up against.
It may also be possible to collapse the MV to a single value and then use some kind of rex/replace to get the matches out, but I've not tried that.
That's a very smart way to do it! I'm going to need some time to dissect how that works, but I know off the bat that the biggest problem is going to be that the max size of the list is going to be variable each time this runs. Still, I think this is super clever and will keep this in mind. 🙂
If you always know what the upper max list size will be then you can put foreach numbers 0 1...999999 if you really needed as nothing will happen for those outside the actual size of the MV.
If you're doing this in a dashboard, you could technically create a token with the numbered steps and use the token in the foreach, e.g.
| foreach $steps_to_iterate$
where steps to iterate is calculated in a post process search of the list and simply
| stats max(eval(mvcount(list))) as max_list
| eval r=mvjoin(mvrange(1, max_list + 1, 1), " ")
with this <done> clause in the dashboard search
<done>
<set token="steps_to_iterate">$result.max_list$</set>
</done>
If this is practical, you can do it with replace, i.e.
| eval elements = mvjoin(mv_to_search, "####")
| eval non_matches = replace(elements, search_value, ""), non_matches=split(non_matches, "####"), non_matches=mvfilter(isnotnull(non_matches))
which joins the elements with a known string, gets rid of all the matches, then splits again and removes nulls.
My guy, that's a super smart solution! Thank you very much, I just tried this out and it works beautifully. 🙂
I'm going to have to keep this kind of approach in mind as I go forward with this project. Very creative thinking!