Aggregate Optimizely Graph results with the C# SDK
Use .Facet() to retrieve value counts for one or more fields alongside query results. Facets power category navigation, filter-by-author panels, and other UI patterns that require aggregated counts.
Retrieve facet counts
Request value counts for one or more fields to display in a filter panel or navigation sidebar.
var result = await client
.QueryContent<BlogPostPage>()
.Facet(x => x.Author!)
.Facet(x => x.ViewCount!)
.GetAsContentAsync();Read the counts back from the result:
var authorFacet = result.Facets?.GetFacet(x => x.Author);
var viewCountFacet = result.Facets?.GetFacet(x => x.ViewCount);Each facet entry contains a value and its count.
Use facets with full-text search
Facet counts reflect the current search results, not the full content set:
var result = await client
.QueryContent<BlogPostPage>()
.SearchFor("api")
.UsingFullText()
.Facet(x => x.ViewCount)
.GetAsContentAsync();Filter within facets
Pass filters to apply a value filter while keeping the full facet list. This pattern is essential for faceted navigation. Users refine or change their selection without losing the other options.
var result = await client
.QueryContent<BlogPostPage>()
.Facet(x => x.Author, filters: ["John Developer"])
.GetAsContentAsync();The result set is filtered to posts by John Developer, but the facet still shows counts for all authors so the user can switch.
Multi-select facets
To let users select multiple values within a single facet, use OR logic. Selections across multiple facets combine using AND logic. Pass an empty string ("") as a placeholder for facets with no active selection:
var result = await client
.QueryContent<BlogPostPage>()
.Facet(x => x.BlogCategory!, filters: [""])
.Facet(x => x.Author!, filters: ["John Developer"])
.GetAsContentAsync();
var categoryFacet = result.Facets?.GetFacet(x => x.BlogCategory);
var authorFacet = result.Facets?.GetFacet(x => x.Author);Filter boolean facets
Use FacetSelected to add a facet on a bool property and optionally filter the results to one value. Pass true or false to filter the results to that value, or null to add the facet without a filter. The facet still reports counts for both values, so the unselected value stays available to users.
var result = await client
.QueryContent<ProductPage>()
.FacetSelected(x => x.InStock, selected: true)
.GetAsContentAsync();For an untyped query, pass the field name as a string, such as client.Query("ProductPage").FacetSelected("InStock", selected: false).
Note In CMS 13.1.3 and later, the Facet overloads that take a non-nullable bool selected parameter are obsolete. These overloads cannot filter results to false, because they treat selected: false the same as no filter. The obsolete overloads still work: selected: true filters the results to true, and selected: false applies no filter. Before 13.1.3, selected: true made Optimizely Graph reject the query. Replace these calls with FacetSelected, and pass null where you do not want a filter.
Read facet configuration from results
In CMS 13.1.3 and later, Facets.Items returns a FacetFieldResult for each faceted field in the response. Each result holds the facet values and the facet configuration that the query requested. Use this configuration to tell filtered facets from unfiltered facets without hard-coding field names.
var result = await client
.QueryContent<BlogPostPage>()
.Facet(x => x.Author, filters: ["John Developer"])
.Facet(x => x.BlogCategory)
.GetAsContentAsync();
foreach (var facet in result.Facets?.Items ?? [])
{
foreach (var value in facet.Values)
{
var isSelected = facet.Filters.Contains(value.Name);
// Render value.Name, value.Count, and the selected state.
}
}FacetFieldResult has the following properties:
- FieldName – The name of the faceted field. Nested paths use dot notation, such as Composition.Type.
- Values – The facet values and their counts.
- HasFilter – true when the query requested the facet with one or more filter values, including a boolean facet filtered to either value. false for plain terms facets, date and time facets, numeric range facets, and fields that the query did not request. A placeholder filter, such as filters: [""] in the multi-select pattern, also sets HasFilter to true.
- Filters – The filter values the query requested, in the same format as FacetValue.Name, so you can compare them directly. Empty when the query requested no filters.
- Selected – true when the query filters a boolean facet to true. To tell a boolean facet filtered to false from an unfiltered facet, check Filters.
- Limit – The limit the query requested for the facet, or null.
Filters narrow the returned items, not the facet values, so use Filters to mark which values the user selected. Facets.Items does not replace GetFacet, HasFacet, or FieldNames.