ESx
A client for the Elasticsearch with Ecto, written in Elixir
Installation
If available in Hex, the package can be installed as:
-
Add
esxto your list of dependencies inmix.exs:
def deps do
[{:esx, "~> x.x.x"}]
end-
Ensure
esxis started before your application:
def application do
[applications: [:esx]]
endhexdocs: https://hexdocs.pm/esx
Configuration
config :esx, ESx.Model,
url: "http://example.com:9200",
repo: MyApp.Repo # Optional, which defines Ecto for connecting database.Multiple configuration
This is configuration that if you’ve have multiple Elasticsearch’s Endpoint which’s another one.
First, that configuration is defined with ESx.Model.Base into your project. It’s like Ecto’s Repo.
defmodule MyApp.ESx do
use ESx.Model.Base, app: :my_app
end
And so that there’s MyApp.ESx configuration for Mix.config below.
config :my_app, MyApp.ESx,
scheme: "http",
host: "example.com",
port: 9200Definition for all of configuration.
config :esx, ESx.Model,
repo: MyApp.Repo, # Optional, which defines Ecto for connecting database.
protocol: "http", # or: scheme: "http"
user: "yourname", password: "yourpass", # or: userinfo: "yourname:yourpass"
host: "127.0.0.1",
port: 9200,
path: "path-to-endpoint"Definition for Analysis.
DSL
defmodule MyApp.Blog do
use ESx.Schema
index_name "blog" # Optional
document_type "blog" # Optional
mapping _all: [enabled: false], _ttl: [enabled: true, default: "180d"] do
indexes :title, type: "string"
indexes :content, type: "string"
indexes :publish, type: "boolean"
end
settings number_of_shards: 10, number_of_replicas: 2 do
analysis do
filter :ja_posfilter,
type: "kuromoji_neologd_part_of_speech",
stoptags: ["助詞-格助詞-一般", "助詞-終助詞"]
tokenizer :ja_tokenizer,
type: "kuromoji_neologd_tokenizer"
analyzer :default,
type: "custom", tokenizer: "ja_tokenizer",
filter: ["kuromoji_neologd_baseform", "ja_posfilter", "cjk_width"]
end
end
end
Setting by keywords lists
defmodule Something.Schema do
use ESx.Schema
mapping [
_ttl: [
enabled: true,
default: "180d"
],
_all: [
enabled: false
],
properties: [
title: [
type: "string",
analyzer: "ja_analyzer"
],
publish: [
type: "boolean"
],
content: [
type: "string",
analyzer: "ja_analyzer"
]
]
]
settings [
number_of_shards: 1,
number_of_replicas: 0,
analysis: [
analyzer: [
ja_analyzer: [
type: "custom",
tokenizer: "kuromoji_neologd_tokenizer",
filter: ["kuromoji_neologd_baseform", "cjk_width"],
]
]
]
]
endDefinition for updating record via such as a Model.
defmodule MyApp.Blog do
use ESx.Schema
defstruct [:id, :title, :content, :publish]
mapping do
indexes :title, type: "string"
indexes :content, type: "string"
indexes :publish, type: "boolean"
end
endWith Ecto’s Model
defmodule MyApp.Blog do
use MyApp.Web, :model
use ESx.Schema
schema "blogs" do
field :title, :string
field :content, :string
field :publish, :boolean
timestamps
end
mapping do
indexes :title, type: "string"
indexes :content, type: "string"
indexes :publish, type: "boolean"
endIndexing Data
The data’s elements which sends to Elasticsearch is able to customize that will make it, this way is the same as Ecto.
defmodule MyApp.Blog do
@derive {Poison.Encoder, only: [:title, :publish]}
schema "blogs" do
field :title, :string
field :content, :string
field :publish, :boolean
timestamps
end
end
When Ecto’s Schema and ESx’s mapping have defferent fields or for customization more, defining function as_indexed_json will make it in order to send relational data to Elasticsearch, too. Commonly it called via ESx.Model.index_document, ESx.Model.update_document.
defmodule MyApp.Blog do
def as_indexed_json(struct, opts) do
all_of_defined_data = super struct, opts
...
...
some_of_custmized_data
end
endBy default will send all of defined mapping’s fields to Elasticsearch.
API Docs
Transport
ESx.Transport and ESx.Model will connect to multipe elasticsearch automatically if builded cluster systems on your environment.
iex(1)> ESx.Transport.conn # Sniffing cluster system and choose random Elasticsearch connection
01:10:26.694 [debug] curl -X GET 'http://127.0.0.1:9200/_nodes/http' # Run sniffing
%ESx.Transport.Connection{client: HTTPoison, dead: false, # chose one of cluster connection.
dead_since: 1492099826, failures: 0,
pidname: :"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDI=",
resurrect_timeout: 60, url: "http://127.0.0.1:9202"}
iex(2)> ESx.Transport.Connection.conns # Below is all of cluster connections.
[%ESx.Transport.Connection{client: HTTPoison, dead: false,
dead_since: 1492099826, failures: 0,
pidname: :"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDE=",
resurrect_timeout: 60, url: "http://127.0.0.1:9201"},
%ESx.Transport.Connection{client: HTTPoison, dead: false,
dead_since: 1492099826, failures: 0,
pidname: :"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDI=",
resurrect_timeout: 60, url: "http://127.0.0.1:9202"},
%ESx.Transport.Connection{client: HTTPoison, dead: false,
dead_since: 1492099826, failures: 0,
pidname: :"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDA=",
resurrect_timeout: 60, url: "http://127.0.0.1:9200"}]Usage
Indexing
MyApp.ESx.reindex, MyApp.Blog
MyApp.ESx.create_index, MyApp.Blog
MyApp.ESx.delete_index, MyApp.Blog
MyApp.ESx.index_exists?, MyApp.Blog
MyApp.ESx.refresh_index, MyApp.Blog
or
ESx.Model.reindex, MyApp.Blog
ESx.Model.create_index, MyApp.Blog
ESx.Model.delete_index, MyApp.Blog
ESx.Model.index_exists?, MyApp.Blog
ESx.Model.refresh_index, MyApp.Blog
ES Document
MyApp.ESx.import, MyApp.Blog
MyApp.ESx.index_document, %MyApp.Blog{id: 1, title: "egg"}
MyApp.ESx.delete_document, %MyApp.Blog{id: 1, title: "ham"}Search & Response
MyApp.ESx.search, MyApp.Blog, %{query: %{match: %{title: "foo"}}}response =
MyApp.Blog
|> MyApp.ESx.search(%{query: %{match: %{title: "foo"}}})
|> MyApp.ESx.results
IO.inspect Enum.map(response, fn r ->
r["_source"]["title"]
end)
# ["foo", "egg", "some"]With Phoenix’s Ecto
response =
MyApp.Blog
|> MyApp.ESx.search(%{query: %{match: %{title: "foo"}}})
|> MyApp.ESx.records
IO.inspect Enum.each(response, fn r ->
r.title
end)
# ["foo", "egg", "some"]API Docs
Pagination
github.com/ikeikeikeike/scrivener_esx
page =
MyApp.Blog
|> MyApp.ESx.search(%{query: %{match: %{title: "foo"}}})
|> MyApp.ESx.paginate(page: 2, page_size: 5)Low-level APIs
There’re Low-level APIs in ESx.API and ESx.API.Indices.
ts = ESx.Transport.transport trace: true # or: ts = ESx.Model.transport
ESx.API.search ts, %{index: "your_app", body: %{query: %{}}}
ESx.API.Indices.delete ts, %{index: "your_app"}API Docs
Testing
Download elasticsearch and build cluster
$ ./test/build.shrun mix test
$ mix testProbably won’t make it.
- Search DSL