Skip to main content

Global Search

Overview​

Global Search provides a unified search experience across multiple models in your CmAdmin panel. Users can press ⌘ + K (Mac) or Ctrl + K (Windows) to open a search modal from anywhere on the platform. It searches records using the existing :search filters on each model's index action, respects Pundit policies and scopes, and returns up to 5 matching records per model.

In addition to record results, the search also suggests matching model pages (e.g., typing "user" suggests navigating to the Users index page).

Features​

  • Keyboard shortcut — Open search with ⌘ + K / Ctrl + K from any page.
  • Cross-model search — Search across all configured models in a single query.
  • Policy-aware — Only shows results the current user is authorized to see.
  • Concurrent execution — Searches models in parallel using a configurable thread pool.
  • Keyboard navigation — Navigate results with Arrow keys and press Enter to visit.
  • Model suggestions — Suggests matching model pages alongside record results.
  • Customizable subtitle fields — Control which fields appear below each search result.
  • Custom display name — Define cm_display_name on your model to control the result title.

Setup​

In your CmAdmin initializer (e.g., config/initializers/zcm_admin.rb), add the models you want to be searchable:

CmAdmin.configure do |config|
config.global_search_models = [User, Post, Order]
end

Note: The order of models in this array determines the display order of results.

Step 2 — Ensure a search filter exists​

Each model must have a :search filter defined in its cm_index block. Global search uses this filter to query records.

cm_admin do
cm_index do
filter %i[email first_name last_name], :search, placeholder: 'Search by name or email'
column :first_name
column :last_name
column :email
end
end

Important: If a model does not have a :search filter on its index action, it will be silently skipped during global search.

Step 3 — Define cm_display_name​

By default, search results display as "ModelName #id" (e.g., "User #42"). To show a more meaningful title, define cm_display_name on your model:

class User < ApplicationRecord
def cm_display_name
"#{first_name} #{last_name}"
end
end
class Post < ApplicationRecord
def cm_display_name
title
end
end

Step 4 — Customize subtitle fields​

Each search result can display additional metadata below the title. By default, global_search_fields is set to [:id, :created_at, :updated_at]. You can override this per model by setting the global_search_fields attribute:

cm_admin do
self.global_search_fields = %i[email status created_at]

cm_index do
filter %i[email first_name], :search
column :email
column :status, field_type: :tag
column :created_at, field_type: :date
end
end

Note: The fields listed in global_search_fields must also be defined as columns in cm_index. Only columns that exist in the index view and pass their display_if condition will be shown.

Configuration Reference​

CmAdmin.config.global_search_models​

PropertyTypeDefaultDescription
global_search_modelsArray[]Array of ActiveRecord model classes to include in global search results.

global_search_fields (per model)​

PropertyTypeDefaultDescription
global_search_fieldsArray[:id, :created_at, :updated_at]Array of field identifiers to display as subtitle in search results.

Environment Variables​

VariableDefaultDescription
GLOBAL_SEARCH_THREAD_POOL_SIZE5Number of threads used for concurrent model searches.

How It Works​

  1. User opens the search modal via ⌘ + K / Ctrl + K or by clicking the search icon.
  2. After typing at least 2 characters, a debounced request (300ms) is sent to the server.
  3. The server iterates over global_search_models in parallel threads.
  4. For each model:
    • Checks if the user has index policy access.
    • Checks if a :search filter is defined on the index action.
    • Applies policy scopes and default scopes.
    • Runs the search filter and limits results to 5 records.
  5. Results are sorted by the original model order and rendered via Turbo Stream.
  6. Additionally, model pages matching the query string are suggested at the bottom.

Full Example​

# config/initializers/zcm_admin.rb
CmAdmin.configure do |config|
config.global_search_models = [User, Post, Comment]
end
# app/models/user.rb
class User < ApplicationRecord
def cm_display_name
"#{first_name} #{last_name}"
end

cm_admin do
set_icon 'fa fa-user'
self.global_search_fields = %i[email created_at]

cm_index do
filter %i[email first_name last_name], :search, placeholder: 'Search users'
column :first_name
column :last_name
column :email
column :created_at, field_type: :date
end
end
end
# app/models/post.rb
class Post < ApplicationRecord
def cm_display_name
title
end

cm_admin do
set_icon 'fa fa-file-text'
self.global_search_fields = %i[status created_at]

cm_index do
filter %i[title], :search, placeholder: 'Search posts'
column :title
column :status, field_type: :tag
column :created_at, field_type: :date
end
end
end

Notes​

  • Models without a :search filter or without index policy access are automatically excluded.
  • The search respects all default scopes and action-level scopes configured on the model.
  • The search modal will not open if another modal is already visible on the page.
  • The placeholder in the search input automatically shows the first 3 configured model names.