ES Kit

A 'pick and mix' library that simplifies writing Elasticsearch code

Overview

ES Kit is an in-Alpha library which makes it easer to get started with Elasticsearch.

Elasticsearch is a enterprise-grade document store and search engine based on the Apache Lucene library. It specialises in free text search using an inverted index to score documents against input search queries.

Elasticsearch's JavaScript Client is used to call its APIs but its inherent flexibility can be overwhelming for beginners:

  • complex request and response formats can lead to verbose, duplicate or fragile application code
  • if you're new to the API, documentation or terminology, it's difficult to know what code to write

Library

To address this, ES Kit provides a constrained "kit of parts" in the form of reusable, atomic helper functions which fit together to reliably abstract the API lifecycle, making it quick, easy (and reasonably obvious how) to write clean, trial-and-error-free client code.

The kits comprises 4 main areas:

  • Queries – build Elastic queries using simple functions
  • Helpers – abstract key parts of the Elastic API lifecycle
  • Scripts – build Elastic scripts from JavaScript functions
  • Api – simplified interaction with Elasticsearch's APIs

These modules tread a carefully-planned line between:

  • abstraction – they abstract the request config and response data only
  • knowledge – you're required to understand the basics of using the client

The helpers themselves address a core subset of the API which creates a much shallower learning curve.

Along with the code, the library ships with what amounts to a beginners guide to Elastic to help new users get up to speed and prevent the library from reinventing the wheel solely for the purpose of making new users productive.

Comparison

As an example of Elastic's verbosity, here's a simple search querying an id and the text cat across multiple fields:

import { client } from './client'

// query is verbose with difficult to remember syntax
const params = {
  index: 'contacts',
  query: {
    bool: {
      must: [
        {
          match: {
            groups: req.params.id,
          },
        },
        {
          multi_match: {
            query: req.query.filter,
            fields: [
              '*',
            ],
            type: 'phrase_prefix',
          },
        },
      ],
    },
  },
}

try {
  const res = await client.search(params)
  if (res.body.hits) {
    // response is complex with deeply nested properties
    return res.body.hits.hits.map(hit => {
      return { _id: hit._id, ...hit._source }
    })    
  }
}
catch (err) {
  console.log(err)
  throw err
}
JavaScript

As a new user, it is difficult to know where to start; the whole process is time-consuming and error-prone.

ES Kit provides various ways to simplify this.

Firstly, using utilities which provide typed functions with clear arguments:

import { client } from './client'
import { Queries as _, Helpers as $ } from '@davestewart/es-kit'

// use helpers to build the query
const params = {
  index: 'contacts',
  query: _.must([
    _.match('groups', req.params.id),
    _.multiMatch('*', req.query.filter),
  ])
}

try {
  const res = await client.search(params)
  return $.results(res) // convert results to something usable
}
catch (err) {
  throw $.error(err) // logs and throws a simplified error structure
}
JavaScript

Or, using the Api (which uses the utilities internally) which wraps everything up into one easy call:

import { Api, Helpers as $ } from '@davestewart/es-kit'

// create query, parse results, handle errors, paginate, and more...
return Api.search('contacts', { query: $.query.request(req) })
JavaScript

The approach is designed to let users augment existing code, completely replace it, or find a sweet spot inbetween.

So...

I hope you found this post interesting or useful.

If you want to engage further, follow me on Twitter, Bluesky, or drop a comment or reaction below.

Either way, thanks for reading!