Search console
Type a query, and Holmes runs it through Craft’s own search — then shows the working. The answer to “why doesn’t this entry come up?” is usually right there.
The syntax is Craft’s
Because it is Craft doing the searching, Craft’s search syntax works as-is:
baker street both words
"baker street" the phrase
-moriarty excluding this
baker* starting with
title:baker only in the title
title::baker the title is exactly this
You can also narrow a query to one site and one element type, which is worth doing on a multi-site install — keywords are normalized per site, so the same query can legitimately behave differently in each.
What you get back
The parsed terms
Every term, with the strategy Craft will use for it and the reason. A term goes through the full-text index unless something stops it, and Holmes names what stopped it:
| Falls back to LIKE when | Because |
|---|---|
| The term is shorter than the minimum word length | The full-text index never contained it |
| It is a stop word | The database excludes it from the index |
| It is a phrase | Full-text matching cannot hold word order |
| It has a leading wildcard | Nothing to anchor an index lookup to |
| It is an exclusion | Expressed as NOT LIKE |
| It is an exact match | Compares the whole stored value |
This is the part that most often explains a surprise. A three-letter product code, or a word your database happens to treat as a stop word, behaves quite differently from everything around it.
Holmes works the strategy out by asking Craft to build the clause and reading what came back, rather than keeping its own copy of the rules. Craft’s stop-word list is private and could change; this cannot drift.
The SQL
The query Craft actually ran, as it ran it. Useful for taking to a DBA, and for confirming that a scope you thought you applied really was applied.
The rows that matched
Every index row that matched, not just every element — so you can see which field caused the match. An entry matching on a field you did not expect is how you find content that should not have been searchable.
The scores
The relevance score Craft gave each element, so you can see why the ordering is what it is. Craft scores per row and takes the best per element; an element matching on its title generally outranks one matching in the middle of a body field.
From a result to the element
Any element in the results opens its detail view: every index row it has, with a word-by-word diff against what Craft would write for it now. If a search is missing an entry you expect, that screen tells you whether the row is absent, empty, stale or simply spelled differently after normalization.
When nothing matches
An empty result is still informative. Work down this list:
- Is the element indexed at all? Check its detail view, or run the unindexed elements check.
- Is there a row for the field you expect to match, or only for the title? That is partly indexed.
- Does the row exist but hold different words than you think? That is drift, and the diff will show it.
- Did every term fall back to LIKE, on a large index? Then the query is slow rather than wrong, and the terms table says why.
From the console
php craft holmes/index/search "baker street" --site=default
php craft holmes/index/element 1234
The console output carries the same parsed terms, strategies, SQL and scores as the control panel.
Logging what people search for
Every console query raises Probe::EVENT_AFTER_PROBE, carrying the parsed terms, the rows and the scores — the seam for recording misses. See Extending.