The real DSL for ElasticSearch in Elixir
Since ElasticSearch has defined a DSL using JSON, why bother writing another lib to write a DSL
for another DSL? The answer is JSON is too verbose. Especially, the map expression in Elixir
makes the situation worse(%{}
instead of {}
, =>
instead of :
).
Considering a ElasticSearch query is just a series of nested function calls, why don't we just use function calls in the Elixir to emulate the ES Query DSL?
Here is a quick compare using a bool query example from ES Official site
Firstly, our function-based DSL
query(
bool(
must: term(user: "kimchy"),
filter: term(tag: "tech"),
must_not: range(age: [from: 10, to: 20]),
should: [term(tag: "wow"), term(tag: "elasticsearch")],
minimum_should_match: 1,
boost: 1.0
)
)
Here the original query expression
{
"query": {
"bool" : {
"must" : {
"term" : { "user" : "kimchy" }
},
"filter": {
"term" : { "tag" : "tech" }
},
"must_not" : {
"range" : {
"age" : { "from" : 10, "to" : 20 }
}
},
"should" : [
{ "term" : { "tag" : "wow" } },
{ "term" : { "tag" : "elasticsearch" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
and the Elixir version
%{"query"=> %{
"bool" => %{
"must" => %{
"term" => %{ "user" => "kimchy" }
},
"filter"=> %{
"term" => %{ "tag" => "tech" }
},
"must_not" => %{
"range" => %{
"age" => %{ "from" => 10, "to" => 20 }
}
},
"should" => [
%{ "term" => %{ "tag" => "wow" } },
%{ "term" => %{ "tag" => "elasticsearch" } }
],
"minimum_should_match" => 1,
"boost" => 1.0
}
}
}
I think the result is clear enough, our solution wins out with more concise expression, and less LOC.
Besides, we provided a simple ElasticSearch abstraction helping you to organize your ElasticSearch related code.
Still under construction, wants to be an early bird?
Add elaxto
to your list of dependencies in mix.exs
:
```elixir
def deps do
[{:elaxto, github: "secretworry/elaxto", branch: :master}]
end
```
Just import Elaxto.Query
in your module, you can use the macro query/1
and suggest/1
to build ElasticSearch query.
defmodule MyApp.UserIndex do
import Elaxto.Query
def search_by_user_name(user_name) do
MyApp.Elacto.post({:my_app, :user},
query(
term(user_name: ^user_name) # Don't forget to use the escape character ^ here
)
)
end
end
Now you can defining ES Query like invoking a bunch of elixir calls, but here's some hint.
- Since it's still a elixir call, you can only put the keyword-like args at the tail of args
To construct a query like this
javascript {"query": { "bool": {"boot": 1.0, "must": { "term": {"user": "name"}}}}}
instead of adding theboot
args at the front of thebool
call, you have to push it to the end of argument list, like thiselixir query(bool(must: term(user: "name"), boot: 1.0)) # instead of # query(boot: 1.0, bool(must: term(user: "name")))
- Use
^
to escape variables in the queryelixir name = "Alice" query(term(user_name: ^name))
- Be conscious about how the queries should be composed. We can compose two queries in two ways, the first is to let them merge into a list, the second would be composing them into a map.
```elixir
# We archive the first one through wrapping the two query into an array
bool(
should: [term(tag: "elaxto"), term(tag: "woo")]
)
# generates: {should: [ {term: {tag: "elaxto"}, {term: {tag: "woo"}]}
# We archive the second on through wrapping the two query into an double-array `[[` and `]]
bool(
must: [[term(tag: "elaxto", match(message: "awesome")]]
)
# generates: {must: {term: {tag: "elaxto"}, match: {message: "awesome"}}}
```
Code for this section is still under frequently reconstruction
- Define your own
Elaxto.Http.Adapter
or use the providedElaxto.Http.Adapters.Maxwell
(thanks to Maxwell a awesome http client adapter) - Define a
Elaxto
in your projectelixir defmodule MyApp.Elaxto do use Elaxto, otp_app: :my_app end
- Add configuration for the
Elaxto
in your config fileelixir config :my_app, MyApp.Elaxto, http_adapter: Elaxto.Http.Adapters.Maxwell
you can also pass options to the adapter in the configelixir config :my_app, MyApp.Elaxto, http_adapter: {Elaxto.Http.Adapters.Maxwell, [key: value]}
The available options are
* `http_adapter` - (required) the Http Adapter used for the Elaxto, you can config different adapter for different env
* `host` - (optional) the host & port for ElasticSearch server, defaults to `http://localhost:9200`
- Now you can use
get/1
,post/2
,put/2
,delete/1
to interact with the server
An index schema defines how to create an index, and how to convert models into documents of ES
(To Be Continued)