Node.js で Elasticsearch にクエリ

Elasticsearch の公式クライアントで叩く。

const elasticsearch = require('elasticsearch');
const client = new elasticsearch.Client({
  host: 'http://localhost:9200'
  httpAuth: 'user:password'
  log: 'trace'
});

client.search({
  index: index,
  type: type,
  size: size,
  body: {
    _source: [
      // to be included fields
    ],
    aggs: {
      // aggregation contents
    },
    query:{
      // query contents
    }  
  }
});

直で REST API 叩く時とちょっとだけ違っていて、 query や aggs の前に body を挟む必要があったりするので注意。

.search の戻り値は Promise になっていて、中身はこんな以下のように取得する。これは REST API の戻り値と (たぶん) おなじ。

client.search().then(response => {
  const documents = response.hits.hits.(x=>x._source);
  const aggregated = response.aggregations;
  // ...
});

当然、

function get() {
  return client.search().then(response => return response;)
}

get().then(result => {
  // ...
});

とか、

 const result = await client.search();

とか書ける。

Scroll API で取得したい場合はちょっと違って

let documents = [];
client.search({
  index: index,
  type: type,
  size: size,
  scroll: chacheTime
  body: {
    query:{
      // query contents
    }
  }
}, function scroll(error, response) {
  documents = documents.concat(response.hits.hits);
  if(response.hits.total !== documents.length) {
    client.scroll({
      scrollId: response._scroll_id,
      scroll: cacheTime
    }, scroll)
  } else {
    return documents;
  }
});

のようになる。この戻り値が Promise とならず、 client.search({...}, function(){...}).then() のように書けない。なので 上述の例と同じように扱いたければ

const promise = new Promise((resolve, reject) => {
  let documents = [];
  client.search({
    // ...
  }, function scroll(error, response) {
    documents = documents.concat(response.hits.hits);
    if(response.hits.total !== documents.length) {
      client.scroll({
        scrollId: response._scroll_id,
        scroll: cacheTime
      }, scroll)
    } else {
      resolve(documents);
    }
  })
})

と Promise オブジェクトを new してやる必要がある。

これはもっとスマートに解決できる方法があるかも?