Muninn.Searcher (Muninn v0.5.5)
View SourceSearcher executes queries against an index and returns results.
The Searcher is obtained from an IndexReader and provides the main search functionality. It executes queries and returns ranked results.
Usage
{:ok, index} = Muninn.Index.open("/path/to/index")
{:ok, reader} = Muninn.IndexReader.new(index)
{:ok, searcher} = Muninn.Searcher.new(reader)
# Execute a search
query = Muninn.Query.term("title", "elixir")
{:ok, results} = Muninn.Searcher.search(searcher, query, limit: 10)Search Results
Results are returned as a Muninn.SearchResult struct containing:
total_hits: The number of matching documentshits: A list ofMuninn.SearchHitstructs, each with:score: Relevance score (higher is better)doc: Map of stored field values
Only fields marked as stored: true in the schema will be included
in the returned documents.
Summary
Functions
Executes aggregations over documents matching a query.
Counts documents matching a query without retrieving them.
Creates a new Searcher from an IndexReader.
Executes a search query and returns results.
Performs fuzzy search for terms within a specified Levenshtein distance.
Performs fuzzy prefix search combining autocomplete with typo tolerance.
Performs fuzzy search with highlighted snippets showing matched terms.
Finds documents similar to the provided document fields.
Performs a prefix search for autocomplete/typeahead functionality.
Executes a search using natural query syntax.
Executes a search sorted by a fast field value instead of relevance score.
Performs a range query on an f64 field.
Performs a range query on an i64 field.
Performs a range query on a u64 field.
Performs a regex search on a specific field.
Executes a search with highlighted snippets showing matching words in context.
Types
@type t() :: reference()
Functions
@spec aggregate(t(), String.t(), [String.t()], map() | String.t(), keyword()) :: {:ok, map()} | {:error, String.t()}
Executes aggregations over documents matching a query.
Uses Tantivy's aggregation framework. Aggregated fields must have fast: true
in the schema.
Parameters
searcher- The searcher to usequery_string- Query to scope which documents are aggregated (use"*"for all)default_fields- Default fields for the query parseraggregations- Aggregation request as a map (from builder DSL) or JSON stringopts- Reserved for future options
Examples
aggs = %{
"avg_price" => %{"avg" => %{"field" => "price"}}
}
{:ok, results} = Muninn.Searcher.aggregate(searcher, "*", ["title"], aggs)
@spec count(t(), String.t(), [String.t()]) :: {:ok, non_neg_integer()} | {:error, String.t()}
Counts documents matching a query without retrieving them.
This is more efficient than a full search when you only need the count.
Parameters
searcher- The searcher to usequery_string- The query string with natural syntaxdefault_fields- List of field names to search when no field is specified
Examples
{:ok, count} = Muninn.Searcher.count(searcher, "elixir", ["title", "content"])
Creates a new Searcher from an IndexReader.
Parameters
reader- The IndexReader to create a searcher from
Returns
{:ok, searcher}- Successfully created searcher{:error, reason}- Failed to create searcher
Examples
{:ok, reader} = Muninn.IndexReader.new(index)
{:ok, searcher} = Muninn.Searcher.new(reader)
@spec search(t(), Muninn.Query.Term.t(), keyword()) :: {:ok, map()} | {:error, String.t()}
Executes a search query and returns results.
Parameters
searcher- The searcher to usequery- The query to execute (e.g., fromMuninn.Query.term/2)opts- Keyword list of options::limit- Maximum number of results to return (default: 10)
Returns
{:ok, results}- Search results as aMuninn.SearchResultstruct{:error, reason}- Search failed
Examples
query = Muninn.Query.term("title", "elixir")
{:ok, results} = Muninn.Searcher.search(searcher, query, limit: 20)
IO.puts("Found #{results.total_hits} matches")
for hit <- results.hits do
IO.puts("Score: #{hit.score}, Title: #{hit.doc["title"]}")
end
Performs fuzzy search for terms within a specified Levenshtein distance.
Fuzzy search is error-tolerant and matches documents containing similar terms, making it ideal for handling typos and spelling variations. For example, searching for "elixr" with distance=1 will match documents containing "elixir".
Parameters
searcher- The searcher resourcefield_name- Name of the text field to search interm- The search term (may contain typos)opts- Keyword list of options::distance- Maximum Levenshtein distance (0-2, default: 1)- 0 = exact match only
- 1 = one character difference (recommended for most use cases)
- 2 = two character differences (slower, use for suggestions)
:transposition- Count character swaps as single edit (default: true)- true = "elixer" → "elixir" counts as 1 edit
- false = "elixer" → "elixir" counts as 2 edits (delete + insert)
:limit- Maximum number of results (default: 10)
Returns
{:ok, results}- Map with "total_hits" and "hits" array{:error, reason}- Error string if search fails
Examples
# Basic fuzzy search (handles common typos)
{:ok, results} = Searcher.search_fuzzy(searcher, "title", "elixr", distance: 1)
# More tolerant search (allows 2 character differences)
{:ok, results} = Searcher.search_fuzzy(searcher, "content", "phoneix", distance: 2)
# Exact transposition handling
{:ok, results} = Searcher.search_fuzzy(
searcher,
"author",
"progarmming",
distance: 1,
transposition: true
)
# Higher result limit
{:ok, results} = Searcher.search_fuzzy(
searcher,
"title",
"elixr",
distance: 1,
limit: 50
)Performance Notes
- Distance=1: ~2-10x slower than exact search (recommended default)
- Distance=2: ~5-50x slower than exact search (use sparingly)
- Transposition=true is slightly faster than false
@spec search_fuzzy_prefix(t(), String.t(), String.t(), keyword()) :: {:ok, map()} | {:error, String.t()}
Performs fuzzy prefix search combining autocomplete with typo tolerance.
Similar to search_prefix/4 but allows for spelling errors in the prefix.
Useful for search-as-you-type with error tolerance.
Parameters
searcher- The searcher resourcefield_name- Name of the text field to search inprefix- The prefix to match (may contain typos)opts- Same assearch_fuzzy/4options
Examples
# Autocomplete with typo tolerance
{:ok, results} = Searcher.search_fuzzy_prefix(
searcher,
"title",
"pho", # User typing "phoenix" but made a typo
distance: 1,
limit: 10
)
@spec search_fuzzy_with_snippets(t(), String.t(), String.t(), [String.t()], keyword()) :: {:ok, map()} | {:error, String.t()}
Performs fuzzy search with highlighted snippets showing matched terms.
Combines search_fuzzy/4 with snippet generation for displaying context
around fuzzy matches.
Parameters
searcher- The searcher resourcefield_name- Name of the text field to search interm- The search term (may contain typos)snippet_fields- List of field names to generate snippets fromopts- Keyword list combining fuzzy and snippet options::distance- Maximum Levenshtein distance (0-2, default: 1):transposition- Count swaps as single edit (default: true):max_snippet_chars- Maximum snippet length (default: 150):limit- Maximum results (default: 10)
Examples
{:ok, results} = Searcher.search_fuzzy_with_snippets(
searcher,
"content",
"elixr",
["content"],
distance: 1,
max_snippet_chars: 200,
limit: 10
)
# Access snippets
for hit <- results["hits"] do
IO.puts(hit["snippets"]["content"]) # "Learn about <b>Elixir</b>..."
end
Finds documents similar to the provided document fields.
Uses Tantivy's MoreLikeThis query to find documents with similar term distributions.
Parameters
searcher- The searcher to usedocument_fields- A map of field name to text value representing the reference documentopts- Keyword list of options::min_doc_freq- Ignore terms appearing in fewer docs (default: 1):min_term_freq- Ignore terms less frequent than this (default: 1):max_doc_freq- Ignore terms in more docs than this (default::unlimited):min_word_length- Minimum word length (default: 0, no minimum):max_word_length- Maximum word length (default: 0, no maximum):max_query_terms- Maximum terms in the generated query (default: 25):boost_factor- Score boost factor (default: 1.0):limit- Maximum results (default: 10)
Examples
{:ok, results} = Muninn.Searcher.search_more_like_this(
searcher,
%{"title" => "Elixir programming", "content" => "Functional programming with Elixir"},
min_doc_freq: 1,
min_term_freq: 1,
limit: 5
)
Performs a prefix search for autocomplete/typeahead functionality.
Searches for terms in a specific field that start with the given prefix. This is useful for implementing autocomplete dropdowns and typeahead search.
Parameters
searcher- The searcher to usefield_name- The field name to search in (must be a text field)prefix- The prefix string to search foropts- Keyword list of options::limit- Maximum number of results to return (default: 10)
Returns
{:ok, results}- Search results with total_hits and hits{:error, reason}- Search failed
Examples
# Search for titles starting with "eli"
{:ok, results} = Muninn.Searcher.search_prefix(
searcher,
"title",
"eli",
limit: 5
)
# Get all authors starting with "al"
{:ok, results} = Muninn.Searcher.search_prefix(
searcher,
"author",
"al"
)
# Autocomplete as user types
user_input = "pho" # User typing "phoenix"
{:ok, suggestions} = Muninn.Searcher.search_prefix(
searcher,
"title",
user_input,
limit: 10
)
for hit <- suggestions["hits"] do
IO.puts(hit["doc"]["title"])
end
Executes a search using natural query syntax.
This function uses Tantivy's QueryParser to support advanced query syntax:
- Field-specific search:
title:elixirorauthor:alice - Boolean operators:
elixir AND phoenix,rust OR go - Phrase queries:
"exact phrase match" - Required terms:
+elixir phoenix(elixir is required) - Excluded terms:
elixir -draft(exclude draft) - Combining:
title:elixir AND (content:phoenix OR content:otp)
Parameters
searcher- The searcher to usequery_string- The query string with natural syntaxdefault_fields- List of field names to search when no field is specifiedopts- Keyword list of options::limit- Maximum number of results to return (default: 10)
Returns
{:ok, results}- Search results with total_hits and hits{:error, reason}- Search or parse failed
Examples
# Search for "elixir" in title and content fields
{:ok, results} = Muninn.Searcher.search_query(
searcher,
"elixir",
["title", "content"],
limit: 10
)
# Field-specific search
{:ok, results} = Muninn.Searcher.search_query(
searcher,
"title:phoenix",
["title", "content"]
)
# Boolean query
{:ok, results} = Muninn.Searcher.search_query(
searcher,
"elixir AND phoenix",
["title", "content"]
)
# Phrase query
{:ok, results} = Muninn.Searcher.search_query(
searcher,
~s("functional programming"),
["title", "content"]
)
# Complex query
{:ok, results} = Muninn.Searcher.search_query(
searcher,
"title:elixir AND (content:web OR content:concurrent) -draft",
["title", "content"]
)
@spec search_query_sorted(t(), String.t(), [String.t()], String.t(), keyword()) :: {:ok, map()} | {:error, String.t()}
Executes a search sorted by a fast field value instead of relevance score.
Requires the sort field to be a numeric type (u64, i64, f64) with fast: true
in the schema.
Parameters
searcher- The searcher to usequery_string- The query string with natural syntaxdefault_fields- List of field names to search when no field is specifiedsort_field- Name of the fast field to sort byopts- Keyword list of options::reverse- Sort descending whentrue(default:falsefor ascending):limit- Maximum number of results (default: 10)
Returns
Results include "sort_value" (the fast field value) instead of "score".
Examples
{:ok, results} = Muninn.Searcher.search_query_sorted(
searcher,
"*",
["title"],
"price",
reverse: true,
limit: 10
)
@spec search_range_f64(t(), String.t(), float(), float(), keyword()) :: {:ok, map()} | {:error, String.t()}
Performs a range query on an f64 field.
Searches for documents where the field value falls within the specified range.
Parameters
searcher- The searcher to usefield_name- The f64 field name to searchlower- Lower bound valueupper- Upper bound valueopts- Keyword list of options (seesearch_range_u64/5)
Examples
# Find products priced between $10.00 and $50.00
{:ok, results} = Searcher.search_range_f64(
searcher,
"price",
10.0,
50.0
)
# Find ratings 4.0 and above (excluding exactly 5.0)
{:ok, results} = Searcher.search_range_f64(
searcher,
"rating",
4.0,
5.0,
inclusive: :lower
)
@spec search_range_i64(t(), String.t(), integer(), integer(), keyword()) :: {:ok, map()} | {:error, String.t()}
Performs a range query on an i64 field.
Searches for documents where the field value falls within the specified range.
Parameters
searcher- The searcher to usefield_name- The i64 field name to searchlower- Lower bound valueupper- Upper bound valueopts- Keyword list of options (seesearch_range_u64/5)
Examples
# Find temperatures between -10 and 30 degrees
{:ok, results} = Searcher.search_range_i64(
searcher,
"temperature",
-10,
30
)
@spec search_range_u64( t(), String.t(), non_neg_integer(), non_neg_integer(), keyword() ) :: {:ok, map()} | {:error, String.t()}
Performs a range query on a u64 field.
Searches for documents where the field value falls within the specified range.
Parameters
searcher- The searcher to usefield_name- The u64 field name to searchlower- Lower bound valueupper- Upper bound valueopts- Keyword list of options::limit- Maximum number of results (default: 10):inclusive- Bound inclusivity (default: :both):both- Include both bounds [lower, upper]:lower- Include lower only [lower, upper):upper- Include upper only (lower, upper]:neither- Exclude both (lower, upper)
Returns
{:ok, results}- Search results{:error, reason}- Search failed
Examples
# Find products with 100-1000 views (inclusive)
{:ok, results} = Searcher.search_range_u64(
searcher,
"views",
100,
1000,
inclusive: :both
)
# Find items with price 10-99 (excluding 10 and 99)
{:ok, results} = Searcher.search_range_u64(
searcher,
"price",
10,
99,
inclusive: :neither
)
Performs a regex search on a specific field.
Uses Tantivy's regex engine (based on tantivy-fst). Note that regex patterns
match against indexed (lowercased, tokenized) terms.
The query parser also supports /regex/ syntax via search_query/4.
Parameters
searcher- The searcher to usefield_name- The text field to search inpattern- The regex patternopts- Keyword list of options::limit- Maximum number of results (default: 10)
Examples
{:ok, results} = Muninn.Searcher.search_regex(searcher, "title", "elix.*")
@spec search_with_snippets(t(), String.t(), [String.t()], [String.t()], keyword()) :: {:ok, map()} | {:error, String.t()}
Executes a search with highlighted snippets showing matching words in context.
This function performs the same search as search_query/4 but also generates
highlighted snippets for specified fields. Snippets show the matching words in
their original context with HTML <b> tags around matched terms.
Parameters
searcher- The searcher to usequery_string- The query string with natural syntaxdefault_fields- List of field names to search when no field is specifiedsnippet_fields- List of text fields to generate snippets foropts- Keyword list of options::limit- Maximum number of results to return (default: 10):max_snippet_chars- Maximum characters per snippet (default: 150)
Returns
{:ok, results}- Search results with snippets{:error, reason}- Search or parse failed
Result format includes an additional "snippets" map with HTML-highlighted snippets:
%{
"total_hits" => 5,
"hits" => [
%{
"score" => 3.14,
"doc" => %{"title" => "...", "content" => "..."},
"snippets" => %{
"content" => "Learn about <b>elixir</b> and <b>phoenix</b>...",
"title" => "<b>Elixir</b> Tutorial..."
}
}
]
}Examples
# Search with content snippets
{:ok, results} = Muninn.Searcher.search_with_snippets(
searcher,
"elixir phoenix",
["title", "content"],
["content"]
)
for hit <- results["hits"] do
IO.puts("Title: #{hit["doc"]["title"]}")
IO.puts("Snippet: #{hit["snippets"]["content"]}")
end
# Search with custom snippet length
{:ok, results} = Muninn.Searcher.search_with_snippets(
searcher,
"functional programming",
["content"],
["content"],
max_snippet_chars: 200
)
# Multiple snippet fields
{:ok, results} = Muninn.Searcher.search_with_snippets(
searcher,
"elixir web",
["title", "content"],
["title", "content"]
)