{"id":20941735,"url":"https://github.com/ikeikeikeike/esx","last_synced_at":"2025-10-10T02:35:35.232Z","repository":{"id":47010438,"uuid":"71870483","full_name":"ikeikeikeike/esx","owner":"ikeikeikeike","description":"A client for the Elasticsearch with Ecto, written in Elixir","archived":false,"fork":false,"pushed_at":"2023-06-29T10:53:56.000Z","size":182,"stargazers_count":27,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-18T00:20:57.408Z","etag":null,"topics":["ecto","elasticsearch","elixir"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ikeikeikeike.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-25T07:24:01.000Z","updated_at":"2024-12-12T11:35:41.000Z","dependencies_parsed_at":"2022-09-12T02:11:53.569Z","dependency_job_id":null,"html_url":"https://github.com/ikeikeikeike/esx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikeikeikeike%2Fesx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikeikeikeike%2Fesx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikeikeikeike%2Fesx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikeikeikeike%2Fesx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ikeikeikeike","download_url":"https://codeload.github.com/ikeikeikeike/esx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254043217,"owners_count":22004912,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecto","elasticsearch","elixir"],"created_at":"2024-11-18T23:17:46.123Z","updated_at":"2025-10-10T02:35:30.215Z","avatar_url":"https://github.com/ikeikeikeike.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ESx\n\n[![Build Status](http://img.shields.io/travis/ikeikeikeike/esx.svg?style=flat-square)](http://travis-ci.org/ikeikeikeike/esx)\n[![Ebert](https://ebertapp.io/github/ikeikeikeike/esx.svg)](https://ebertapp.io/github/ikeikeikeike/esx)\n[![Hex version](https://img.shields.io/hexpm/v/esx.svg \"Hex version\")](https://hex.pm/packages/esx)\n[![Inline docs](https://inch-ci.org/github/ikeikeikeike/esx.svg)](http://inch-ci.org/github/ikeikeikeike/esx)\n[![Lisence](https://img.shields.io/hexpm/l/ltsv.svg)](https://github.com/ikeikeikeike/esx/blob/master/LICENSE)\n\nA client for the Elasticsearch with Ecto, written in Elixir\n\n## Installation\n\nIf [available in Hex](https://hex.pm/docs/publish), the package can be installed as:\n\n1. Add `esx` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [{:esx, \"~\u003e x.x.x\"}]\nend\n```\n\n2. Ensure `esx` is started before your application:\n\n```elixir\ndef application do\n  [applications: [:esx]]\nend\n```\n\nhexdocs: https://hexdocs.pm/esx\n\n## Configuration\n\n###### This is configuration that if you've have multiple Elasticsearch's Endpoint which's another one.\n\nFirst, that configuration is defined with `ESx.Model.Base` into your project. It's like Ecto's Repo.\n\n```elixir\ndefmodule MyApp.ESx do\n  use ESx.Model.Base, app: :my_app\nend\n```\n\nAnd so that there's `MyApp.ESx` configuration for Mix.config below.\n\n```elixir\nconfig :my_app, MyApp.ESx,\n  scheme: \"http\",\n  host: \"example.com\",\n  port: 9200\n```\n\n#### Definition for all of configuration.\n\n```elixir\nconfig :my_app, MyApp.ESx,\n  repo: MyApp.Repo,                        # Optional, which defines Ecto for connecting database.\n  protocol: \"http\",                        # or: scheme: \"http\"\n  user: \"yourname\", password: \"yourpass\",  # or: userinfo: \"yourname:yourpass\"\n  host: \"127.0.0.1\",\n  port: 9200,\n  path: \"path-to-endpoint\"\n```\n\n## Definition for Analysis.\n\n#### DSL\n```elixir\ndefmodule MyApp.Blog do\n  use ESx.Schema\n\n  index_name    \"blog\"  # Optional\n  document_type \"blog\"  # Optional\n\n  mapping _all: [enabled: false], _ttl: [enabled: true, default: \"180d\"] do\n    indexes :title, type: \"string\"\n    indexes :content, type: \"string\"\n    indexes :publish, type: \"boolean\"\n  end\n\n  settings number_of_shards: 10, number_of_replicas: 2 do\n    analysis do\n      filter :ja_posfilter,\n        type: \"kuromoji_neologd_part_of_speech\",\n        stoptags: [\"助詞-格助詞-一般\", \"助詞-終助詞\"]\n      tokenizer :ja_tokenizer,\n        type: \"kuromoji_neologd_tokenizer\"\n      analyzer :default,\n        type: \"custom\", tokenizer: \"ja_tokenizer\",\n        filter: [\"kuromoji_neologd_baseform\", \"ja_posfilter\", \"cjk_width\"]\n    end\n  end\n\nend\n\n```\n#### Setting by keywords lists\n\n```elixir\ndefmodule Something.Schema do\n  use ESx.Schema\n\n  mapping [\n    _ttl: [\n      enabled: true,\n      default: \"180d\"\n    ],\n    _all: [\n      enabled: false\n    ],\n    properties: [\n      title: [\n        type: \"string\",\n        analyzer: \"ja_analyzer\"\n      ],\n      publish: [\n        type: \"boolean\"\n      ],\n      content: [\n        type: \"string\",\n        analyzer: \"ja_analyzer\"\n      ]\n    ]\n  ]\n\n  settings [\n    number_of_shards:   1,\n    number_of_replicas: 0,\n    analysis: [\n      analyzer: [\n        ja_analyzer: [\n          type:      \"custom\",\n          tokenizer: \"kuromoji_neologd_tokenizer\",\n          filter:    [\"kuromoji_neologd_baseform\", \"cjk_width\"],\n        ]\n      ]\n    ]\n  ]\n\nend\n```\n\n## Definition for updating record via such as a Model.\n\n```elixir\ndefmodule MyApp.Blog do\n  use ESx.Schema\n\n  defstruct [:id, :title, :content, :publish]\n\n  mapping do\n    indexes :title, type: \"string\"\n    indexes :content, type: \"string\"\n    indexes :publish, type: \"boolean\"\n  end\nend\n```\n\n#### With Ecto's Model\n\n```elixir\ndefmodule MyApp.Blog do\n  use MyApp.Web, :model\n  use ESx.Schema\n\n  schema \"blogs\" do\n    field :title, :string\n    field :content, :string\n    field :publish, :boolean\n\n    timestamps\n  end\n\n  mapping do\n    indexes :title, type: \"string\"\n    indexes :content, type: \"string\"\n    indexes :publish, type: \"boolean\"\n  end\n```\n\n###### Indexing Data\n\nThe data's elements which sends to Elasticsearch is able to customize that will make it, this way is the same as Ecto.\n\n```elixir\ndefmodule MyApp.Blog do\n  @derive {Poison.Encoder, only: [:title, :publish]}\n  schema \"blogs\" do\n    field :title, :string\n    field :content, :string\n    field :publish, :boolean\n\n    timestamps\n  end\nend\n```\n\nWhen 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 `MyApp.ESx.index_document`, `MyApp.ESx.update_document`.\n\n```elixir\ndefmodule MyApp.Blog do\n  def as_indexed_json(struct, opts) do\n    all_of_defined_data = super struct, opts\n    ...\n    ...\n\n    some_of_custmized_data\n  end\nend\n```\n\nBy default will send all of defined mapping's fields to Elasticsearch.\n\n###### API Docs\n\n- https://hexdocs.pm/esx/ESx.Schema.html\n\n## Transport\n\n`ESx.Transport` and `MyApp.ESx` will connect to multipe elasticsearch automatically if builded cluster systems on your environment.\n```elixir\niex(1)\u003e MyApp.ESx.transport # Load configuration to ESX.Transport this is required.\niex(2)\u003e ESx.Transport.conn  # Sniffing cluster system and choose random Elasticsearch connection\n\n01:10:26.694 [debug] curl -X GET 'http://127.0.0.1:9200/_nodes/http'  # Run sniffing\n\n%ESx.Transport.Connection{client: HTTPoison, dead: false, # chose one of cluster connection.\n dead_since: 1492099826, failures: 0,\n pidname: :\"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDI=\",\n resurrect_timeout: 60, url: \"http://127.0.0.1:9202\"}\n\niex(2)\u003e ESx.Transport.Connection.conns  # Below is all of cluster connections.\n[%ESx.Transport.Connection{client: HTTPoison, dead: false,\n  dead_since: 1492099826, failures: 0,\n  pidname: :\"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDE=\",\n  resurrect_timeout: 60, url: \"http://127.0.0.1:9201\"},\n %ESx.Transport.Connection{client: HTTPoison, dead: false,\n  dead_since: 1492099826, failures: 0,\n  pidname: :\"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDI=\",\n  resurrect_timeout: 60, url: \"http://127.0.0.1:9202\"},\n %ESx.Transport.Connection{client: HTTPoison, dead: false,\n  dead_since: 1492099826, failures: 0,\n  pidname: :\"RWxpeGlyLkVTeC5UcmFuc3BvcnQuQ29ubmVjdGlvbl9odHRwOi8vMTI3LjAuMC4xOjkyMDA=\",\n  resurrect_timeout: 60, url: \"http://127.0.0.1:9200\"}]\n```\n\n\n## Usage\n\n### Indexing\n\n```elixir\nMyApp.ESx.reindex, MyApp.Blog\nMyApp.ESx.create_index, MyApp.Blog\nMyApp.ESx.delete_index, MyApp.Blog\nMyApp.ESx.index_exists?, MyApp.Blog\nMyApp.ESx.refresh_index, MyApp.Blog\n\n```\n\n### ES Document\n\n```elixir\nMyApp.ESx.import, MyApp.Blog\nMyApp.ESx.index_document, %MyApp.Blog{id: 1, title: \"egg\"}\nMyApp.ESx.delete_document, %MyApp.Blog{id: 1, title: \"ham\"}\n```\n\n### Search \u0026 Response\n\n```elixir\nMyApp.ESx.search, MyApp.Blog, %{query: %{match: %{title: \"foo\"}}}\n```\n\n```elixir\nresponse =\n  MyApp.Blog\n  |\u003e MyApp.ESx.search(%{query: %{match: %{title: \"foo\"}}})\n  |\u003e MyApp.ESx.results\n\nIO.inspect Enum.map(response, fn r -\u003e\n  r[\"_source\"][\"title\"]\nend)\n# [\"foo\", \"egg\", \"some\"]\n```\n\n##### With Phoenix's Ecto\n\n```elixir\nresponse =\n  MyApp.Blog\n  |\u003e MyApp.ESx.search(%{query: %{match: %{title: \"foo\"}}})\n  |\u003e MyApp.ESx.records\n\nIO.inspect Enum.each(response, fn r -\u003e\n  r.title\nend)\n# [\"foo\", \"egg\", \"some\"]\n```\n\n###### API Docs\n\n- https://hexdocs.pm/esx/MyApp.ESx.html\n\n##### Pagination\n\n[github.com/ikeikeikeike/scrivener_esx](https://github.com/ikeikeikeike/scrivener_esx)\n\n```elixir\npage =\n  MyApp.Blog\n  |\u003e MyApp.ESx.search(%{query: %{match: %{title: \"foo\"}}})\n  |\u003e MyApp.ESx.paginate(page: 2, page_size: 5)\n```\n\n\n## Low-level APIs\n\n#### Configuration\n\n```elixir\nconfig :esx, ESx.Model,\n  url: \"http://example.com:9200\"\n```\n\nThere're Low-level APIs in `ESx.API` and `ESx.API.Indices`.\n\n```elixir\nts = ESx.Transport.transport trace: true  # or: ts = MyApp.ESx.transport\n\nESx.API.search ts, %{index: \"your_app\", body: %{query: %{}}}\n\nESx.API.Indices.delete ts, %{index: \"your_app\"}\n```\n\n###### API Docs\n\n- https://hexdocs.pm/esx/ESx.API.html\n- https://hexdocs.pm/esx/ESx.API.Indices.html\n\n\n## Testing\n\nDownload elasticsearch and build cluster\n\n```ruby\n$ ./test/build.sh\n```\n\nrun mix test\n\n```ruby\n$ mix test\n```\n\n\nProbably won't make it.\n- Search DSL\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikeikeikeike%2Fesx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fikeikeikeike%2Fesx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikeikeikeike%2Fesx/lists"}