Search and discovery
A catalog is only useful if you can find things in it. Componecat indexes everything and gives you several ways in.
One page for browsing and searching
Section titled “One page for browsing and searching”Catalog is where you both explore and search. Open it with an empty search box and you get the whole catalog; type into that box and the same page narrows to what matches. There is no separate search screen to switch to, and no filter that works in one place but not the other.
Tree or list
Section titled “Tree or list”A toggle at the top right of the results picks how they are drawn:
- Tree follows the hierarchy — systems containing components containing sub-components — with expand and collapse for navigating deep structures.
- List ranks results by relevance and shows the matching text, including the documentation snippet that matched.
Browsing starts in the tree; typing a query moves you to the ranked list, because that is when relevance has something to say. Toggle whenever you like — the choice is remembered, and it travels in the link you share.
Narrowing the catalog does not flatten the tree. Matches stay where they belong in the hierarchy, and any parent that did not itself match is still drawn above them, dimmed, so you can see where a result lives. Those context rows are not counted in the result total, but they are still real entities you can open.
Full-text search
Section titled “Full-text search”Search runs across an entity’s display name, name, description, tags, and key field values — and across documentation sections, so a phrase buried in a runbook still surfaces the entity. Partial and case-insensitive matches are handled, so you don’t have to remember exact spelling.
Faceted filters
Section titled “Faceted filters”Narrow the catalog by kind, lifecycle, owner, namespace, source repository, tags, and any searchable field. Filters opens a dialog over the page; each chip carries the number of entities it would leave you with, and a running result count sits at the top so you can see the effect without closing it. Filters combine, so “production components owned by team-payments” is a couple of clicks. Filter state lives in the URL, so a filtered view is a shareable, bookmarkable link.
If your organization defines a lot of fields, the dialog does not draw a row of chips for every one. Pick the ones you want from Custom fields and their chips appear; a field already carrying a selection is shown from the start. Removing a field from that list clears whatever it had selected, so nothing keeps filtering out of sight.
The query language
Section titled “The query language”Facets answer “narrow this down”. When you need to ask a real question of the catalog — anything with an or, a not, or a comparison — type it into the same search box:
lifecycle:production AND (language:go OR language:rust) AND NOT tag:deprecatedA query is a mix of bare words, which search names, descriptions, tags, and documentation exactly as they always have, and field clauses, which filter on a specific field. Typing plain words still does plain search — the language only engages once you use a clause or an operator.
Fields
Section titled “Fields”Built-in fields: name, displayName, namespace, description, kind,
lifecycle, owner, ownerHasMembers, tag, language, repository, branch,
hasDescriptor, interface, interfaceVisibility, protocol, hasDocs, hasSpec,
scorecard, scorecardScore, scorecardLevel, createdAt, updatedAt,
lastSyncedAt, docsUpdatedAt. Several have shorter aliases — ns, desc, team,
lang, repo, updated, synced.
interface, interfaceVisibility, and protocol ask about the interfaces an
entity exposes, and match when any one of them fits: interface:mcp finds every
entity publishing an MCP server, interface:* every entity publishing anything at
all, and interface:null the ones publishing nothing. hasDocs:true finds entities
carrying documentation, whether a linked page or a section held in the catalog.
hasSpec:true finds entities with at least one API interface carrying a spec URL —
interface:!api OR hasSpec:true reads as “no API interface, or at least one of its
API interfaces is specced” (an any-of test, not a check that every API interface
individually carries a spec).
Every entity has an owning team — owner is required, so owner:* matches
everything and owner:null matches nothing. ownerHasMembers:true is the useful
presence test: it finds entities whose owning team actually has someone on its
roster, as opposed to one that’s just an empty placeholder.
lastSyncedAt is when an entity’s componecat.yaml descriptor last synced
successfully — an entity that has never synced matches neither side of a
comparison operator, only :null, the same way namespace:null matches an
absent field below. docsUpdatedAt is
when an entity’s documentation — a linked page or a section held in the catalog —
was last touched, matching when any one of them falls in the given window, the
same any-of convention interface uses.
scorecard:security finds every entity evaluated against the security
scorecard, by name or id. scorecardScore: and scorecardLevel: ask about that
evaluation’s weighted score (0-100) and attained ladder level, and always need a
scorecard: clause in the same AND to say which scorecard they mean — there’s
no composite score across scorecards to fall back to:
scorecard:security AND scorecardScore:>=80scorecard:security AND scorecardLevel:goldscore: and level: still work as shorter names for the same two fields — unless
your organization has its own field definition
keyed exactly score or level (a maturity score, a risk score, a support tier
level), in which case that field wins the name: score: and level: address your
field, reachable the same way any colliding field is (field.score/field.level
still works too), and scorecardScore:/scorecardLevel: remain the unambiguous
way to reach the scorecard evaluation fields.
If your organization has exactly one scorecard, scorecardScore:/scorecardLevel:
work on their own — there’s nothing to disambiguate. With more than one, leaving off
scorecard: is a query error rather than a guess, and names every scorecard it
could have meant so you can pick one.
Your organization’s field definitions are
addressable by their key: tier:1, costCenter:CC-100. If a field key happens to
match a built-in name, reach it as field.owner and the built-in as owner.
Start typing a field name in the search box and it will suggest the ones that exist, including your own.
Operators
Section titled “Operators”| Expression | Matches |
|---|---|
lifecycle:production |
Field equals the value |
name:~gateway |
Field contains the value |
name:*gateway* |
Wildcards — * for any run of characters, ? for one |
replicas:>=3 |
Compare — >, >=, <, <= on number and date fields |
updatedAt:[2026-01-01 TO 2026-03-01] |
Inclusive range; * for an open end |
updatedAt:<now-90d |
Relative date — see below |
namespace:null |
Field is absent |
repository:* |
Field is present |
owner:!platform-team |
Field does not equal the value |
description:!"" |
Field is not the empty string — on an always-present field like description, this is how you exclude blanks, since :* only tests presence; on a nullable field (like namespace) it also matches rows where the field is absent, not just non-empty ones |
Combine clauses with AND, OR, NOT, and parentheses. A space between clauses
means AND, so kind:service lifecycle:production needs no operator. Negation
also has the short forms -tag:deprecated and !tag:deprecated. Quote anything
containing a space: owner:"Platform Team".
owner: takes a team name or a team id, and kind: takes a kind key.
Relative dates
Section titled “Relative dates”Date fields — createdAt, updatedAt, and any date field of your own — also accept
tokens relative to the moment the query runs, so “not touched in 90 days” can be
written once and mean the same thing every day it’s asked:
updatedAt:<now-90dupdatedAt:[now-30d TO now]field.reviewedAt:<now-180dnow is the moment the search runs. Follow it with a sign and a number and a unit
to offset it — now-90d (90 days ago), now+30d (30 days from now), now-24h
(24 hours ago), now-6mo (6 months ago), now-1y (1 year ago). Supported units are
h (hours), d (days), mo (months), and y (years) — m is rejected, since it
could mean either minutes or months.
A relative token resolves to the exact instant it names, not the start or end of a
calendar day the way updatedAt:2026-01-01 does — now-90d is 90×24 hours before
the moment the query ran, down to the microsecond. Everything in a single query
resolves against the same moment, so both ends of updatedAt:[now-30d TO now]
line up.
This matters most for anything you save: a saved search or a dynamic collection
written with updatedAt:<now-90d keeps meaning “not updated in the last 90 days”
every time it’s evaluated, instead of silently rotting into a fixed calendar date
the way a literal date would.
Text matching ignores case everywhere, on your organization’s fields as much as on
the built-in ones, so tier:GOLD and tier:gold find the same entities. Number,
boolean, and date fields compare by value rather than by text, so replicas:>=9
matches 10 and a wildcard in one of them is an error rather than a literal
character to look for.
If a query doesn’t parse, the search box says why and points at the character that
confused it — nothing is silently ignored. Plain words are never rejected, though:
text that happens to contain a colon — auth: token, a pasted link — is searched
for as written rather than treated as a broken expression.
Filters and queries together
Section titled “Filters and queries together”The chips write themselves into the search box. Select a kind and kind:service
appears there; take the chip off and the clause goes with it. What the box shows is
the whole search that ran, so a click-built filter is also a worked example of the
language — and when you need an or the chips can’t express, the expression is
already there to edit.
It reads the other way too. Type kind:service into the box and Service ticks in
the kind picker, because a clause you wrote by hand belongs on the control that owns
it as much as one you clicked.
That only happens when what you typed is exactly what the chips would have written. The page redraws the box from the chips, so anything it rewrote — a different clause order, quotes it didn’t need — would move the text and your cursor with it while you were still typing. So recognition is deliberately narrow, and everything else simply stays as text and still runs: an or, a not, a comparison, a wildcard, and a value your organization doesn’t have, which is far more likely a typo than a filter. When the box holds something it can’t put on a chip, the chips hand over and the query does the work. The source repository is the one facet with no clause of its own, so it stays a chip either way.
The same language everywhere
Section titled “The same language everywhere”The expression you type in the UI is the same one the API takes. Pass it as q to
GET /api/rest/v1/catalog/search, as query to the GraphQL searchCatalog field, or as
query to the MCP search_catalog tool — see the
API reference.
Saved searches
Section titled “Saved searches”A search you run often doesn’t have to be rebuilt every time. Saved — beside the Filters button — keeps the current search under a name and puts it one click away.
What it stores is the whole search, not just the text: the query, the facet selections, whether you were in the tree or the list, and the sort order. Opening one puts the page back exactly as you left it, chips and all.
Saved searches are private. Yours are visible only to you, and two people can each keep a “My services” without colliding. Names have to be unique within your own list, so there is never a pair you can’t tell apart.
From the menu you can also rename a saved search, delete it, or — once you’ve changed the search it opened — update it to match what’s on screen. The update offer only appears when the page has actually drifted from what was saved. Deleting one removes the shortcut and nothing else; the entities it matched are untouched.
A query that doesn’t parse is refused at save time, for the same reason a collection’s is: a shortcut that quietly matched nothing would be worse than one that won’t save.
Keeping a query: dynamic collections
Section titled “Keeping a query: dynamic collections”Where a saved search is a personal shortcut, a collection is a named, shareable set of catalog entities that everyone in the organization can see. A static one holds the entities you picked by hand, in the order you put them in. A dynamic one holds a query instead, and its members are whatever that query matches right now — an entity that starts matching joins on its own, and one that stops matching leaves.
Both are built with the search you already know. A static collection’s Add items box takes the same query language, the same Filters dialog, and the same Saved searches as the catalog — so you can narrow to the twelve services you want and add them one by one, instead of remembering each name.
The query is written in the language above, in a box with the same field suggestions and the same syntax help, alongside a live preview of what the collection would contain. You do not have to write it from memory: Filters opens the same dialog the catalog uses, and every chip you pick writes itself into the query, exactly as it does on the catalog page. Saved starts you from one of your saved searches, so a view you already worked out can become a collection everyone can see.
The one filter missing here is the source repository. A collection stores a query and nothing else, and that filter has no clause in the language, so it is left out rather than offered and then quietly dropped.
Anything you can search for, you can keep:
kind:service AND lifecycle:production AND NOT tag:deprecatedinterface:mcp AND hasDocs:trueowner:"Platform Team" AND updatedAt:>2026-01-01A collection’s page shows its query, and Open in catalog hands the query back to the catalog search box so you can widen it, narrow it, and save the result as another collection.
Where the search box treats a query it can’t make sense of as ordinary words to search for, a saved query is refused until it parses — a collection that quietly matched nothing would be worse than one that won’t save.
The command palette
Section titled “The command palette”Press Cmd/Ctrl + K anywhere for a global command palette: type to jump straight to any entity. Your recently viewed entities are one keystroke away, so hopping between the things you’re working on is fast.
Results are grouped by what they are. Alongside Catalog entries and app pages, a Documentation group matches this documentation itself, so a question about how Componecat works — “descriptor”, “team sync”, “impact analysis” — finds the page that answers it without leaving what you were doing. Catalog matches always rank first, so searching for a term that names one of your entities still puts the entity at the top. Selecting a documentation result opens the page here on the documentation site in a new tab, at the exact section that matched.
The palette shows the best few matches, not all of them. See all results at the bottom of the Catalog group opens the catalog page with the same text, where the filters and the query language are waiting.
Related
Section titled “Related”- Entities and kinds
- Documentation and activity — docs participate in search.