Global search (PRO)
One search box over everything the constructor holds: blocks on pages and table rows, at once. It opens from the PageBlocks Search icon in the manager's top bar, next to the regular MODX items.
What can be a source
| Source | When it shows up in the list |
|---|---|
| Blocks | Always |
| A table | The table has the Search box checked, has its own model, and is published and not deleted |
The table's "Search" box does two jobs
In the table constructor it is labelled simply "Search", and the visible half of it is the search box above the row list. The same flag decides whether the table becomes a source of the global search. Uncheck it to remove search from the grid and the table disappears from the global search too, searchable fields and all.
A table without its own model — one whose rows live in the shared pb_table_data — is never a source: such tables are filtered out explicitly, before any search happens.
Which fields are searched
A field joins the search when its Searchable flag is on. There are two ways to set it, and they write the same value (pb_fields.searchable):
- on the field itself — the Searchable flag;
- on the Search tab of the table constructor — as a list, without opening each field.
Grid columns have nothing to do with it: a field can be searchable without appearing in the list, and the other way round.
A column or a key in data — both are searched
A searchable field can be a real column of the table, or it can live as a key inside the JSON data column — see Models. The search works out which is which and builds the matching condition for each:
LOWER(`field_name`) LIKE '%query%' -- column
LOWER(JSON_UNQUOTE(JSON_EXTRACT(`data`, '$."field_name"'))) LIKE ? -- key in dataSame rule as the table's own grid search: if there is a column, it goes through the column; if there isn't, it looks for a key of that name in data.
Such a field used to switch off search for the whole table
The search only knew columns before. A single data field made the query fail on an unknown column, the error was swallowed — and the table silently returned an empty result, with nothing said in the interface. From the outside that read as "search finds nothing", not as "a field is flagged wrongly". If you unchecked Searchable on such fields to work around it, check them back.
A field backed by neither a column nor a data column on the table is simply skipped — the remaining fields are searched as usual.
A numeric query also matches the id
A query made entirely of digits is additionally compared against the row's id. 1234 finds both the row with that number and a row where 1234 appears in a searchable field.
A side effect: a table with no searchable fields at all still answers a numeric query — by row number.
Searching blocks
Blocks are searched across three pb_block_data columns at once: the block name, the chunk and the whole of data. Deleted blocks are skipped, newer ones come first.
Searching JSON finds more than you expect
data is matched as plain text, key names included. A query for title finds a block by the field's name, not only by its value.
Two characters minimum
A query shorter than two characters returns an empty result without touching the database.
This is not a validation message
It answers success with no rows rather than an error, because the search box fires as you type, 350 ms after the last keystroke. A single letter across every block on a large site is a table scan that helps nobody.
How much comes back
Each source contributes 50 rows. There is no pagination on purpose: you narrow a search by choosing a source, not by paging through results.
Several sources can be selected — they are scanned one after another, and the status line names the one being scanned. With nothing selected the search covers everything: blocks plus every table, so up to 50 + 50 × number of tables rows.
What a result looks like
| Block | Table row | |
|---|---|---|
| Title | Block name | "Table name #id" |
| Subtitle | Chunk | — |
| Description | 140 characters of text around the match | Searchable field values joined with · |
Clicking opens the object in place: a block in the block editing window, a row in the table row window. Rows of own-model tables also carry their site address when they have one.
It is LIKE, not a full-text index
The component neither creates nor uses FULLTEXT indexes. LIKE '%…%' cannot use an ordinary index even where one exists, and searching data is a full scan of pb_block_data.
On a small site this goes unnoticed. On a site with hundreds of thousands of blocks or rows it does not — and the cure is picking a source: one table instead of everywhere.
Unpublished is found, deleted is not
This is the editor's search: it finds content in order to edit it, and so it does not filter results by publishing. An unpublished row or block is found like any other — otherwise you could not search your way back to a draft.
Deleted content is not found: rows and blocks sitting in the recycle bin are filtered out by deleted_at. A table without such a column has nothing to filter — the check accounts for that instead of breaking the query.
Site search is not this
Both endpoints require a manager session and the paid edition, so the front end cannot call them. Search for visitors is a separate thing: your own controller, your own query, your own template. The two have different requirements, and sharing them would mean either exposing unpublished and deleted rows to visitors or hiding them from editors.