# URLのコンテキスト毎に集計するクエリを知りたいです

**URL:** https://discuss.elastic.co/t/url/167962
**Category:** 日本語による質問・議論はこちら
**Created:** [February 12, 2019, 6:22am UTC](https://discuss.elastic.co/t/url/167962 "2019-02-12T06:22:59Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![nidcode](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nidcode/32/28552_2.png) [@nidcode](https://discuss.elastic.co/u/nidcode)
#### Post date: [February 12, 2019, 6:22am UTC](https://discuss.elastic.co/t/url/167962/1 "2019-02-12T06:22:59Z")

</div>

現在、Elasticsearchにウェブサーバーのアクセスログがあり、"path"というフィールドにURLのパスが保存されています。

一度のクエリでpathのコンテキスト毎にドキュメント数を集計可能かどうか、良い方法があれば知りたいです。

- データ  
/aaaa/index.html  
/aaaa/api/v1/user  
/bbbb/index.html

- 結果のイメージ  
{  
"aaaa": 2,  
"bbbb": 1  
}

---

<div class="post-metadata">

### Author: ![tsgkdt](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/tsgkdt/32/39151_2.png) [@tsgkdt](https://discuss.elastic.co/u/tsgkdt)
#### Post date: [February 12, 2019, 11:28am UTC](https://discuss.elastic.co/t/url/167962/2 "2019-02-12T11:28:37Z")

</div>

コンテキストごとに集計可能か、とのことですが２つぐらいの方法で可能かと思います。

1. データ投入時にコンテキストに相当する文字列を抽出して別フィールドに格納しておき、そこに対してAggregationを指定する

2. 検索クエリ発行時に指定するScriptを使った結果に対してAggregationを実行する

できれば、データ投入時にpathを加工してIndexを作成した方が検索時のパフォーマンスは良いかと思います。

どちらもScriptを使いますが、以下の「 部分文字列を返す」の箇所をご覧いただくと、イメージしやすいかとおもいます。

> **[KibanaのScript FieldでPainlessを使う
	  	 | Elastic](https://www.elastic.co/jp/blog/using-painless-kibana-scripted-fields)**
>
> Kibanaは、Elasticsearchに保存されたデータの検索や可視化を可能にする強力なツールです。可視化にあたって、KibanaはElasticsearch mappingに定義されているフィールドを探し、チャートを作成するユーザーにそのフィールドをオプションとして提示してくれます。でも、もし重要な値を別のフィールドとして定義し忘れてしまった場合はどうでしょう？あるいは、2つの異なるフィール...

## 1. データ投入時にコンテキストを指定する例

Ingest NodeのScript Processorを使います。

### Pipelineの作成

pathというフィールドの文字列から最初の/を除いて、次の/までの文字を取り出し、path\_contextというフィールドに詰めます。

```auto
PUT _ingest/pipeline/pipe0212
{
  "processors": [
    {
      "script": {
        "source": """
            def path = ctx.path;
            if (path != null) {
              if (path.startsWith('/')) {
                path = path.substring(1, path.length()-1);
              }
              int slashIndex = path.indexOf('/');
              if (slashIndex > 0) {
                ctx.path_context = path.substring(0, slashIndex);
              }
            }
"""
      }
    }
  ]
}

```

### データ投入例

先に作成したPipelineを指定してデータ投入。

```auto
PUT forum0212/_doc/1?pipeline=pipe0212
{
  "title": "pathで最初の方の文字がほしい",
  "path": "/aaaa/index.html"
}
PUT forum0212/_doc/2?pipeline=pipe0212
{
  "title": "pathで最初の方の文字がほしいです",
  "path": "/bbbb/index.html"
}

```

### 結果

```auto
GET forum0212/_doc/1

```

ここで、path\_contextフィールドでaaaaが抜き出されていることが確認できるので、このフィールドに対してAggregationを指定すれば期待する結果が得られると思います。

```auto
{
  "_index" : "forum0212",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "_seq_no" : 2,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "path" : "/aaaa/index.html",
    "title" : "pathで最初の方の文字がほしい",
    "path_context" : "aaaa"
  }
}

```

## 2. Scriptの結果に対してAggregationを指定する例

terms aggregationを使っているかと思いますが、中身をscriptにし、先のpipelineと同じような感じで、指定文字列を抜き出せるようにします。

```auto
GET forum0212/_search
{
  "query": {
    "match_all": {}
  },
  "size": 0,
  "aggs": {
    "contextaggs": {
      "terms": {
        "script": {
          "lang": "painless",
          "source": """
            def path = doc['path.keyword'].value;
            if (path != null) {
              if (path.startsWith('/')) {
                path = path.substring(1, path.length()-1);
              }
              int slashIndex = path.indexOf('/');
              if (slashIndex > 0) {
                path = path.substring(0, slashIndex);
                return path;
              }
            }
            return "";
"""
        },
        "size": 10
      }
    }
  }
}

```

結果

contextaggsのところで、期待するaaaa, bbbbが取得できていることが分かります。

```auto
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.0,
    "hits" : []
  },
  "aggregations" : {
    "contextaggs" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "aaaa",
          "doc_count" : 1
        },
        {
          "key" : "bbbb",
          "doc_count" : 1
        }
      ]
    }
  }
}

```

---

<div class="post-metadata">

### Author: ![nidcode](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nidcode/32/28552_2.png) [@nidcode](https://discuss.elastic.co/u/nidcode)
#### Post date: [February 15, 2019, 6:22am UTC](https://discuss.elastic.co/t/url/167962/3 "2019-02-15T06:22:35Z")

</div>

tsgkdtさん、ありがとうございます！

2.の方法で取得できました。  
なお、pathが'/'の場合にうまくいかなかったので下記のように対応しました。  
念のため共有いたします。

> def path = doc['path.keyword'].value;  
> if (path != null) {  
> if (path.length() \> 1 && path.startsWith('/')){  
> path = path.substring(1, path.length()-1);  
> }  
> int slashIndex = path.indexOf('/');  
> if (slashIndex \> 0) {  
> path = path.substring(0, slashIndex);  
> return path;  
> }  
> }  
> return "";

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)
#### Post date: [March 15, 2019, 6:22am UTC](https://discuss.elastic.co/t/url/167962/4 "2019-03-15T06:22:49Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
