Filters and Facets
Facets are the structured-data sidekicks of free-text search — checkboxes, sliders, and date ranges that let the user narrow results without re-typing a query. Curiosity supports three facet shapes, each with a different cost/value trade-off.
Property facets
Driven by a property directly on the node, and configured under Manage → Search → Facets (#/manage/search/facets) → Property Facets (or as the Facets tab of the Indexes settings page at #/manage/data/indexes — the same configuration, reached from wherever you happen to be).
The page is segmented by the field's data type, because the type decides what kind of facet the field produces. Each segment lists the schema fields of that type together with the indexes already configured for them:
| Segment | Field types | What the facet looks like |
|---|---|---|
| Text | string, Language |
One facet value per distinct text value. Best for a small set of repeated values (a status, a category, a country) — a field with thousands of distinct values produces a facet nobody can use. |
| Yes / No | bool |
A two-value facet, so users can narrow to the nodes where the flag is set or not set. |
| Time | date and time fields | A range facet, with values grouped into buckets. The bucket size (day, month, year, …) is the index's Time Quantization Strategy — open the advanced settings of a configured field to change it. |
| Numeric | numeric fields | A range facet, bucketed. The bucket size is the index's Quantization Strategy; a numeric facet can also bucket by a custom expression instead of by a fixed order of magnitude. |
A facet on a field does not make its text content searchable — it only filters by it. It can also speed up a query that would otherwise filter with a .Where("FieldName", f => f.StartsWith("Value")) clause. To index a field without exposing it as a facet, set the field's index to No Facet.
The field selector in the index editor only offers the field types the chosen index accepts, so a field cannot be configured with an index that cannot serve it.
Choosing a good property to facet on:
| Property example | Good facet? | Why |
|---|---|---|
SupportCase.Status |
Yes | Small set ("Open", "Pending", "Resolved"). |
Article.Category |
Yes | Curated, small set. |
SupportCase.IsEscalated |
Yes | A bool — the Yes / No segment. |
Article.Updated |
Yes | A date field, bucketed by the index's time quantization. Prefer the global time filter when you only need "recent". |
SupportCase.Summary |
No | Unique per row — facet would explode. |
Related (graph) facets
The differentiator versus most search engines. A related facet filters by an edge to a normalized entity node — e.g. "cases for devices made by Apple".
Two reasons to use related facets instead of duplicating the field on the node:
- No denormalization. If a device changes manufacturer, all cases follow automatically.
- Multi-hop. Filter on something two or three edges away (manufacturer of the device of the case).
Configure under Manage → Search → Facets → Related Facets.
Empty facets are hidden
A related facet is only rendered when at least one node in the current result set actually has a matching edge. If you've just configured a facet and it doesn't appear, the most common cause is that none of the visible results connect to a node of the target type. When testing a new related facet, ingest (or link) a handful of nodes on both sides of the edge before expecting the facet to show up.
From code:
var req = SearchRequest.For("screen flicker");
req.BeforeTypesFacet = new HashSet<string> { N.SupportCase.Type };
req.RelatedFacets = new Dictionary<string, List<RelatedFacet>>
{
["Manufacturer"] = new()
{
new RelatedFacet { TargetType = "Manufacturer", TargetKey = "Apple" }
}
};
var query = await Graph.CreateSearchAsUserAsync(req, CurrentUser, CancellationToken);
return query.Emit();
Time filter
Every node carries a Timestamp (or Time) field. The workspace exposes a global time filter (week, month, custom range) that applies to any indexed node type.
Exclude reference types from the time filter — Manufacturer, Status, Category rarely change and would show up in every time window:
- Manage → Search → Search (
#/manage/search/settings) → Advanced Settings → Excluded from Time
Value facets
Driven by a numeric range — score buckets, age ranges, prices. Configured the same way as property facets but rendered as a range slider.
Performance tips
- Cardinality matters. Facets over millions of distinct values become slow to count. Pre-aggregate by binning if necessary.
- Pre-compute multi-hop facets. If a related facet walks three hops, consider materializing the relationship onto the source node during ingestion so the facet is one hop.
- Lazy load facet values. The UI defaults to top-N values per facet. Don't expose
Show allfor high-cardinality facets in production.
Cross-links
- Full text search
- Ranking and boosting
- Search execution scopes — for code-driven custom facets.
- File metadata as related facets — worked example: promoting extracted file metadata to nodes so it can drive a related facet.
- Schema design — modeling for facet-friendliness.