LoginSignup
12
17

More than 5 years have passed since last update.

実践的Elasticsearchクエリ

Last updated at Posted at 2016-12-12

この記事はWanoグループAdventCalendar2016の13日目の記事になります。

Elasticsearchのクエリはとても使いやすく、かつ、実開発現場で必要となる、複雑で複数の検索結果が欲しいような場面でも、保守性を損なうことのない、見やすい一つのクエリで書ききることができるような設計になっています。

ですが、実際書く段なると細部までは覚えていないので、マニュアルや実装例をWebで調べることになるのですが、ブログ記事などの実装例が非常に助けになったので、微力ながらそういったものの一つになればと思い、この記事を書くことにしました。

今回は、下記のような力士データベースを例にしてみます。

項目名 データ例
名前 string 稀勢の里
生年月日 date 1986年7月3日
性別 string
出身地 string 茨城県
格付 string 西大関
所属 string 田子ノ浦部屋
年齢 integer 30歳
身長 integer 188cm
体重 integer 171cm
バスト integer 147cm
ウエスト integer 203cm
ヒップ integer 185cm
血液型 string B型
趣味 string スポーツ観戦
得意技 string 左四つ・寄り・突き
好きなタイプ string 仲間由紀恵
髪型 string 大銀杏
今までに突き合った人数 integer 1209人

それでは、好みの力士を検索してみます。
(説明用にコメント文を挿入していますが、使用時は削除してください)

{
    //取得件数と開始位置
    "size": "20",
    "from": "0",

   //ここからが検索条件
    "query": {
        "bool": {

            //単に結果をフィルタしたいだけの場合はfilter構文を使うといくらか高速
            "filter": {
                "bool": {

                    //mustは全部の条件が満たされたいということ
                    "must": [
                        {
                            //年齢は15歳~35歳に限定
                            "range": {
                                "年齢": {
                                    "gte": "15", 
                                    "lte": "35"
                                }
                            }
                        }, 
                        {
                            //性別は女性だけ
                            "term": {
                                "性別": "女"
                            }
                        }, 
                        {
                            //髪型はショートカットかセミロングどちらか
                            "terms": {
                                "髪型": [
                                    "ショートカット",
                                    "セミロング",
                                ]
                            }
                        }
                    ], 

                    //must_notで満たしたくない条件を指定する。
                    //男性は含まない、って上で女性指定してるからこの条件は不要ですね。
                    "must_not": {
                        "terms": {
                            "性別": ["男"]
                        }
                    }
                }
            },

            //ここからがfilterではない本来のqueryなのでスコア付けされる 
            "must": [
                {
                    "bool": {

                        //boostして各条件に重み付けをしている。
                        //よく分からない重み付けになってるけど、
                        //とにかく好きなタイプがプログラマーかSEかサーバーエンジニア希望ということ。
                        "should": [
                            {
                                "term": {
                                    "boost": 4, 
                                    "好きなタイプ": "プログラマー"
                                }
                            }, 
                            {
                                "term": {
                                    "boost": 3, 
                                    "好きなタイプ": "SE"
                                }
                            }, 
                            {
                                "term": {
                                    "boost": 2, 
                                    "好きなタイプ": "サーバーエンジニア"
                                }
                            }
                        ]
                    }
                }, 
                {
                    //趣味か得意技にプログラミングかラムダ式が含まれること希望。
                    //趣味に若干重みを付けている。
                    "multi_match": {
                        "fields": [
                            "趣味^6", 
                            "得意技^4", 
                        ], 
                        "operator": "or", 
                        "query": "プログラミング ラムダ式", 
                        "type": "most_fields"
                    }
                }
            ]
        }
    }, 

    //検索条件は終わってソート。
    //ソートしてしまったら、さっきまでの重み付けの意味が・・・。
    //指定しなかったら、スコア順になる。
    //まず、バストは大きい方が良くて、同じサイズならウエストは小さいほうが良いらしい。
    "sort": [
        {
            "バスト": {
                "order": "desc"
            }
        },
        {
            "ウエスト": {
                "order": "asc"
            }
        }
    ],

    //検索結果のついでに、全検索結果の統計情報も取り出す
    "aggs": {

        //最初のタイトル(ここでは「血液型グループ」)は結果の目印なので適当に。
        //血液型がB型の人数を知りたいみたい。
        "血液型グループ": {
            "filter": {
                "term": {
                    "血液型": "B"
                }
            }
        }, 

        //これは複雑ですが、今までに突き合った人数を10人単位でヒストグラム化している
        //~9人:5
        //1019人:3
        //2029人:7
        //みたいな感じで。
        "今までに突き合った人数グループ": {
            "aggs": {
                "今までに突き合った人数ヒストグラム": {
                    "histogram": {
                        "field": "今までに突き合った人数", 
                        "interval": 10, 
                        "order": {
                            "_key": "asc"
                        }
                    }
                }
            },

            //ヒストグラムに、今までに突き合った人数が0人の人は含めないようにしている。
            "filter": {
                "range": {
                    "今までに突き合った人数": {
                        "gte": 1
                    }
                }
            }
        }, 

        //趣味ごとの人数を、多い順に並べている
        "趣味グループ": {
            "terms": {
                "field": "趣味", 
                "order": {
                    "_count": "desc"
                }, 
                "size": 0 //全趣味を取ってくるために0を指定
            }
        } 
    } 
}

こんな感じで、好みの力士を検索することができました。

12
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
17