SIGNALS Documentation
API Reference

Global Search

The Cmd-K command palette, how in-app and API relevance differ, and the q[search_cont] filter for continuing a search on list and API indexes.

Overview

Global search is backed by a polymorphic search_index table maintained by searchable models (observer-driven create/update/delete sync). The in-app command palette (Cmd-K) and the REST search API share that index but use different matching and ranking rules. Laravel Scout is not used.

Command palette (Cmd-K)

Opening the palette

Trigger Behaviour
⌘K / Ctrl+K Toggle the command palette
/ Opens when focus is not in an input, textarea, or select
Header search icon Opens the palette
Esc Closes

Also listed on the Keyboard shortcuts page.

Modes

  • Search mode (default) — entity hits plus filtered navigation commands.
  • Command mode — start the query with > to list palette commands only (label / group / keywords).

Placeholder: “Search, > for commands, or type where to go…”. Entity search requires at least 2 characters (200 ms debounce) and calls GET /search?q=….

Palette commands load the same way: the first time the palette opens it calls GET /palette/commands, which returns {"commands": [...]} from NavigationService::paletteCommandsFor(), and the list is cached client-side for the lifetime of the page. Only the static Help entries (Keyboard shortcuts) ship inline in the page, so no permission-gated nav lookup or plugin palette provider runs on a page that never opens the palette. A short skeleton shows while that first fetch is in flight.

Result groups

Permission-gated groups (via NavigationService::canSearch) include Accounts, Fleet, Catalogue items, Assets, CatalogueItem Groups, Activities, Rentals, Invoices, Credit Notes, Payments, Purchase Orders, Virtual Stock, Repairs, Stock Checks, and Warehouse Transfers. Caps are typically 5–8 hits per group. Selecting a hit navigates to the record’s show page (product groups open the catalogue_items index filtered by group).

Accounts, Rentals, and Invoices can show a preview pane with facts and View/Edit actions. Empty query lists commands only; a single character filters commands client-side without fetching entities.

Keyboard navigation

Key Action
↑ / ↓ Move selection
Enter Open the selected result
Esc Close

Clicking an entity with a query of 2+ characters also POST /search/record (query, type, id) for analytics — the palette does not currently re-rank or resurface those recent searches in the UI.

Relevance behaviour

In the command palette

The palette uses a case-insensitive substring match on search_index title, content, and flattened meta text (SearchService::matchingIds). Candidates are then ordered by each bucket’s own model sort (e.g. name, id) — not PostgreSQL ts_rank. Embeddings are not blended on this path.

On the API

GET /api/v1/search?query=… (ability search:read) uses PostgreSQL full-text search (websearch_to_tsquery + ts_rank) with weighted fields (title strongest, then content, then meta values). Results are grouped and capped per type (per_type, default 5). When embeddings are configured and available, semantic neighbours can be blended in at a lower weight than typical FTS hits — see AI embeddings.

GET /api/v1/search/semantic?query=… is vector-only and returns empty results when embeddings are unavailable.

SQLite (common in local/test) falls back to LIKE matching without meaningful ranking; production ranking assumes PostgreSQL.

List / API continuation — q[search_cont]

What it does

Every searchable model’s index endpoint accepts the Ransack-style filter:

GET /api/v1/accounts?q[search_cont]=Marquee&q[is_active_true]=1

search_cont correlates to search_index for that model’s morph type and applies the same substring match the palette uses on title / content / meta text. It combines with other q[…] filters via AND. Blank values are ignored; % and _ are escaped.

Where it applies

  • API index routes that use the shared Ransack filter pipeline
  • Live filters that pass search_cont as an explicit filter

Only rows that have been indexed match. Addresses and conversations are not searchable entity types in the palette or this filter.

What it does not do

Cmd-K does not hand off into a Livewire list page with ?q[search_cont]=…. Choosing a palette hit opens the show page (or catalogue_items-by-group). List-page DataTable search boxes use their own column ilike search, separate from search_cont.

Reindexing

If results look stale after bulk changes, administrators can rebuild the index with the search:reindex Artisan command (and related backfill jobs). Normal create/update/delete paths keep the index in sync automatically.