Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 27 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,32 @@ Add `elasticsearch` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:elasticsearch, "~> 0.1.1"}
{:elasticsearch, "~> 0.2.0"}
]
end
```

Then, create an `Elasticsearch.Cluster` in your application:

```elixir
defmodule MyApp.ElasticsearchCluster do
use Elasticsearch.Cluster, otp_app: :my_app
end
```

## Configuration

See the annotated example configuration below.

```elixir
config :elasticsearch,
config :my_app, MyApp.ElasticsearchCluster,
# The URL where Elasticsearch is hosted on your system
url: "http://localhost:9200", # or {:system, "ELASTICSEARCH_URL"}
url: "http://localhost:9200",

# If your Elasticsearch cluster uses HTTP basic authentication,
# specify the username and password here:
username: "username", # or {:system, "ELASTICSEARCH_USERNAME"}
password: "password", # or {:system, "ELASTICSEARCH_PASSWORD"}
username: "username",
password: "password",

# When indexing data using the `mix elasticsearch.build` task,
# control the data ingestion rate by raising or lowering the number
Expand All @@ -49,7 +57,7 @@ config :elasticsearch,
# If you want to mock the responses of the Elasticsearch JSON API
# for testing or other purposes, you can inject a different module
# here. It must implement the Elasticsearch.API behaviour.
api_module: Elasticsearch.API.HTTP,
api: Elasticsearch.API.HTTP,

# Customize the library used for JSON encoding/decoding.
json_library: Poison, # or Jason
Expand Down Expand Up @@ -83,7 +91,7 @@ config :elasticsearch,
}
```

## Protocols & Behaviours
## Protocols and Behaviours

#### Elasticsearch.Store

Expand Down Expand Up @@ -130,8 +138,8 @@ This can be used in test mode, for example:

```elixir
# config/test.exs
config :elasticsearch,
api_module: MyApp.ElasticsearchMock
config :my_app, MyApp.ElasticsearchCluster,
api: MyApp.ElasticsearchMock
```

Your mock can then stub requests and responses from Elasticsearch.
Expand All @@ -140,7 +148,8 @@ Your mock can then stub requests and responses from Elasticsearch.
defmodule MyApp.ElasticsearchMock do
@behaviour Elasticsearch.API

def get("/posts/1", _headers, _opts) do
@impl true
def request(_config, :get, "/posts/1", _data, _opts) do
{:ok, %HTTPoison.Response{
status_code: 404,
body: %{
Expand All @@ -161,37 +170,37 @@ hot-swap technique with Elasticsearch aliases.
```bash
# This will read the `indexes[posts]` configuration seen above, to build
# an index, `posts-123123123`, which will then be aliased to `posts`.
$ mix elasticsearch.build posts
$ mix elasticsearch.build posts --cluster MyApp.ElasticsearchCluster
```

See the docs on `Mix.Tasks.Elasticsearch.Build` and `Elasticsearch.Index`
for more details.

#### Individual Documents

Use `Elasticsearch.put_document/2` to upload a document to a particular index.
Use `Elasticsearch.put_document/3` to upload a document to a particular index.

```elixir
# MyApp.Post must implement Elasticsearch.Document
Elasticsearch.put_document(%MyApp.Post{}, "index-name")
Elasticsearch.put_document(MyApp.ElasticsearchCluster, %MyApp.Post{}, "index-name")
```

To remove documents, use `Elasticsearch.delete_document/2`:
To remove documents, use `Elasticsearch.delete_document/3`:

```elixir
Elasticsearch.delete_document(%MyApp.Post{}, "index-name")
Elasticsearch.delete_document(MyApp.ElasticsearchCluster, %MyApp.Post{}, "index-name")
```

## Querying

You can query Elasticsearch the `post/2` function:
You can query Elasticsearch the `post/3` function:

```elixir
# Raw query
Elasticsearch.post("/posts/post/_search", '{"query": {"match_all": {}}}')
Elasticsearch.post(MyApp.ElasticsearchCluster, "/posts/post/_search", '{"query": {"match_all": {}}}')

# Using a map
Elasticsearch.post("/posts/post/_search", %{"query" => %{"match_all" => %{}}})
Elasticsearch.post(MyApp.ElasticsearchCluster, "/posts/post/_search", %{"query" => %{"match_all" => %{}}})
```

See the official Elasticsearch [documentation](https://www.elastic.co/guide/en/elasticsearch/reference/6.x/index.html)
Expand Down
8 changes: 8 additions & 0 deletions bin/coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
#
# Coverage Report
#
# Generates and opens a code coverage report for the entire project.

mix coveralls.html || { echo 'Tests failed!'; exit 1; }
open cover/excoveralls.html
4 changes: 0 additions & 4 deletions bin/test
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ MIX_ENV=test mix format --check-formatted || { echo 'Please format code using `m
MIX_ENV=test mix compile --warnings-as-errors --force || { echo 'Please fix all compiler warnings.'; exit 1; }
MIX_ENV=test mix docs || { echo 'Elixir HTML docs were not generated!'; exit 1; }

if [ ! $CI ]; then
MIX_ENV=test mix dialyze || { echo 'Dialyzer checks failed.'; exit 1; }
fi

if [ $CI ]; then
mix coveralls.travis || { echo 'Elixir tests failed!'; exit 1; }
else
Expand Down
13 changes: 13 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,16 @@ config :elasticsearch,
sources: [Post]
}
}

config :elasticsearch, Elasticsearch.Test.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "elasticsearch_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox,
priv: "test/support/"

config :elasticsearch, ecto_repos: [Elasticsearch.Test.Repo]

config :logger, level: :warn
11 changes: 11 additions & 0 deletions coveralls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"skip_files": [
"lib/elasticsearch/api/api.ex",
"lib/elasticsearch/storage/store.ex",
"lib/mix/elasticsearch.install.ex",
"test/support/*"
],
"coverage_options": {
"treat_no_relevant_lines_as_covered": true
}
}
104 changes: 104 additions & 0 deletions guides/upgrading/0.1.x_to_0.2.x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Upgrading from 0.1.x to 0.2.x

Version 0.2.0 brings this library in line with Elixir best practices for
configuration.

## Rationale

Configuration is no longer global. Instead, you configure
`Elasticsearch.Cluster` modules, which are `GenServer`s. This has several
benefits:

1. A single OTP app can talk to multiple Elasticsearch clusters. This is
useful for umbrella apps in particular.

2. Reading from environment variables is standardized. You do it by
overriding the cluster's `init/1` callback, as in Ecto and Phoenix.

3. You can start clusters like any other genserver, passing in
configuration at that time.

4. You can bypass clusters altogether by passing configuration instead
of a cluster to any function that expects a cluster.

## Changes

**BREAKING**: an `Elasticsearch.Cluster` module or configuration map is now
required for all `Elasticsearch` function calls.

**BREAKING**: `{:system, "ENV_VAR"}` is no longer supported in configuration.
Instead, you should read from environment variables in the `init/1` callback
as described in the `Elasticsearch.Cluster` documentation.

**BREAKING**: The `:api_module` configuration option has been renamed
to `:api`.

**BREAKING**: The required callbacks for `Elasticsearch.API` have been
simplified down to a single `request/5` function.

**BREAKING**: `mix elasticsearch.build` now requires the `--cluster` option.

## How to Update Your App

First, add a `Cluster` to your application:

defmodule MyApp.ElasticsearchCluster do
use Elasticsearch.Cluster, otp_app: :my_app
end

Next, switch over your `:elasticsearch` configuration over to your
application, and include the cluster. Be sure to rename the `:api_module`
option to `:api`. See `Elasticsearch.Cluster` for more options.

# BEFORE
config :elasticsearch,
url: "http://localhost:9200",
api_module: Elasticsearch.API.HTTP,
# ...

# AFTER
config, :my_app, MyApp.ElasticsearchCluster,
url: "http://localhost:9200",
api: Elasticsearch.API.HTTP,
# ...

Next, be sure to start your Cluster in your application supervisor:

children = [
MyApp.ElasticsearchCluster
]

Next, add your cluster to all calls to `Elasticsearch` functions:

# BEFORE
Elasticsearch.post("/posts/_search", %{...})

# AFTER
Elasticsearch.post(MyApp.ElasticsearchCluster, "/posts/_search", %{...})

Next, update any mock implementations of `Elasticsearch.API` to implement the
`request/5` function instead of the previous functions.

# BEFORE
defmodule MyApp.ElasticsearchMock do
@behaviour Elasticsearch.API

def get("/url", _opts) do
{:ok, %HTTPoison.Response{}}
end
end

# AFTER
defmodule MyApp.ElasticsearchMock do
@behaviour Elasticsearch.API

@impl true
def request(_config, :get, "/url", _data, _opts) do
{:ok, %HTTPoison.Response{}}
end
end

Finally, update any calls to `mix elasticsearch.build` to include the cluster
as an option:

mix elasticsearch.build [index] --cluster MyApp.ElasticsearchCluster
Loading