SearchCore.Highlight (search_core v0.4.0)

Copy Markdown View Source

Mark the words of a raw text that match a search query.

Matching goes through the same SearchCore.Pipeline as indexing and querying, so a word is highlighted exactly when it would have matched in Postgres: both sides are stemmed and accent-folded before comparing. "tomate" highlights "tomates", "idee" highlights "idées".

The result is a list of {:text, part} / {:match, part} segments in document order — renderer-agnostic, so a LiveView can map :match to <mark>, a terminal to ANSI, etc. Joining every part reproduces the input text byte for byte. The fragments carved out of the text — the matched words and the spans around them — are each copied into a standalone binary rather than left as sub-binaries, so stashing the segments (or just the :match words) in long-lived state never pins the whole source in memory through a word-sized fragment. (A query that matches nothing returns your text itself as the one segment, unchanged.)

iex> SearchCore.Highlight.highlight("Livraison de tomates anciennes", "tomate", :fr)
[{:text, "Livraison de "}, {:match, "tomates"}, {:text, " anciennes"}]

Summary

Functions

Split text into {:text, _} / {:match, _} segments for query in lang.

Types

segment()

@type segment() :: {:text, String.t()} | {:match, String.t()}

Functions

highlight(text, query, lang, opts \\ [])

@spec highlight(String.t(), String.t(), SearchCore.Language.t(), keyword()) :: [
  segment()
]

Split text into {:text, _} / {:match, _} segments for query in lang.

Options (all forwarded to SearchCore.Pipeline):

  • :prefix — when true, a word matches if its stem starts with a query stem, mirroring a tsquery built with prefix: true (default false, mirroring SearchCore.tsquery/3). Highlighting should use the same :prefix as the query that ran.
  • :min_length, :stopwords, :fold_accents — as in SearchCore.Pipeline.process/3.

A query with no usable tokens (blank, all stopwords) highlights nothing:

iex> SearchCore.Highlight.highlight("Un texte", "de", :fr)
[{:text, "Un texte"}]

iex> SearchCore.Highlight.highlight("Les idées vertes", "idee", :fr)
[{:text, "Les "}, {:match, "idées"}, {:text, " vertes"}]

iex> SearchCore.Highlight.highlight("BL-2024-0012", "bl", :fr, prefix: true)
[{:match, "BL"}, {:text, "-2024-0012"}]