Skip to content

Elixir implementation of Algolia search API

License

Notifications You must be signed in to change notification settings

TheRealReal/algolia-elixir

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Algolia

Build Status Inline docs

This is the elixir implementation of Algolia search API, it is purely functional

Add to your dependencies

  defp deps do
    [{:algolia, "~> 0.8.0"}]
  end

(Pre-Elixir-1.4) Add :algolia to your applications

  def application do
    [applications: [:algolia]]
  end

Configuration

Using environment variables:

ALGOLIA_APPLICATION_ID=YOUR_APPLICATION_ID
ALGOLIA_API_KEY=YOUR_API_KEY
ALGOLIA_HACKNEY_POOL_NAME=YOUR_HACKNEY_POOL_NAME

Using config:

config :algolia,
  application_id: YOUR_APPLICATION_ID,
  api_key: YOUR_API_KEY,
  hackney_pool_name: ALGOLIA_HACKNEY_POOL_NAME

NOTE: You must use ADMIN API_KEY instead of SEARCH API_KEY to enable write access

The Client

You don't need to initiate an index with this client unlike other OO Algolia clients. However, Most of the client search/write functions all use the syntax

operation(index, args....)

So you can easy emulate the index.function() syntax using piping

"my_index" |> operation(args)

Return values

All functions are serialized into maps before returning these responses

  • {:ok, response}
  • {:error, error_code, response}
  • {:error, "Cannot connect to Algolia"}: The client implements retry strategy on all Algolia hosts with increasing timeout, It should only return this error when it has tried all 4 hosts. More Details here.

Examples

Searching

Searching an index

    "my_index" |> search("some query")

With Options

    "my_index" |> search("some query", [attributesToRetrieve: "firstname", hitsPerPage: 20])

See all available search options here

Multiple queries at once

    multi([%{index_name => "my_index1", query: "search query"},
            %{index_name => "my_index2", query: "another query", hitsPerPage: 3,},
            %{index_name => "my_index3", query: "3rd query", tagFilters: "promotion"}])

You can specify a strategy to optimize your multiple queries

  • :none: Execute the sequence of queries until the end.
  • stop_if_enough_matches: Execute the sequence of queries until the number of hits is reached by the sum of hits.
    multi([query1, query2], strategy: :stop_if_enough_matches)

Saving

All save_* operations will overrides the object at the objectID

Save a single object to index without specifying objectID, must have objectID inside object, or use the id_attribute option (see below)

    "my_index" |> save_object(%{objectID: "1"})

Save a single object with a given objectID

    "my_index" |> save_object(%{title: "hello"}, "12345")

Save multiple objects to an index

    "my_index" |> save_objects([%{objectID: "1"}, %{objectID: "2"}])

Updating

Partially updates a single object

    "my_index" |> partial_update_object(%{title: "hello"}, "12345")

Update multiple objects, must have objectID in each object, or use the id_attribute option (see below)

    "my_index" |> partial_update_objects([%{objectID: "1"}, %{objectID: "2"}])

Partial update by default creates a new object if an object does not exist at the objectID, you can turn this off by passing false to the :upsert? option

    "my_index" |> partial_update_object(%{title: "hello"}, "12345", upsert?: false)
    "my_index" |> partial_update_objects([%{id: "1"}, %{id: "2"}], id_attribute: :id, upsert?: false)

Bonus for this Elixir client only: id_attribute option

All write functions such as save_object and partial_update_object comes with an id_attribute option that lets the you specifying an objectID from an existing field in the object, so you do not have to generate it yourself

    "my_index" |> save_object(%{id: "2"}, id_attribute: :id)

It also works for batch operations, such as save_objects and partial_update_objects

    "my_index" |> save_objects([%{id: "1"}, %{id: "2"}], id_attribute: :id)

However, this cannot be used together with an ID specifying argument together

    "my_index" |> save_object(%{id: "1234"}, "1234", id_attribute: :id)
    > Error

Wait for task

All write operations can be waited on by simply piping the response into wait/1

    "my_index" |> save_object(%{id: "123"}) |> wait

Since the client polls the server to check for publishing status, You can specify a time between each tick of the poll, the default is 1000 ms

    "my_index" |> save_object(%{id: "123"}) |> wait(2_000)

You can also use the underlying wait_task function explicitly

    {:ok, %{"taskID" => task_id, "indexName" => index}}
      = "my_index" |> save_object(%{id: "123"}

    wait(index, task_id)

or with option

    wait(index, task_id, 2_000)

Index related operations

Listing all indexes

   list_indexes()

move_index/2

Moves an index to a new one

   move_index(source_index, destination_index)

copy_index/2

Copies an index to a new one

    copy_index(source_index, destination_index)

Clear an index

    clear_index(index)

Telemetry

Request Events

Algolia emits the following telemetry events for each HTTP request:

[:algolia, :request, :start] — Dispatched at the start of the HTTP request

Measurement

system_time - System time when the request started

[:algolia, :request, :stop] — Dispatched after a HTTP request is complete

Measurement

duration - Duration of the HTTP request in native unit

[:algolia, :request, :exception] — Dispatched when the HTTP request encounters an exception.

Measurement

duration - Duration of the HTTP request in native unit
kind - Either exit or error
reason - An Exception or term
stacktrace - Stacktrace of the error. If the kind is exit, it will be an empty list

Common Metadata

In addition to the measurements above, the events share this metadata.

%{
  attempt: "The current number of the attempt. The HTTP request will
  be retried on failure.",
  http: %{
    method: "The HTTP method (e.g. :get, :post) of the request",
    status_code: "The HTTP status code of the response",
    url: "The request url"
  }
  status: ":ok on success, :error on failure"
}

Contributing

To run tests for this repo, you need to set the following ENV variables with credentials from an Algolia test application (ideally a sandbox):

ALGOLIA_APPLICATION_ID
ALGOLIA_API_KEY
ALGOLIA_HACKNEY_POOL_NAME

Settings

get_settings/1

    get_settings(index)

Example response

{:ok,
  %{"minWordSizefor1Typo" => 4,
    "minWordSizefor2Typos" => 8,
    "hitsPerPage" => 20,
    "attributesToIndex" => nil,
    "attributesToRetrieve" => nil,
    "attributesToSnippet" => nil,
    "attributesToHighlight" => nil,
    "ranking" => [
        "typo",
        "geo",
        "words",
        "proximity",
        "attribute",
        "exact",
        "custom"
    ],
    "customRanking" => nil,
    "separatorsToIndex" => "",
    "queryType" => "prefixAll"}
}

set_settings/2

    set_settings(index, %{"hitsPerPage" => 20})

     > %{"updatedAt" => "2013-08-21T13:20:18.960Z",
        "taskID" => 10210332.
        "indexName" => "my_index"}

TODOS:

  • get_object
  • save_object
  • save_objects
  • update_object
  • partial_update_object
  • partial_update_objects
  • delete_object
  • delete_objects
  • list_indexes
  • clear_index
  • wait_task
  • wait (convenience function for piping response into wait_task)
  • set_settings
  • get_settings
  • list_user_keys
  • get_user_key
  • add_user_key
  • update_user_key
  • delete_user_key

About

Elixir implementation of Algolia search API

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Elixir 100.0%