In previous tutorials, we discussed how to use Elasticsearch native clients in RustJava, and Python among others.

Today, we’re going to cover Node.js, – a popular JavaScript server-side solution based on event-driven architecture suitable for the development of fast servers, I/O heavy applications, and real-time applications (RTAs). Node.js has one of the fastest growing ecosystems, and it is now possible to interact with the Elasticsearch API using a Node.js client.

Elasticsearch.js is the official Elasticsearch client for Node.js that ships with:

  • one-to-one mapping with Elasticsearch REST API
  • automatic discovery of ES nodes
  • intelligent handling of ES node/connection failures
  • load balancing across ES nodes
  • Typescript support
  • asynchronous operations with JavaScript Promises.

Elasticsearch.js has among the widest support for standard and advanced Elasticsearch features and is regularly tested against new ES releases. The client is regularly updated and maintained by the vibrant Node.js community.

Tutorial

We will be using hosted Elasticsearch on qbox.io for this tutorial. You can sign up and launch your Elasticsearch cluster here, or click “Get Started” in the header navigation menu. If you need help setting up, refer to “Provisioning a Qbox Elasticsearch Cluster.” Alternatively, you can use your own local Elasticsearch cluster. 

Before you proceed to the tutorial, ensure that the latest version of Node.js is installed on your computer. If you are not sure how to do this, refer to Node.js installation guide for directions. 

Once we have Node.js up and running, we need to install elasticsearch.js into our Node.js application. Do this with the following npm (Node Package Manager) command:

npm install elasticsearch

Using the elasticsearch.js module in Node.js we can easily connect to and interact with our Elasticsearch cluster on Qbox.io:

var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
   hosts: [ 'https://username:password@host:port']
});

The client constructor accepts a config object/hash where you can define default parameters, or even entire classes, for the client to use. In addition, our client variable inherits all methods of the elasticsearch.js client constructor that directly map to the Elasticsearch API.

We can now ping to our Qbox.io cluster to check if everything is ok.

client.ping({
     requestTimeout: 30000,
 }, function(error) {
     if (error) {
         console.error('elasticsearch cluster is down!');
     } else {
         console.log('Everything is ok');
     }
 });

If something goes wrong, you can debug using the error object returned by the callback. The Elasticsearch API also includes other useful methods to inspect your Qbox.io cluster. For example, using a cluster object you can check cluster health (cluster.health), stats, current state (e.g operational), and more.

Features of Elasticsearch.js

Now, we are going to discuss basic features of elasticsearch.js such as indexing, creating documents, and search.

Indexing

Unlike conventional SQL databases that store documents as rows in tables, Elasticsearch stores data in document indexes. In this example, we will create a “blog” index to store “posts.” It can be done easily with elasticsearch.js:

 client.indices.create({
     index: 'blog'
 }, function(err, resp, status) {
     if (err) {
         console.log(err);
     } else {
         console.log("create", resp);
     }
 });

You get an index_already_exists exception if the index already exists. Otherwise, a new index ready to store your documents is created.

Adding Documents to an Index

Now that we have an index, we need some documents to post to it. For that purpose, we are going to index a new document with “posts.”

 client.index({
     index: 'blog',
     id: '1',
     type: 'posts',
     body: {
         "PostName": "Integrating Elasticsearch Into Your Node.js Application",
         "PostType": "Tutorial",
         "PostBody": "This is the text of our tutorial about using Elasticsearch in your Node.js application.",
     }
 }, function(err, resp, status) {
     console.log(resp);
 });

Here, the body is a typed JSON document in an index that makes it searchable. We have defined a body object with key parameters of our document: PostName, PostType, and PostBody. You can add whatever parameters of the blog post you need.

The inner process of posting a new document works like this. If id param is not specified, Elasticsearch will auto-generate a unique id. In case you specify an id, Elasticsearch will either create a new document (if it does not exist) or update an existing one. Similarly, Elasticsearch will perform optimistic concurrency control when the version argument is used.

If the document is successfully saved, we get the following response from the server:

{ _index: 'blog',
  _type: 'posts',
  _id: '1',
  _version: 1,
  result: 'created',
  _shards: { total: 1, successful: 1, failed: 0 },
  created: true }

If the document already exists and we didn’t specify a version argument, the existing document will be simply updated by the new one and specified as its second version.

{ _index: 'blog',
  _type: 'posts',
  _id: '1',
  _version: 2,
  result: 'updated',
  _shards: { total: 1, successful: 1, failed: 0 },
  created: false }

In addition to creating documents, elasticsearch.js supports index flushing, analysis, recovery, creation of mappings, simulation, and other advanced methods.

Search Documents Using Query Params

Elasticsearch.js has mature search functionality that supports both simple queries and Elasticsearch Query DSL. To search documents using a simple query you need to specify a “q” parameter in your request object. In this example, we are searching for all posts with “Node.js” in the post title.

client.search({
    index: 'blog',
    type: 'posts',
    q: 'PostName:Node.js'
}).then(function(resp) {
    console.log(resp);
}, function(err) {
    console.trace(err.message);
});

Elasticsearch Query DSL

If you want to have a fine-grained control over document search, Elasticsearch offers a query DSL.

client.search({
    index: 'blog',
    type: 'posts',
    body: {
        query: {
            match: {
                "PostName": 'Node.js'
            }
        }
    }
}).then(function(resp) {
    console.log(resp);
}, function(err) {
    console.trace(err.message);
});

A successful response looks like this:

{ took: 407,
  timed_out: false,
  _shards: { total: 4, successful: 4, failed: 0 },
  hits: { total: 1, max_score: 0.26742277, hits: [ [Object] ] } }

It shows that a total number of documents with the title that matches the query (“Node.js”) is equal to one.

If no documents match the query, Qbox ElasticSearch returns:

{ took: 69,
  timed_out: false,
  _shards: { total: 4, successful: 4, failed: 0 },
  hits: { total: 0, max_score: null, hits: [] } }

Elasticsearch.js supports all Query DSL features including leaf clauses and compound clauses. As a bonus, you can use wildcard searches and regular expressions. In the example below, we create a query for all matches where “.js” is preceded by four characters:

query: { wildcard: { "PostBody": "????.js" } }

Conclusion

Elasticsearch.js is a mature Elasticsearch client for Node.js, and it’s able to handle basic use cases and to support many advanced ones. In addition to the aforementioned functionality, elasticsearch.js supports cross-shard search, scrolling, bulk operations in a single API call, and more. Broad coverage of low-level Elasticsearch functions and leveraging the power of Javascript asynchronous calls makes this library the best choice for your Node.js application.