search_ash — multilingual Postgres full-text search for Ash
Hi everyone,
I’m building a multi-tenant SaaS (Ash + LiveView + Postgres) and needed a global search across several entity types — products, customers, orders… I wanted to avoid pulling in Elasticsearch/Meilisearch just for that, and couldn’t find an existing Ash-native solution, so I built one and open-sourced it.
search_ash v0.1 is a set of Ash extensions for full-text search on top of Postgres tsvector/ts_rank. No hand-written migrations or SQL. It’s built on two other libs I published alongside it: search_core (the non-Ash pipeline logic) and stemmers (a Rust NIF for Snowball stemming, 15+ languages including French).
It covers two use cases:
1. Per-resource search
defmodule MyApp.Post do
use Ash.Resource,
domain: MyApp.Blog,
data_layer: AshPostgres.DataLayer,
extensions: [SearchAsh]
postgres do
table "posts"
repo MyApp.Repo
end
search do
fields [:title, :body]
language_attribute :language
end
end
This generates, at compile time: a :search_text attribute, a change that keeps it in sync on create/update, a GIN expression index (round-trips cleanly with mix ash_postgres.generate_migrations), and a :search read action.
2. Global cross-entity search
This was my actual use case: searching across multiple resource types from one ranked query. SearchAsh.GlobalIndex + SearchAsh.Source maintain a unified index resource, with tenant-aware identities, soft-delete/archive support, and a :global_search action returning ranked, typed results.
Known 0.1 trade-offs (documented in the README, not surprises):
- Indexing is synchronous (same transaction as the write) — no Oban/async path yet, maybe better to implement the asynch approach in the webapp
- One language per query (no cross-language OR search)
- Bulk updates on per-resource
search doneedstrategy: :stream - Index creation isn’t
CONCURRENTLYyet
There’s a runnable multi-tenant demo in examples/search_demo (Postgres-backed test suite, plus a GreenAsh console for browsing results).
Feedback, issues, and PRs very welcome.






















