Implementing Full‑Text Search with FaunaDB Indexes in a Serverless Stack
Learn how to add full‑text search to a serverless app using FaunaDB's native search indexes, with a complete FQL example, performance considerations, and practical verification steps.
09 Apr 2026, 05:09 UTC

The problem: search without a separate service
Serverless applications often need a lightweight way to search text stored in a database. Adding Elasticsearch or Algolia introduces extra infrastructure, latency, and cost. FaunaDB ships a built‑in full‑text search index that lives alongside your data, so you can query it with the same FQL (Fauna Query Language) you already use for CRUD.
Defining a search index
An index is created once with a search clause that lists the fields to tokenize and the language for stemming. The index stores a term vector for each document, which Fauna uses to rank results by relevance.
// Run in Fauna Shell or the Dashboard query editor with an admin key
CreateIndex({
name: "posts_by_content",
source: Collection("Posts"),
search: {
fields: ["title", "body"],
language: "en" // optional, defaults to English
}
})
Required permission: an admin or server key that can create indexes. The source collection must already exist. After the command finishes, the index appears in the dashboard under Indexes and is ready for queries.
Querying the index
Use Match against the index name and pass the search term. Fauna returns a set of references ordered by an internal relevance score. You can paginate or filter further with Paginate and Filter.
// Example: find posts mentioning "serverless"
Map(
Paginate(Match(Index("posts_by_content"), "serverless"), { size: 10 }),
Lambda(["ref", "score"],
Get(Var("ref"))
)
)
The lambda receives two values: the document ref and the numeric score. Higher scores indicate stronger matches. The size parameter caps the page size; default is 64.
Worked example: end‑to‑end
- Create the collection (once):
CreateCollection({ name: "Posts" }) - Insert sample documents:
Create(Collection("Posts"), { data: { title: "Getting started with serverless", body: "Serverless functions scale automatically..." } }) Create(Collection("Posts"), { data: { title: "FaunaDB indexing basics", body: "Indexes make queries fast and expressive." } }) - Create the search index (as shown above).
- Run a search:
Expected result: the first document appears with a higher score than the second. Verify by checking theMap( Paginate(Match(Index("posts_by_content"), "serverless"), { size: 5 }), Lambda(["ref", "score"], { ref: Var("ref"), score: Var("score"), data: Select(["data"], Get(Var("ref"))) }) )scorefield in the response.
Risk note: Building the index on a large existing collection can take minutes and consumes write ops. Plan the creation during low‑traffic windows.
Trade‑offs and limitations
- Static indexes – any change to an indexed field triggers a re‑index of that document. Bulk updates on millions of rows can be expensive.
- Term length limit – each search term may not exceed 1,024 characters. Longer queries must be split or truncated.
- No fuzzy matching – Fauna does not support approximate or typo‑tolerant search out of the box. Consider a secondary service if that is required.
- Relevance algorithm fixed – you cannot tweak weighting per field; the built‑in TF‑IDF‑style scoring is the only option.
- Storage overhead – term vectors add roughly 30‑50 % extra storage per indexed field.
How to verify it works in your environment
After creating the index, run a simple Match query for a known term and confirm two things:
- The response includes a
scorenumber for each hit. - The returned documents contain the search term in the indexed fields.
If scores look reasonable and latency is sub‑100 ms for typical page sizes, the index is ready for production traffic.
Next steps
Add the index creation to your migration scripts so new environments get it automatically. Monitor write‑op consumption during heavy update periods, and evaluate whether the 1,024‑character term limit affects your query patterns. If you need fuzzy or synonym support later, you can layer a lightweight external search service without rewriting your data model.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.