eson-dsl: The elasticsearch query DSL, in Ruby

Build Status

This gem provides a full implementation of the ElasticSearch query DSL, independent of a client. At the moment, it encodes the query DSL of ElasticSearch 0.19.

Features:

  • Safe: it does its best to avoid generating improper queries
  • Close to the original: All queries and filters have the same name an similar parameter order as in the elasticsearch documentation
  • Ease of use: makes handing some of the trickier aspects of the query language easier
  • Complete: Incorporates all of the elasticsearch query language

Usage

require 'eson-dsl'
require 'json'

q = Eson::Search::BaseQuery.new do
  query do
    wildcard :user, :value => "kim*y", :boost => 2.0
  end
  filters do
    range :age, :from => 10, :to => 20
  end
  facets do
    histogram :hist1, :field => :age, :interval => 2
  end
end

JSON.dump(q.to_query_hash)

This example yields:

{
  "query": {
    "wildcard": {
      "user": {
        "value": "kim*y",
        "boost": 2.0}
      }
    }
  },
  "filter": {
    "range": {
      "age": {
        "from": 10,
        "to": 20
      }
    }
  },
  "facets": {
    "hist1": {
      "histogram": {
        "field": "age",
        "interval": 2
      }
    }
  }
}

For a list of all available generation methods along with examples, have a look at MethodReference.

Parameters

Parameters can be passed to BaseQuery to allow generation of dynamic queries, without playing with variable visibility:

Eson::Search::BaseQuery.new(:search_string => "kim*y", :boost => 2.0) do
  query do
    wildcard :user, :value => param(:search_string), :boost => param(:boost)
  end
end

Examples

See examples for all examples used in the test suite.

Specialities

Eson supports scoped facets in an object-oriented way. To scope a facet, call scope on it and pass the reference to a subquery. In practice, this requires a bit of trickery in Ruby 1.9, as it local variables are not propagated outside of the introducing block:

Eson::Search::BaseQuery.new do
  q = nil
  query do
    q = nested :path => :obj1, :score_mode => "avg" do
      query do
        match_all
      end
      filters do
        range :age, :from => 10, :to => 20
      end
    end
  end

  facets do
    (histogram :hist1, :field => :age, :interval => 2).scope(q, 'my_scope')
  end
end

Caveats

and, not and or are Ruby keywords and can only be used as methods of the receiver is explicit. For that reason, you need to write the following to generate and- and or-filters:

q.filter do |f|
  f.or do
    term "name.first" => "Felix"
    term "name.first" => "Florian"
  end
end

and

q.filter do |f|
  f.and do #and is a keyword, so it needs a receiver
    range :post_date, {:from => "2010-03-01", :to => "2010-04-01"}
    prefix "name.second" => "ba"
  end
end

Due to clever defaults, and can be omitted altogether:

q.filter do |f|
  range :post_date, {:from => "2010-03-01", :to => "2010-04-01"}
  prefix "name.second" => "ba"
end