Splunk: ワイルドカード・正規表現での検索におけるヒット条件毎の統計

必要な機会があったのでメモ。

###### 実施環境： Splunk Free 8.2.2

###### 前置き

文字列の検索において、条件毎のヒット数を算出することを考えます。
文字列が固定であれば、以下のように stats コマンドを使用することで容易に実現できます。

```Splunk:Splunk
| makeresults count=10
| streamstats count AS CNT
| eval NUM = CNT % 3
| eval STR = "A" + NUM
| where STR = "A1" OR STR = "A2"
| stats count BY STR
```

![スクリーンショット 2022-02-06 22.12.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/0d4e4d87-30f1-aebc-af78-210e73e16961.png "stats")

では、この条件がワイルドカードや正規表現を含むものであればどうでしょうか。

###### やりたいこと

以下のようなデータを考えます。

|STR|
|:-----|
|one|
|two|
|three|
|four|
|five|

```Splunk:Splunk
| inputlookup "test_data.csv" 
```

![スクリーンショット 2022-02-06 22.09.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/5e27a04d-368b-0356-73d9-87e5a5228770.png "test_data")

検索条件は「 t で始まる文字列」「 e で終わる文字列」の2つを指定します。

|STR|
|:-----|
|t*|
|*e|

```Splunk:Splunk
| inputlookup "test_search.csv" 
```

![スクリーンショット 2022-02-06 22.16.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/273ee077-3e9b-4746-50f2-fbfefe1b1da9.png "test_search")

上記のデータにおいて「 t で始まる文字列」は2つ、「 e で終わる文字列」は3つあります。
この数字を導出することを目指します。

###### 単純に検索すると

まずは単純に format コマンドで検索条件を組み立てて search コマンドで検索してみます。
 where コマンドを使用しないのは where コマンドだとワイルドカードをそのまま利用できないためです。

```Splunk:Splunk
| inputlookup "test_data.csv"
| search
    [
      | inputlookup "test_search.csv"
      | format
    ]
```

![スクリーンショット 2022-02-06 22.16.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/ae505352-8bc5-0d10-49c6-3f0ccfdbed96.png "format")

|STR|
|:-----|
|one|
|two|
|three|
|five|

上記の結果をいろいろこねくり回してみましたが、この方針で目的を達成するのは難しそうです。
何が悪いのかを考えると、「どの条件でヒットしたのかが結果から判断できない」ということに気づきます。

以下のように条件毎にサーチ文を作って合成するという方法も思いつきますが、汎用性が無さすぎます。

```Splunk:Splunk
| inputlookup "test_data.csv"
| search
    [
      | inputlookup "test_search.csv"
      | head 1
      | format
    ]
| eval STR_SEARCH = "t*"
| stats count BY STR_SEARCH
| append
    [
      | inputlookup "test_data.csv"
      | search
          [
            | inputlookup "test_search.csv"
            | tail 1
            | format
          ]
      | eval STR_SEARCH = "*e"
      | stats count BY STR_SEARCH
    ]
```

|STR_SEARCH|count|
|:-----|-----:|
|t*|2|
|*e|3|

![スクリーンショット 2022-02-13 13.30.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/d11ec5fa-f991-3df1-e74b-d6a7f76b5ec4.png "append")


###### 解決方法

いろいろ考えた結果、以下の方法に行き着きました。

```Splunk:Splunk
| inputlookup "test_data.csv"
| join max=0
    [
      | inputlookup "test_search.csv"
      | eval STR_SEARCH = STR
      | eval STR_SEARCH_R = replace(STR,"\*","%")
      | fields STR_SEARCH, STR_SEARCH_R
    ]
| where like(STR, STR_SEARCH_R)
| stats count BY STR_SEARCH
```

|STR_SEARCH|count|
|:-----|-----:|
|*e|3|
|t*|2|

![スクリーンショット 2022-02-13 13.33.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/f29c7c95-47a5-03c3-f1b4-3839611b43a6.png "wildcard")

ポイントは以下の2点です。

* join コマンドの max オプションを使用して検索対象と検索条件の組を全て書き出す

* where コマンドの like 関数を使用して、組毎に検索を実施する

ポイントの1点目ですが、 join コマンドの max オプションに 0 を指定すると全ての組み合わせを網羅する形での結合(交差結合)が実現できます。
これを利用して、検索対象と検索条件の組み合わせを、1組1行で全て書き出します。

```Splunk:Splunk
| inputlookup "test_data.csv"
| join max=0
    [
      | inputlookup "test_search.csv"
      | eval STR_SEARCH = STR
      | fields STR_SEARCH
    ]
```

|STR| STR_SEARCH |
|:-----|:-----|
|one|t*|
|one|*e|
|two|t*|
|two|*e|
|three|t*|
|three|*e|
|four|t*|
|four|*e|
|five|t*|
|five|*e|

![スクリーンショット 2022-02-13 13.34.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/f268fffb-979e-6e09-7817-06d3af3737a3.png "join")

書き出した組み合わせについて、ポイントの2点目で行毎に検索を実施し、ヒットした行のみを抽出します。
2つのフィールドを比較するので、先ほどは逆に where コマンドである必要があります。
注意点として、 like 関数のワイルドカードは「任意の文字列」を表す文字が「*」でなく「%」になるので、そのように条件の文字列を変換しないとうまく動きません。

```Splunk:Splunk
| inputlookup "test_data.csv"
| join max=0
    [
      | inputlookup "test_search.csv"
      | eval STR_SEARCH = STR
      | eval STR_SEARCH_R = replace(STR,"\*","%")
      | fields STR_SEARCH, STR_SEARCH_R
    ]
| where like(STR, STR_SEARCH_R)
```

|STR| STR_SEARCH | STR_SEARCH_R |
|:-----|:-----|:-----|
|one|*e|%e|
|two|t*|t%|
|three|t*|t%|
|three|*e|%e|
|five|*e|%e|

![スクリーンショット 2022-02-13 13.37.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/3d0b47e1-0bce-b084-62ef-0dbd170ab7f6.png "like")

このようにすれば、「ヒットした値」と「ヒットした条件」の組が残るので、 stats コマンドで集計することができます。

###### 正規表現

さらにこの方法だと、 like 関数を match 関数に変えるだけで正規表現にも対応することができます。
 search コマンドで正規表現は使えないので、この方法は統計処理をしない検索だけでも有用かと思います。

```Splunk:Splunk
| makeresults count=200
| streamstats count AS STR
| join max=0
    [
      | makeresults count=2
      | streamstats count AS STR
      | eval STR_SEARCH = "^" + STR + "*5{1,2}$"
      | fields STR_SEARCH
    ]
| where match(STR, STR_SEARCH)
| stats count BY STR_SEARCH
```

![スクリーンショット 2022-02-13 13.50.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/0a1259f7-d11f-915d-3de7-7afebcc66d00.png "match")