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