LoginSignup
11
8

More than 5 years have passed since last update.

JavaScriptでElasticsearchのクエリを作る

Last updated at Posted at 2016-12-16

この記事は、Elastic stack Advent Calendar 2016の13日目の記事です。

作ったツールをこの記事で紹介しようと思います。
GitHub: es-builder

このツールを使ってJavaScriptでElasticsearchのクエリを簡単に作成できます。
特に作成できるのが、Query DSL経由で作る部分です (bodyのquery項目)。

Motivation

弊社で開発しているサービスでElasticsearchを導入することになったことで、クエリ作成も含めてNode.jsで公式のクライアントを使って、検索ロジックを実装しました。

簡単なクエリなら公式クライアントで十分かもしれませんが、複雑なクエリを作るならツールが必要となると思います。直接JSONでかくと、間違えやすいし、かなり冗長になります。

elastic.jsを使おうと思いましたが、2年ぐらいメンテされなくて、Query DSL 2.xに互換性がありません。他に良さそうなツールもありましたが、ネストクエリができなかったりだったので、結局自分で作ることにしました。

基本の使い方

下記のJSONクエリを

 {
   "bool": {
     "must": [{
       "term": {
         "name": "承太郎"
       }
     }, {
       "match": {
         "description": {
           "query": "やれやれだぜ"
         }
       }
     }],
     "must_not": {
       "term": {
         "location": "杜王町"
       }
     }
   }
 }

es-builderで作りましょう。

const eb = require('es-builder');
const elasticsearch = require('elasticsearch');

const client = new elasticsearch.Client({
  host: 'localhost:9200'
});

const query = eb.QueryBuilder()
  .query(eb.TermQuery('name', '承太郎'))
  .query(eb.MatchQuery('description', 'やれやれだぜ'))
  .queryMustNot(eb.TermQuery('location', '杜王町'));

作られたqueryインスタンスをstringifyすると、上記と同じJSONが作られます。

JSON.stringify(query);
// {
//   "bool": {
//     "must": [{
//       "term": {
//             ...
//             ...
//     }
//    }

内部的にElasticsearchのライブラリに渡されるJSのオブジェクトのbodyJSONにコンバートしてくれるので、 JSON.stringifyせずにそのまま作ったqueryインスタンスを渡してもいいです!

client.search({
  index: 'user',
  type: 'stand_user',
  body: {
    query: query
  }
}, function(err, resp) {
  // result of the search here
  ...
});

上記に作ったクエリは固定していましたが、インプット等で可変なクエリのJSONを作るのは複雑になると思います。その場合もes-builderを使うと楽になります。

ネストクエリ

ネストクエリも作成可能です。

const eb = require('es-builder');

const query = eb.QueryBuilder();

// フィルターを追加する
query
  .filter(eb.TermsQuery('name', ['ジョセフ', 'ジョナサン']))
  .filter(eb.ExistsQuery('age'));

// bool queryを作る
const boolQuery = eb.BoolQuery()
  .should(eb.RangeQuery('age').gt(20).lt(40))
  .should(eb.PrefixQuery('surname', 'ジョ'));

// ネストする
query.filter(boolQuery);

// queryインスタンスはstringifyすると
// JSON.stringify(query)
// {
//   "bool": {
//     "filter": {
//       bool: {
//         "must": [{
//           "terms": {
//             "name": {
//               "value": ["ジョセフ", "ジョナサン"]
//             } 
//           }
//         }, {
//           "exists": {
//             "field": "age"
//           }
//         }, {
//           "bool": {
//             "should": [{
//               "range": {
//                 "age": { "gt": 20, "lt": 40 }
//               }
//             }, {
//               "prefix": {
//                 "surname": {
//                   "value": "ジョ"
//                 }
//               }
//             }]
//           }
//         }]
//       }
//     }
//   }
// }

REPL

global moduleとしてインストールすれば、REPL経由でクエリを作成できます。
REPLの場合だと全クエリはexposeされたので、eb.なしでそのまま使えます。

$ npm install -g es-builder
...

$ es-builder

es-builder> query = QueryBuilder().query(TermQuery('name', 'カービィ')).query(MatchQuery('description', 'まるくて、ふわふわしてる')).queryMustNot(TermQuery('name', 'ワドルディ'));

es-builder> query.stringified
'{"bool":{"must":[{"term":{"name":{"value":"カービィ"}}},{"match":{"description":{"query":"まるくて、ふわふわしてる"}}}],"must_not":{"term":{"name":{"value":"ワドルディ"}}}}}'

es-builder> .exit

作られたクエリをコピーして、curlでもで使えます。

$ curl -XGET 'http://localhost:9200/games/dreamland/_search' -d'
{
    "query" : {"bool":{"must":[{"term":{"name":{"value":"カービィ"}}},{"match":{"description":{"query":"まるくて、ふわふわしてる"}}}],"must_not":{"term":{"name":{"value":"ワドルディ"}}}}}
}'

おわりに

es-builderを使って、JavascriptでElasticsearchのクエリを作れるツールの紹介でした。
提案や意見等あれば是非イシューもしくはプルリクエストを送ってください。お待ちしております!

11
8
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
11
8