Fun combining Java, JavaScript and elastic.js within the elasticshell

by Luca CavannaApril 11, 2013

elasticshell
I recently wrote a couple of articles about the elasticshell, the command line shell for Elasticsearch that I created. If you haven’t heard about it, it’s a json friendly command line tool that allows to quickly interact with Elasticsearch: you can easily index documents, execute queries and make use of all the API that Elasticsearch provides. It allows for more advanced usecases as well, since it exposes the power and flexibility of both JavaScript and Java. That’s scary, isn’t it? Let’s see what this means…

If you have a bit of experience with Elasticsearch you know how cool its query DSL is. You might also have realized that even though you start with simple queries, you most likely end up with big json objects containing different nested queries, filters, facets, highlighting and so on. That’s why I thought the elasticshell should make it easier to compose queries and execute them. I wasn’t totally satisfied about how you could do it using the first BETA release, since it used to require some knowledge of the Elasticsearch Java API in order to build queries without having to manually write plain json objects. That’s why I worked on a couple of nice new features that are going to help a lot when it comes to querying Elasticsearch.

The toJson command
A cool addition is the toJson command, that allows to create a json object not only from a string, but directly from a QueryBuilder object too (and more). I’m talking about Java objects here, instances of classes that are part of Elasticsearch itself, which we can convert to native json in a single call. Let’s see how this feature makes it easier to combine plain json queries with the ones generated through the Java API. Let’s start copy pasting a multi match query taken from the Elasticsearch documentation and adapting it to our needs obtaining the following result:

{
  "query": {
    "multi_match": {
      "query": "elasticshell",
        "fields": [
          "title^2",
          "content"
        ]
      }
  }
}

After that we want to add a facet to the existing query, but we don’t remember exactly the right json syntax. The FacetBuilders object provided with the Java API comes to the rescue, together with the available auto-suggestions:

> var facetBuilder = FacetBuilders.termsFacet('author').field('author');

If we now print the created object we do see some json, but that’s still a java object and we only see its string representation (literally the output of its toString method). We can now parse it as a json like this:

> var facet=toJson(facetBuilder);

What we obtained is a json object that we can combine with the existing search request like this:

> search.facets=facet

Let’s check what happened in our search object:

> search
{
  "query": {
    "multi_match": {
      "query": "elasticshell",
      "fields": [
        "title^2",
        "content"
      ]
    }
  },
  "facets": {
    "author": {
      "terms": {
        "field": "author",
        "size": 10
      }
    }
  }
}

What’s the trick? The Elasticsearch classes whose content can be represented as json usually implement the internal ToXContent interface. The toJson command just accepts ToXContent objects as argument and it’s able to convert them to json objects, native within the shell.

The best is yet to come
The toJson command helps but I thought it’s not enough. Some knowledge about the Java API is still required, and there are other ways to interact with Elasticsearch. What if you don’t know the Java API? Maybe you know the widely used JavaScript API, also known as elastic.js? You can just download it and load it using the load command, providing the location where the library is located on disk:

> load('/home/luca/Desktop/elastic.js');

Here it is, let’s now type ejs., the namespace that elastic.js uses, followed by the tab key to see what the suggestions look like:

> ejs.
AndFilter()                 BoolFilter()
BoolQuery()                 BoostingQuery()
CommonTermsQuery()          ConstantScoreQuery()
CustomBoostFactorQuery()    CustomFiltersScoreQuery()
CustomScoreQuery()          DateHistogramFacet()
DisMaxQuery()               Document()
ExistsFilter()              FieldMaskingSpanQuery()
FieldQuery()                FilteredQuery()
FilterFacet()               FuzzyLikeThisFieldQuery()
FuzzyLikeThisQuery()        FuzzyQuery()
GeoBboxFilter()             GeoDistanceFacet()
GeoDistanceFilter()         GeoDistanceRangeFilter()
GeoPoint()                  GeoPolygonFilter()
GeoShapeFilter()            GeoShapeQuery()
HasChildFilter()            HasChildQuery()
HasParentFilter()           HasParentQuery()

Wow, that means we can start creating queries using elastic.js. Let’s initialize a new search request and add a simple term query to it.

> var req=ejs.Request();
> req.query(ejs.TermQuery('title','elasticshell'));

Once our query is ready to go, we only need to send it to Elasticsearch. We can use the internal _self function to read the underlying query json object and send it to elasticsearch as we normally do within the elasticshell:

> es.search(req._self());

Load anything you need, at startup too
What’s really cool about the load command is that it allows to load any external javascript code. That means it’s possible to load custom scripts that contain documents, queries, or even functions that we frequently use within the elasticshell. It’s also possible to execute arbitrary commands at startup adding the .elasticshellrc.js file to the user home directory. Those commands don’t necessarily need to be javascript, they can be whatever command that’s valid within the elasticshell, even Java code!

Let’s wrap it up
In this article I showed you some creative ways to compose and run Elasticsearch queries using the elasticshell. You can either use the toJson command to combine the Java API with plain json queries, or the great elastic.js library if you’re not a Java guy. The described features are available in the latest elasticshell release: there are three active development branches at the moment that contain the same features but work with a different version of Elasticsearch: 0.19, 0.20 and the brand new 0.90. If you have any questions feel free to get in touch with me and it would also be great to know how you progress with its use too.