小ネタ
| makeresults
| eval _raw="time,duration,rollno
2016-08-07 21:13:00,10.07 9.56 7.35 12.32,10 20 30 40"
| multikv forceheader=1
| table time duration rollno
| eval duration=split(duration," "), rollno=split(rollno," ")
`comment("this is sample data")`
time | duration | rollno |
---|---|---|
2016-08-07 21:13:00 | 10.07 9.56 7.35 12.32 |
10 20 30 40 |
こんなデータが在った時の分割方法
期待する結果は
_time | duration | rollno |
---|---|---|
2016/08/07 21:13:00 | 10.07 | 10 |
2016/08/07 21:13:00 | 9.56 | 20 |
2016/08/07 21:13:00 | 7.35 | 30 |
2016/08/07 21:13:00 | 12.32 | 40 |
#素直な考え方
mvzip
でmutivalueフィールドをくっつけて、mvexpand
して、あとは元のフィールド抽出
...
| eval zipped=mvzip(duration,rollno)
| mvexpand zipped
| eval duration=mvindex(split(zipped,","),0), rollno=mvindex(split(zipped,","),1)
| table _time duration rollno
これの欠点はフィールドがたくさんだとmvzipをネストしないといけないこと
#カウンターを使ってとりだし
一つフィールドを選んでmvexpnad
。streamstats
でカウンターを作って、あとは元のフィールド抽出
...
| mvexpand rollno
| streamstats count
| eval duration=mvindex(duration,count - 1)
| table _time duration rollno
multivalueのフィールドは順番が保持されることを利用したクエリー。
stats value(X) as X
とかでフィールドをたくさん作った場合に使えると思う。
#イベントのカウンター処理
2020/1/19 修正。
| makeresults count=4
| eval fieldA=random() % 50 + 1, fieldB=random() % 50 + 1, fieldC=random() % 50 + 1 ,fieldD=random() % 50 + 1, fieldE=random() % 50 + 1
| stats values(*) as * by _time
`comment("this is sample data")`
| eval counter=mvrange(0,mvcount(fieldA))
| streamstats count as session
| stats list(*) as * by session counter
| foreach field*
[eval <<FIELD>> = mvindex(<<FIELD>>, counter)]
| table field*
こちらはmvexpand
を使用しないでシングルイベントに分割したクエリー。
foreach
は単なる*
は_
から始まるフィールドをみない。
なので、今回はfield*
で指定したけど
rename session as _session, counter as _counter
として foreach *
とやる手もある。
ここのキモは
-
streamstats count as session
で各イベントのナンバーを作成 -
eval counter=mvrange(0,基準フィールドのmultivalue数)
で各イベントのmultivalue展開用カウンターを作成 -
stats list(*) as * by session counter
で一括シングル化用イベント作成 -
foreach *
で一括シングルイベント化
これで、複数イベントでフィールドがなんであれ、一括変換可能。
ただし、フィールド名にフィールドで使えない文字が入っているとforeach *
の処理ができないので、見つけたらrename
とかしてやってからforeach
。
#JSON
2019/12/7 追加。 ちょっとテクを見つけた。
mvexpandメモリ超過@Splunk Answer
multivalueを展開してくれるmvexpand
は_limits.conf_設定のメモリ容量を超過してしまうとイベントが省略されてしまう。
そのために先の記事では_やる前に_raw
を削りなさい_というコツを教えてくれる。
最近同じような_raw
を削ってもまだ、mvexpandメモリ超過になってしまう恐ろしい話の解答をみていて、stats
を利用してmultivalueを展開していることがあった。
これは使える
| makeresults
| eval _raw="{\"_data\":{\"services\":[{\"id\":\"FB00000\",\"users\":[100,122]},{\"id\":\"FB11111\",\"users\":[404,797]}],\"socialNetwork\":\"FB\"},\"_timestamp\":\"01-02-02013T01:00:04.582+0100\",\"_type\":\"ServiceReport\"}"
| spath path=_data.services{} output=data
| kv
| rename data.services{}.* as *
| stats count by data.socialNetwork timestamp type data
| spath input=data
| stats count values(*) as * by users{}
| rename users{} as users, data.socialNetwork as socialNetwork
| table id users socialNetwork timestamp type
mvexpand
を使わなくても展開可。メモリ超過の心配なし。
####ここでのポイント
- multivalue になるデータを
spath
で一旦とりだす。 -
kv
(spath
)でフィールド抽出 -
stats count by フィールド名
で展開 - 一旦取り出しておいたデータを
spath
で抽出 - 繰り返し
rename
は適宜行うと、SPLが書き易い。
なお、JSONは一旦 JSON Formatter
に通して、テキストエディタで"
を\"
に変換してやって貼り付けてます。
だいぶ楽になりました。
#JSON2
| makeresults
| eval _raw="{ \"Items\": [ { \"CN\": \"AccountName\", \"CV\": \"AccountOne\", \"Props\": [ { \"PN\": \"PropOne\", \"PV\": \"5\" }, { \"PN\": \"PropTwo\", \"PV\": \"3\" } ] }, { \"CN\": \"AccountName\", \"CV\": \"AccountOne\", \"Props\": [ { \"PN\": \"PropOne\", \"PV\": \"5\" } ] }, { \"CN\": \"AccountName\", \"CV\": \"AccountTwo\", \"Props\": [ { \"PN\": \"PropOne\", \"PV\": \"5\" }, { \"PN\": \"PropThree\", \"PV\": \"8\" } ] }, { \"CN\": \"PersonName\", \"CV\": \"Bob\", \"Props\": [ { \"PN\": \"PropOne\", \"PV\": \"5\" }, { \"PN\": \"PropThree\", \"PV\": \"8\" } ] }, { \"CN\": \"PersonName\", \"CV\": \"Bob\", \"Props\": [ { \"PN\": \"PropThree\", \"PV\": \"8\" } ] } ] }"
この場合、何も考えずspath
すると
Items{}.CN | Items{}.CV | Items{}.Props{}.PN | Items{}.Props{}.PV |
---|---|---|---|
AccountName AccountName AccountName PersonName PersonName |
AccountOne AccountOne AccountTwo Bob Bob |
PropOne PropTwo PropOne PropOne PropThree PropOne PropThree PropThree |
5 3 5 5 8 5 8 8 |
と大変なことになる。 |
ネストの深さにもよるが、spath
とstats count
とmvexpand
を組み合わせると上手く表にできる。
...
| spath path=Items{} output=Items
| stats count by Items
| spath input=Items path=Props{} output=Props
| mvexpand Props
| spath input=Props
| spath input=Items
| fields - Items count Props*
考え方としては、by
の引数が重複していない時はstats count
で分割
重複している、または他の項目を素直に分けたい場合mvexpand
を使用する。
結果は
CN | CV | PN | PV |
---|---|---|---|
AccountName | AccountOne | PropOne | 5 |
AccountName | AccountOne | PropOne | 5 |
AccountName | AccountOne | PropTwo | 3 |
AccountName | AccountTwo | PropOne | 5 |
AccountName | AccountTwo | PropThree | 8 |
PersonName | Bob | PropOne | 5 |
PersonName | Bob | PropThree | 8 |
PersonName | Bob | PropThree | 8 |
この後こんなことをしていました。
| eval names=CN."_".CV
| stats sum(PV) as PV by names PN
| xyseries names PN PV
| rex field=names "AccountName_(?<AccountName>\w+)|PersonName_(?<PersonName>\w+)"
| table PersonName AccountName PropOne PropTwo PropThree
xyseries
は引数としてはたくさん使用できるがフィールド名が汚くなるので、3つにまとめてあとで分解するといい感じになると思います。
#まとめ
spath
とかでJSONを展開すると、multivalueなフィールドがたくさんできる。
展開の方法はいろいろあって、フィールドの数やイベントの数に応じてここの方法のどれかを使うといいと思います。