Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@toniov

JavaScriptでElasticsearchのクエリを作る

More than 3 years have passed since last update.

この記事は、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のクエリを作れるツールの紹介でした。
提案や意見等あれば是非イシューもしくはプルリクエストを送ってください。お待ちしております!

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
toniov
スペインからきたエンジニアです。 現在鯖サイドエンジニアやっています。 炙り鯖が好きです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?