routing function in Document protocol
This commit is contained in:
parent
8e00d8dabe
commit
ed8a3a07e3
12 changed files with 154 additions and 54 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
|||
# Change Log
|
||||
|
||||
## [v0.5.0](https://github.com/infinitered/elasticsearch-elixir/tree/v0.5.0) (2018-08-01)
|
||||
[Full Changelog](https://github.com/infinitered/elasticsearch-elixir/compare/v0.4.1...v0.5.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add support for routing keys in Document protocol. Add breaking change
|
||||
documentation.
|
||||
|
||||
## [v0.4.1](https://github.com/infinitered/elasticsearch-elixir/tree/v0.4.1) (2018-06-26)
|
||||
[Full Changelog](https://github.com/infinitered/elasticsearch-elixir/compare/v0.4.0...v0.4.1)
|
||||
|
||||
|
@ -90,4 +100,4 @@
|
|||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
||||
|
|
|
@ -141,6 +141,7 @@ protocol.
|
|||
```elixir
|
||||
defimpl Elasticsearch.Document, for: MyApp.Post do
|
||||
def id(post), do: post.id
|
||||
def routing(_), do: false
|
||||
def encode(post) do
|
||||
%{
|
||||
title: post.title,
|
||||
|
|
55
guides/upgrading/0.4.x_to_0.5.x.md
Normal file
55
guides/upgrading/0.4.x_to_0.5.x.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Upgrading from 0.4.x to 0.5.x
|
||||
|
||||
Version `0.5.0` added the `routing` meta-field to be specified in the Document
|
||||
protocol.
|
||||
|
||||
## Rationale
|
||||
|
||||
The `routing`/`_routing` meta-field used to force a document to be hashed to a
|
||||
particular shard which is required for `join` field datatypes but also used to
|
||||
gain more control over which shard your documents are routed to.
|
||||
|
||||
## Changes
|
||||
|
||||
**BREAKING**: `routing` function is now required to be specified in the
|
||||
`Elasticsearch.Document` protocol. You may specify it to return `false` to
|
||||
use default routing (document `id`).
|
||||
|
||||
## How to Update Your App
|
||||
|
||||
Add a `routing/1` function to your `Elasticsearch.Document` implementation.
|
||||
|
||||
# BEFORE
|
||||
defimpl Elasticsearch.Document, for: MyApp.Post do
|
||||
def id(post), do: post.id
|
||||
def encode(post) do
|
||||
%{
|
||||
title: post.title,
|
||||
author: post.author
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# AFTER (using default routing)
|
||||
defimpl Elasticsearch.Document, for: MyApp.Post do
|
||||
def id(post), do: post.id
|
||||
def routing(_), do: false
|
||||
def encode(post) do
|
||||
%{
|
||||
title: post.title,
|
||||
author: post.author
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# AFTER (using routing)
|
||||
defimpl Elasticsearch.Document, for: MyApp.Post do
|
||||
def id(post), do: post.id
|
||||
def routing(post), do: post.account_id
|
||||
def encode(post) do
|
||||
%{
|
||||
title: post.title,
|
||||
author: post.author
|
||||
}
|
||||
end
|
||||
end
|
|
@ -9,7 +9,6 @@ defmodule Elasticsearch do
|
|||
|
||||
alias Elasticsearch.{
|
||||
Document,
|
||||
DocumentMeta,
|
||||
Cluster,
|
||||
Cluster.Config
|
||||
}
|
||||
|
@ -127,12 +126,22 @@ defmodule Elasticsearch do
|
|||
defp document_url(document, index) do
|
||||
url = "/#{index}/_doc/#{Document.id(document)}"
|
||||
|
||||
case DocumentMeta.routing(document) do
|
||||
nil -> url
|
||||
routing -> "#{url}?routing=#{routing}"
|
||||
if routing = Document.routing(document) do
|
||||
document_url_with_routing(url, routing)
|
||||
else
|
||||
url
|
||||
end
|
||||
end
|
||||
|
||||
defp document_url_with_routing(url, routing) do
|
||||
url <>
|
||||
if url =~ ~r/\?/ do
|
||||
"&"
|
||||
else
|
||||
"?"
|
||||
end <> URI.encode_query(%{routing: routing})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Waits for a given Elasticsearch cluster to be available.
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ defmodule Elasticsearch.Index.Bulk do
|
|||
alias Elasticsearch.{
|
||||
Cluster,
|
||||
DataStream,
|
||||
Document,
|
||||
DocumentMeta
|
||||
Document
|
||||
}
|
||||
|
||||
require Logger
|
||||
|
@ -71,10 +70,12 @@ defmodule Elasticsearch.Index.Bulk do
|
|||
"_id" => Document.id(struct)
|
||||
}
|
||||
|
||||
attrs = case DocumentMeta.routing(struct) do
|
||||
nil -> attrs
|
||||
routing -> Map.put(attrs, "_routing", routing)
|
||||
end
|
||||
attrs =
|
||||
if routing = Document.routing(struct) do
|
||||
Map.put(attrs, "_routing", routing)
|
||||
else
|
||||
attrs
|
||||
end
|
||||
|
||||
config.json_library.encode!(%{type => attrs})
|
||||
end
|
||||
|
|
|
@ -40,4 +40,25 @@ defprotocol Elasticsearch.Document do
|
|||
"""
|
||||
@spec encode(any) :: map
|
||||
def encode(item)
|
||||
|
||||
@doc """
|
||||
Returns the Elasticsearch `_routing` for the item. Elasticsearch
|
||||
default if this value is not provided is to use the `_id`.
|
||||
Setting this value to `false` or `nil` will omit sending the
|
||||
meta-field with your requests and use default routing behaviour.
|
||||
Routing allows you to control which shard the document should
|
||||
be directed to which is necessary for `join` fields.
|
||||
|
||||
## Example
|
||||
|
||||
Specify a routing key to control the destination shard, like so:
|
||||
|
||||
def routing(item), do: item.parent_id
|
||||
|
||||
or omit routing and use default Elasticsearch functionality:
|
||||
|
||||
def routing(_), do: false
|
||||
"""
|
||||
@spec routing(any) :: any
|
||||
def routing(item)
|
||||
end
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
defprotocol Elasticsearch.DocumentMeta do
|
||||
@fallback_to_any true
|
||||
@moduledoc """
|
||||
A protocol for converting a struct into an Elasticsearch meta-fields.
|
||||
|
||||
## Example
|
||||
|
||||
defimpl Elasticsearch.DocumentMeta, for: MyStruct do
|
||||
def routing(struct), do: struct.id
|
||||
end
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Returns the Elasticsearch `_routing` for the item. Elasticsearch
|
||||
default if this value is not provided is to use the `_id`.
|
||||
|
||||
## Example
|
||||
|
||||
def routing(item), do: item.id
|
||||
"""
|
||||
@spec routing(any) :: any
|
||||
def routing(item)
|
||||
end
|
||||
|
||||
defimpl Elasticsearch.DocumentMeta, for: Any do
|
||||
def routing(_), do: nil
|
||||
end
|
|
@ -81,7 +81,8 @@ defmodule Elasticsearch.Index.BulkTest do
|
|||
|
||||
describe ".encode!/3" do
|
||||
test "respects _routing meta-field" do
|
||||
assert Bulk.encode!(Cluster, %Comment{id: "my-id", post_id: "123"}, "my-index") =~ "\"_routing\":\"123\""
|
||||
assert Bulk.encode!(Cluster, %Comment{id: "my-id", post_id: "123"}, "my-index") =~
|
||||
"\"_routing\":\"123\""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,31 @@ defmodule ElasticsearchTest do
|
|||
Elasticsearch.delete(Cluster, "/nonexistent")
|
||||
end)
|
||||
end
|
||||
|
||||
describe ".put_document/3" do
|
||||
test "routing meta-field is included if specified in Document" do
|
||||
assert :ok =
|
||||
Elasticsearch.Index.create_from_file(
|
||||
Cluster,
|
||||
"posts-routing",
|
||||
"test/support/settings/posts.json"
|
||||
)
|
||||
|
||||
assert {:ok, _} =
|
||||
Elasticsearch.put_document(
|
||||
Cluster,
|
||||
%Post{id: 1, title: "Example Post", author: "John Smith"},
|
||||
"posts-routing"
|
||||
)
|
||||
|
||||
# If a routing key is not provided, this will throw an {:error, _}
|
||||
# Elasticsearch.Exception: [routing] is missing for join field [doctype]
|
||||
assert {:ok, _} =
|
||||
Elasticsearch.put_document(
|
||||
Cluster,
|
||||
%Comment{id: 2, body: "Example Comment", author: "Jane Smith", post_id: 1},
|
||||
"posts-routing"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ defimpl Elasticsearch.Document, for: Comment do
|
|||
def id(comment), do: comment.id
|
||||
def type(_item), do: "comment"
|
||||
def parent(_item), do: false
|
||||
def routing(comment), do: comment.post_id
|
||||
|
||||
def encode(comment) do
|
||||
%{
|
||||
|
@ -26,7 +27,3 @@ defimpl Elasticsearch.Document, for: Comment do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Elasticsearch.DocumentMeta, for: Comment do
|
||||
def routing(comment), do: comment.post_id
|
||||
end
|
||||
|
|
|
@ -48,23 +48,27 @@ defmodule Elasticsearch.DataCase do
|
|||
|
||||
def random_post_id do
|
||||
case Elasticsearch.Test.Repo.one(
|
||||
from p in Post,
|
||||
order_by: fragment("RANDOM()"),
|
||||
limit: 1
|
||||
) do
|
||||
from(
|
||||
p in Post,
|
||||
order_by: fragment("RANDOM()"),
|
||||
limit: 1
|
||||
)
|
||||
) do
|
||||
nil -> nil
|
||||
post -> post.id
|
||||
end
|
||||
end
|
||||
|
||||
def populate_comments_table(quantity \\ 10) do
|
||||
comments = 0..quantity |> Enum.map(fn _ ->
|
||||
%{
|
||||
body: "Example Comment",
|
||||
author: "Jane Doe",
|
||||
post_id: random_post_id()
|
||||
}
|
||||
end)
|
||||
comments =
|
||||
0..quantity
|
||||
|> Enum.map(fn _ ->
|
||||
%{
|
||||
body: "Example Comment",
|
||||
author: "Jane Doe",
|
||||
post_id: random_post_id()
|
||||
}
|
||||
end)
|
||||
|
||||
Elasticsearch.Test.Repo.insert_all("comments", comments)
|
||||
end
|
||||
|
|
|
@ -13,6 +13,7 @@ defimpl Elasticsearch.Document, for: Post do
|
|||
def id(post), do: post.id
|
||||
def type(_item), do: "post"
|
||||
def parent(_item), do: false
|
||||
def routing(_item), do: false
|
||||
|
||||
def encode(post) do
|
||||
%{
|
||||
|
|
Loading…
Reference in a new issue