Skip to content

preciz/meili

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Meili

An idiomatic, high-performance, and lightweight Meilisearch client for Elixir built on top of Req and Finch.

Why Meili?

  • No Bloat: Built directly on Req, leveraging connection pooling and HTTP/2 stream multiplexing out-of-the-box. No heavy middleware layers.
  • Idiomatic Elixir:
    • Provides both a global/default client configuration (via Application environment) and explicit client structs (for multi-tenant setups).
    • Automatically converts Elixir-style snake_case keys to Meilisearch-style camelCase keys for query parameters, search options, and settings.
    • Standard operations return {:ok, result} / {:error, error} and suffix-raising bang (!) functions (e.g., search!/3).
    • Custom exception structures (Meili.Error) wrap Meilisearch-specific API errors (code, type, message, link).
  • Asynchronous Polling: Includes a robust wait_for_task!/3 helper that polls tasks with backoff and raises clear error messages if tasks fail.

Installation

Add meili to your list of dependencies in mix.exs:

def deps do
  [
    {:meili, "~> 0.2.0"}
  ]
end

Configuration

In your config/config.exs or config/runtime.exs:

config :meili,
  url: System.get_env("MEILI_HTTP_ADDR") || "http://localhost:7700",
  key: System.get_env("MEILI_MASTER_KEY")

Quick Start

Global Client Mode

If you're using a single Meilisearch instance, configure your credentials in the config and call operations directly:

# Create an index
{:ok, task} = Meili.create_index("movies", primary_key: "id")
# Block until index creation completes
Meili.wait_for_task!(task["taskUid"])

# Add documents
documents = [
  %{id: 1, title: "Star Wars: Episode IV", genre: "Sci-Fi"},
  %{id: 2, title: "The Dark Knight", genre: "Action"}
]
{:ok, task} = Meili.add_documents("movies", documents)
Meili.wait_for_task!(task["taskUid"])

# Search documents (note: snake_case parameters are automatically converted to camelCase)
{:ok, results} = Meili.search("movies", "star wars", limit: 5, show_ranking_score: true)
IO.inspect(results["hits"])

Explicit Client Mode (Multi-tenant / Dynamic Setup)

If you need to connect to multiple instances dynamically:

client = Meili.client(url: "https://my-meili-instance.com", key: "secret-key")

# Perform search with the explicit client
{:ok, results} = Meili.search(client, "movies", "batman")

Settings Management

settings = %{
  searchable_attributes: ["title", "overview"],
  filterable_attributes: ["genre"],
  ranking_rules: ["words", "typo", "proximity"]
}

{:ok, task} = Meili.Settings.update("movies", settings)
Meili.wait_for_task!(task["taskUid"])

Advanced Req Options

Since Meili is built on Req, you can pass custom options directly down to the underlying HTTP client using :req_options. This is useful for adjusting timeouts, enabling compression, configuring connection pooling, or modifying how JSON is parsed.

client = Meili.Client.new(
  url: "http://localhost:7700",
  key: "secret-key",
  req_options: [
    receive_timeout: 60_000,
    compress_body: true,
    finch: MyApp.MeiliFinch
  ]
)

Note: meili preserves the raw Meilisearch payloads and returns maps with string keys (e.g., task["taskUid"]). String keys are enforced to prevent VM memory exhaustion (atoms are not garbage collected).

Migration from meilisearch_ex

If you are porting an existing codebase from meilisearch_ex, here is a quick mapping of common functions:

meilisearch_ex meili
Meilisearch.Index.create(uid, primaryKey: pk) Meili.Index.create(client, uid, primary_key: pk)
Meilisearch.Document.add_or_replace(idx, docs) Meili.Document.add_or_replace(client, idx, docs)
Meilisearch.Document.delete(idx, id) Meili.Document.delete(client, idx, id)
Meilisearch.Settings.update(idx, settings) Meili.Settings.update(client, idx, settings)
Meilisearch.Search.search(idx, query, opts) Meili.Search.search(client, idx, query, opts)

Testing

This library comes with a mock-server testing suite built using Bypass. You can run the tests locally without needing a live Meilisearch instance:

mix test

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages