I am sure I am sure I am missing something easy but, for some reason, when I compare these two values (they are in string format from my data) the comparison isn't correct. I don't seem to have this issue when I use this EVAL statement with other versions I have, but for some reason, this comparison just throws it out of whack:
| eval Status=case("21.3.0.44"<"5.5.5.0","Declining","21.3.0.44"="5.5.5.0","Mainstream","21.3.0.44">"5.5.5.0","Emerging")
The result just shows "21.3.0.44" as always less-than. Please advise if I am missing some caveat I am not aware of. P.S. I tried converting to these to numbers but due to all the decimals in version numbers, the number isn't valid. I suppose I could replace the decimals somehow but thought I would ask first before I try going down this route.
Thanks in Advance!
Good pick! There was a final case statement missing where a part equals the same part of the other version, in which case State became null, having already been set. Should have been
| makeresults
| eval testVersion=split("5.5.5.0;21.3.0.44;21.1.0.133", ";"), myVersion=split("21.3.0.44;5.5.5.0;4.18.19.216;7.999.1.4;5.5.1.3;7.5.5.0;3.5.5.0;104.99.1.1",";")
| mvexpand myVersion
| mvexpand testVersion
| eval testParts=split(testVersion,"."), myParts=split(myVersion,".")
| foreach 0 1 2 3 [ eval tp=mvindex(testParts,<<FIELD>>), mp=mvindex(myParts,<<FIELD>>), State=case(mp<tp, coalesce(State,"Declining"), mp>tp, coalesce(State, "Emerging"), 1==1, State) ]
| fillnull State value="Mainstream"
| table myVersion testVersion State
| sort testVersion
i.e. add in the final case (1==1, State)
I added your test case in the above, which seems to handle all cases now - so you just need that single foreach line and the fillnull following. (I have removed you 'currentState' var).
Ahhh. I knew there must be a "splunky" (i.e. non-iterative) version. Took me a while but here it is.
| makeresults
| eval ver1="1.4.4.1", ver2="1.4.4"
| eval ver1split=split(ver1,"."), ver2split=split(ver2,".")
| eval vercombined=mvzip(ver1split,ver2split)
| eval verdiffs=mvmap(vercombined,tonumber(mvindex(split(vercombined,","),0,0))-tonumber(mvindex(split(vercombined,","),1,1)))
| eval verdiffpos=mvfind(verdiffs,"^[^0]")
| eval verdiffval=mvindex(verdiffs,verdiffpos,verdiffpos)
| eval verdiffsign=case(verdiffval>0,-1,verdiffval<0,1,1=1,0)
| eval verresult=if(isnotnull(verdiffpos),verdiffsign,case(mvcount(ver1split)>mvcount(ver2split),-1,mvcount(ver1split)<mvcount(ver2split),1,1=1,0))
It's ugly as hell but it works for any length of version numbers and doesn't use any iterative features (foreach, map or anything like that). Works only on multivalue fields.
It compares version numbers by finding an index on which one "number" differs from the other. If both are "equal" but one of them has additional components at the end, it's considered a higher version number.
So "1.0.2">"1.0.2", "2.0.1">"1.0.4.2.12", but "1.0.1.2">"1.0.1"
Returns -1 if ver1>ver2, 1 if ver1<ver2 or 0 if they are equal
Two remarks:
1) Can be reworked to use less pipes and less intermediate fields but it'd get sooooo ugly...
2) As a nice side-effect, if you add another base to tonumber() calls, you can compare non-decimal numbered versions.
String comparisons are always lexographic, i.e. 21 is always less than 5 as the string 2 is less than the string 5. If you were to compare the strings 21 with 05 then 21 would be > than 05.
To handle version number comparisons would need something like this (sets up an example data set then does the comparisons - paste this search into the search window)
| makeresults
| eval testVersion="5.5.5.0", myVersion=split("21.3.0.44;5.5.5.0;4.18.19.216;7.999.1.4;5.5.1.3",";")
| mvexpand myVersion
| eval testParts=split(testVersion,"."), myParts=split(myVersion,".")
| foreach 0 1 2 3 [ eval tp=mvindex(testParts,<<FIELD>>), mp=mvindex(myParts,<<FIELD>>), State=case(mp<tp, coalesce(State,"Declining"), mp>tp, coalesce(State, "Emerging")) ]
| fillnull State value="Mainstream"
| table myVersion testVersion State
i.e 4 test versions. This will
Note that foreach will perform checks for 4 parts. If your versions have different numbers of parts, then it will have to work differently.
Thank you so much, this is great! Any thoughts on how to easily do a "05" to "21" comparison? I have to account for these still, even with your great solution, because we are encountering situations where the last part of the version number are misreading still. Is there a way in Splunk to check if it's more than 1 digit string to force it to compare as a number? I'm fairly new to Splunk so I am not sure how that would look.
Example of issue (should be "Declining"):
| makeresults
| eval testVersion="21.1.0.133", myVersion=split("5.5.0.144",";")
| mvexpand myVersion
| eval testParts=split(testVersion,"."), myParts=split(myVersion,".")
| foreach 0 1 2 3 [ eval tp=mvindex(testParts,<<FIELD>>), mp=mvindex(myParts,<<FIELD>>), State=case(mp<tp, coalesce(State,"Declining"), mp>tp, coalesce(State, "Emerging"))
| eval currentState = if(isnull(State),currentState, State)]
| fillnull currentState value="Mainstream"
| table myVersion testVersion currentState
Thanks in advance
Good pick! There was a final case statement missing where a part equals the same part of the other version, in which case State became null, having already been set. Should have been
| makeresults
| eval testVersion=split("5.5.5.0;21.3.0.44;21.1.0.133", ";"), myVersion=split("21.3.0.44;5.5.5.0;4.18.19.216;7.999.1.4;5.5.1.3;7.5.5.0;3.5.5.0;104.99.1.1",";")
| mvexpand myVersion
| mvexpand testVersion
| eval testParts=split(testVersion,"."), myParts=split(myVersion,".")
| foreach 0 1 2 3 [ eval tp=mvindex(testParts,<<FIELD>>), mp=mvindex(myParts,<<FIELD>>), State=case(mp<tp, coalesce(State,"Declining"), mp>tp, coalesce(State, "Emerging"), 1==1, State) ]
| fillnull State value="Mainstream"
| table myVersion testVersion State
| sort testVersion
i.e. add in the final case (1==1, State)
I added your test case in the above, which seems to handle all cases now - so you just need that single foreach line and the fillnull following. (I have removed you 'currentState' var).
Unfortunately, you're limited to harcoded limit of 4 segments in the version number.
And in case of versions of different length but with one being a prefix of the other (like 1.1.0 and 1.1.0.2) it treats them as equal versions.
This works great! I'll mark this as resolved. Just a question on the logic so I understand:
Let me know if I am misunderstanding.
Thanks in advance.