Skip to main content
Premium

Global Search

The cross-resource spotlight search ships in the paid habarico-enterprise gem — install it and require "habarico_enterprise", see Installation. Without it the global_fulltext_search_enabled option below does not exist, while per-resource search (search_fields) keeps working in the free gem.

Enable a resource to appear in the global fulltext search results:

global_fulltext_search_enabled true

When enabled, the SearchService runs Habarico's typed fulltext search (see search_fields below) against this resource and returns matching records.

The frontend calls GET {serviceUrl}/search/?search=…, optionally with &limit=…. Each result exposes id, title (sourced from record.representation when available) and highlights (see below). A blank search returns an empty array without touching the database.

caution

The query parameter is search, not query. Both backends read the same name, which is what lets one frontend build talk to either.

Result limit​

limit applies per resource: every resource with global_fulltext_search_enabled contributes up to limit results to the same response.

It must be an integer between 1 and the configured ceiling. Anything else — a non-numeric value, zero, a negative number, or a number above the ceiling — is refused with 422 rather than silently corrected, the same way an over-wide aggregation is refused. Omitting limit (or sending limit= with nothing after it) falls back to the default. Both ends are configurable:

# config/initializers/habarico.rb
HabaricoEnterprise::Search::Config.default_limit = 5
HabaricoEnterprise::Search::Config.max_limit = 100 # nil removes the ceiling entirely
caution

The ceiling sits far below the page size of a resource's own list endpoint on purpose: the spotlight materializes up to limit records of every enabled resource and runs the match highlighter over each one. To read many rows of a single resource, page its list endpoint instead.

The rule lives in HabaricoEnterprise::Search::Limit.resolve, which the controller applies before the search fans out. SearchService#get_search_result takes the resolved integer, so a host calling it directly is responsible for the value it passes — run it through Limit.resolve to get the same validation the endpoint performs.

search_fields​

Declare which fields the typed fulltext search matches against. Paths may traverse relations via __ (e.g. author__name):

search_fields %i[title description author__name status published]

search_fields is the single source of truth for typed matching and match highlighting. Each path is resolved against the model via ActiveRecord reflection (associations) — or Habarico references — with the leaf field classified into one of four search types:

TypeDetected fromMatching
:textstring / text columnscase-insensitive substring (lower(…) LIKE)
:choicesenum attributes (has_enum_attr?)against the human-readable choice labels
:booleanboolean columnsagainst the translated Yes / No labels
:numericinteger / float / decimal columnscast-to-text substring (e.g. 12 matches 121.00)

Recommended field types are text, choices, boolean and numeric — they produce useful matches and aligned highlights. Not recommended: date / datetime and uuid columns are classified as :text and technically match their ISO/raw form, but the FE usually renders them differently, so highlight offsets won't line up with what the user sees — use a range filter for dates instead.

Unresolvable paths (a missing field, or a non-relation intermediate hop) are skipped, so a stale entry never breaks the request. search_fields is optional, but without it the matcher never narrows anything: an enabled resource with no search_fields returns all records — up to the limit — for any search query (and the list endpoint returns all records unfiltered).

Habarico owns the whole search query (it does not delegate the search key to eluvia-base filter_2!), so search_fields is authoritative and supports relation traversal and the unaccent toggle. Both the service search endpoint and the resource list endpoint (?search=… or ?filter=search="…") apply the same matcher.

Diacritics-insensitive matching​

Enable accent-insensitive matching (and highlighting) globally:

# config/initializers/habarico.rb
Habarico.setup do |config|
config.search_unaccent_enabled = true
end

When on, text matching wraps columns in the PostgreSQL unaccent() function (the extension must be installed) and the highlighter strips accents when locating matches, mapping the offsets back onto the original, accented value. Off by default.

Highlight sidecar​

When a search is active, every matched record carries a highlights payload — on the service search endpoint as objects[i].highlights (trimmed to a snippet around the matches) and on the resource list endpoint as each row's meta.highlights (the full field value). Both share the same shape:

[
{
"name": "author__name",
"value": "<mark>Alpha</mark> article",
"label": "Author name",
"offsets": [[0, 5]]
}
]
  • name — the search_fields path that matched.
  • value — HTML-escaped field value with each matched substring wrapped in <mark>...</mark>. Choice fields and booleans use their human-readable display (the enum label, the translated Yes/No).
  • label — the field's human-readable label.
  • offsets — half-open [start, end] spans of each match in the original (raw, non-escaped) value, independent of any snippet trimming applied to value.