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 whenBecause
The term is shorter than the minimum word lengthThe full-text index never contained it
It is a stop wordThe database excludes it from the index
It is a phraseFull-text matching cannot hold word order
It has a leading wildcardNothing to anchor an index lookup to
It is an exclusionExpressed as NOT LIKE
It is an exact matchCompares 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:

  1. Is the element indexed at all? Check its detail view, or run the unindexed elements check.
  2. Is there a row for the field you expect to match, or only for the title? That is partly indexed.
  3. Does the row exist but hold different words than you think? That is drift, and the diff will show it.
  4. 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.