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
}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
}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) })The approach is designed to let users augment existing code, completely replace it, or find a sweet spot inbetween.