Querying
Outline
- What you'll learn: How to load content and metadata using the core CMS APIs in CMS 12 (PaaS).
-
Core tools:
IContentLoaderfor reads,IContentRepositorymainly for writes (but it can read too). - Common scenarios: Single item, hierarchy (children/descendants), batch loading, basic property filtering, metadata extraction.
- Performance reminder: If your query starts looking like search + facets + ranking, that's Optimizely Find territory.
Introduction
Efficiently querying and retrieving content (and the metadata that comes with it) is core to almost every Optimizely CMS 12 build: dynamic navigation, integrations, reporting, page rendering, and "show me the latest X" components.
Optimizely stores content in a hierarchical tree. Editors browse and manage it in the UI; developers fetch and shape it programmatically. This page focuses on the core CMS APIs - primarily IContentLoader and IContentRepository. For advanced search-driven querying (full-text, faceting, relevance ranking), Optimizely Find is the dedicated solution.
Core interfaces for querying
The primary interfaces for programmatic content retrieval in Optimizely CMS 12 are:
-
IContentLoader: Read-only interface for retrieving content. It is optimised for reads and benefits from built-in caching. For any read operation, this should be your first choice. -
IContentRepository: Primarily used for modifications (create/update/delete), but it also exposesGetmethods. If you plan to load-and-modify in one flow it can be convenient, thoughIContentLoaderis still preferred for pure reads.
Both interfaces are typically obtained through dependency injection.
1. Loading a single content item
The most basic query is retrieving one content item when you know its unique identifier: a ContentReference.
Steps
Select a topic to expand and read the details.
Steps for loading a single item ▼
-
Identify the
ContentReference: e.g.,ContentReference.StartPage, or a reference stored in a property. -
Load as a specific type:
IContentLoader.Get<T>for type safety and easier property access. -
Load generically when needed:
IContentLoader.Get<IContent>if the runtime type is unknown.
Loading by a specific type gives you type-safe access, but can throw TypeMismatchException if the item exists but isn't assignable to that type. Loading as IContent avoids type mismatch and lets you inspect the runtime type.
2. Querying content by hierarchy
Optimizely's content tree enables hierarchical querying, which is essential for navigation, sitemaps, and "related content" listings.
Steps
Select a topic to expand and read the details.
Steps for hierarchical querying ▼
-
Identify the parent: the starting
ContentReference. -
Direct children: use
GetChildren<T>for one-level-down queries. -
All descendants: use
GetDescendants<T>to walk the entire subtree.
GetChildren<T> fits navigation menus. GetDescendants<T> is more sitemap-style, and can get expensive if the subtree is large - treat it as a power tool, not a toothbrush.
3. Loading multiple content items by references
If you already have a list of ContentReference values, use GetItems() to load them efficiently instead of calling Get<T> in a loop.
Steps
Select a topic to expand and read the details.
Steps for batch loading ▼
-
Collect references: assemble an
IEnumerable<ContentReference>. -
Use
GetItems(): optionally supplyLoaderOptionsfor language behaviour.
GetItems() helps you avoid N+1 loading patterns. The LoaderOptions configuration makes language behaviour explicit and helps you retrieve the "best available" variant for a given culture.
4. Basic filtering by type and properties
For basic filtering, you can load a set of content and use LINQ in memory. This is fine for small-to-medium collections. For large datasets, complex filtering, full-text search, and faceting, use Optimizely Find.
Steps
Select a topic to expand and read the details.
Steps for property-based filtering ▼
- Retrieve a collection: children or descendants.
-
Filter with LINQ:
Where,OrderBy,Select,OfType. - Read properties: standard or custom fields.
Note: This is a practical pattern for basic filtering, but it is still in-memory filtering after loading content. If the subtree is large, shift the problem to a search/index solution instead of brute-forcing it in application code.
5. Accessing content metadata
Every content item includes metadata that helps you understand lifecycle, relationships, scheduling, and version status. This is especially useful for reporting, audits, and operational tooling.
Common metadata fields
-
ContentLink: the unique identifier. -
Name: the display name (for pages, this typically maps toPageName). -
Created: when the content was created. -
Changed: when it was last modified. -
StartPublish: scheduled publish time (if set). -
StopPublish: scheduled unpublish time (if set). -
Status: fromIVersionable(e.g., Published, CheckedOut, AwaitingApproval). -
ParentLink: reference to the parent content item. -
ContentTypeID: content type definition ID.
Metadata is available off IContent. For version status, cast to IVersionable. This gives you a quick operational snapshot without needing to inspect the UI.
6. Best practices for content querying
-
Prefer
IContentLoaderfor reads: optimised, cached, and intent-revealing. -
Avoid N+1: use
GetItems()for batches instead of repeatedGet<T>calls. -
Use hierarchy APIs thoughtfully:
GetChildrenis safer;GetDescendantscan explode on large trees. -
Be explicit about language: use
CultureInfoandLoaderOptionsincluding fallback behaviour. -
Handle expected exceptions:
ContentNotFoundExceptionandTypeMismatchExceptionare normal in real systems. - Use Optimizely Find for advanced search: don't replicate search engines with LINQ over large collections.
- Prefer strong types where possible: typed loading simplifies property access and reduces runtime casting.
Conclusion
Programmatic content and metadata querying is a cornerstone of Optimizely CMS 12 development. When you master IContentLoader and hierarchical retrieval patterns, you can build efficient navigation, listings, integrations, and operational tooling.
The key is choosing the right approach for the job: core CMS APIs for direct access, Optimizely Find for search-grade querying, and explicit language handling so multilingual behaviour stays predictable.
