Dev guideRecipesAPI ReferenceChangelog
Dev guideRecipesUser GuidesNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

What content Optimizely Graph indexes

Understand which content items Optimizely Graph discovers during a sync, and how to index content that lives outside the default content tree

Optimizely Graph does not scan your database for content to index. It starts from a set of registered indexing roots, which are content items that mark where indexing begins. Graph then walks down the content tree from each root. Content outside every registered root is never discovered, so it never reaches your Graph schema or query results.

Prerequisite

To follow the steps in this article, confirm you have the following:

  • A CMS 12, CMS 13, or CMS (SaaS) installation with the Optimizely Graph integration configured.
  • Access to deploy code, for the indexing root registration steps.
  • CMS administrator access, to run the synchronization job.

How Graph discovers content

Graph indexes IContent instances, which covers pages, blocks, and media. During a full sync, the job loops through every registered indexing root and enumerates the descendants of each. It then sends the resulting content items to Graph.

Graph registers one indexing root by default: the CMS content tree root. This is the Root node at the top of the content tree, not the start page of a site. Every site's start page sits below it, so the default root covers all site content in a standard CMS installation.

One root exists per installation, not per site. A multi-site installation gives each site a start page below the single Root, so Graph indexes every site from that one root.

CMS 12, CMS 13, and CMS (SaaS) all use this model. CMS (SaaS) differs only in configuration, such as the gateway address. No SaaS code path changes how Graph discovers content.

📘

Note

Because the default root is the top of the content tree, Graph discovers most content in a standard installation without configuration.

Content that Graph does not discover

Content is missing from Graph when it is not reachable by walking down from a registered root. In practice this means content served by a custom content provider whose entry point sits outside the content tree root.

A provider that mounts within the Root subtree is discovered automatically. The tree walk resolves providers as it descends, so it follows the mount point into the provider's own content. Only a provider mounted outside the Root hierarchy is missed.

Commerce Connect is the clearest example. Catalog content lives in its own provider, outside the CMS content tree. Commerce Connect therefore registers its own indexing root, and Graph indexes the catalog. Custom providers you write need the same treatment.

What Graph excludes within a root

Being a descendant of an indexing root is necessary but not sufficient. Graph enumerates everything below each root and then filters the results. Graph excludes content when any of the following applies:

  • System root nodes – Graph never indexes five nodes: Root, the Recycle Bin, the Global Assets root, the Content Assets root, and the Blueprints root. This applies to those nodes themselves, not to everything beneath them. Graph indexes media stored under Global Assets normally.
  • Deleted content – Graph skips content marked as deleted, which also covers items in the Recycle Bin.
  • Content types excluded by convention – Graph skips content types excluded through the conventions API, and every type derived from them.
  • Abandoned media assets – Graph skips media in a content assets folder when the owning content no longer exists. Graph logs each one as Skipped index content '{name}' with key '{key}' since it is in abandoned asset folder.
  • Unpublished content, when drafts are excluded – This exclusion depends on configuration. ContentVersionSynchronizationMode defaults to All, which syncs published, draft, and previously published versions. Graph therefore indexes unpublished content by default.
📘

Note

ContentVersionSynchronizationMode accepts PublishedOnly, DraftAndPublishedOnly, or All. If you expected drafts to stay out of Graph, set the mode to PublishedOnly explicitly rather than relying on the default.

Register an additional indexing root

Register an indexing root for each content tree you want Graph to index in addition to the default.

CMS 13

Implement IContentIndexingRoot from the Optimizely.Graph.Cms.Content namespace. The interface has two members: a Name used in indexing logs, and the ContentRoot to start from. Graph indexes every descendant of ContentRoot.

using EPiServer.Core;
using Optimizely.Graph.Cms.Content;

public class CatalogIndexingRoot : IContentIndexingRoot
{
    private readonly ICatalogSystem _catalogSystem;

    public CatalogIndexingRoot(ICatalogSystem catalogSystem)
    {
        _catalogSystem = catalogSystem;
    }

    public string Name => "catalog";

    public ContentReference ContentRoot => _catalogSystem.GetCatalogRoot();
}

Register the implementation during startup:

services.TryAddEnumerable(
    ServiceDescriptor.Singleton<IContentIndexingRoot, CatalogIndexingRoot>());

Use TryAddEnumerable rather than AddSingleton. Graph then registers your root alongside the default CMS root instead of replacing it.

CMS 12

CMS 12 uses the same pattern with a different interface. Implement IIndexTarget and expose the ContentRoot to start from:

using EPiServer.Core;
using Optimizely.ContentGraph.Cms.Services.Internal;

public class CatalogIndexTarget : IIndexTarget
{
    private readonly ICatalogSystem _catalogSystem;

    public CatalogIndexTarget(ICatalogSystem catalogSystem)
    {
        _catalogSystem = catalogSystem;
    }

    public ContentReference ContentRoot => _catalogSystem.GetCatalogRoot();
}

Register it during startup:

services.AddSingleton<IIndexTarget, CatalogIndexTarget>();

The synchronization job collects every registered IIndexTarget, takes the distinct set of roots, and walks the descendants of each.

Run a full sync after you add a root

Registering an indexing root changes only what future sync runs discover. It does not backfill content that earlier runs skipped.

After you deploy an indexing root, run the full synchronization job so Graph receives the newly reachable content:

  1. Go to Admin > Scheduled Jobs.
  2. Select the full synchronization job.
  3. Click Start Manually.

Content indexed outside a registered root

Full sync is not the only path into the index. Delta sync reads the activity log, and event-driven indexing subscribes to CMS events. Neither one checks where the content sits in the tree, so both can index content that no registered root reaches.

A regular full sync does not undo this. Full sync only adds and updates the content it discovers. It does not delete content that it failed to reach, so content indexed from outside a registered root stays in the index.

A smooth rebuild does undo it. A smooth rebuild provisions a new slot, runs a full sync into that slot, and then swaps it live. The new slot holds only the content the tree walk discovered, plus the delta changes caught during the sync. Content outside every registered root is absent from the new slot and disappears when the rebuild commits.

❗️

Warning

Content that reaches Graph only through delta or event-driven indexing disappears at the next smooth rebuild commit. If you rely on content that no registered root reaches, register a root for it before you run a smooth rebuild.

Troubleshoot missing content

If a content item does not appear in Graph, work through the following:

  1. Check the content's position in the tree. Confirm the item is a descendant of a registered indexing root. If a custom content provider serves it, confirm that provider's entry point is covered by a root.
  2. Confirm a full sync has run. Event-driven indexing covers changes as editors publish them. Only a full sync picks up content that predates your Graph setup.
  3. Check the exclusion rules. Work through What Graph excludes within a root. Deleted content, excluded content types, and abandoned media assets are the common causes.
  4. Check the synchronization job logs. Graph logs some skip reasons and not others, so a silent skip does not mean the content was indexed. See the following table.
  5. Check property-level indexing. If the item appears but a field is missing, the cause is the property's indexing type, not content discovery. See Control field indexing.

Skip reasons Graph logs

The job summary lists items with an explicit skip reason as {contentReference}: Skipped due to {message}. Only some reasons produce a message:

Skip reasonLoggedMessage
Abandoned media assetYesSkipped index content '{name}' with key '{key}' since it is in abandoned asset folder
Unsupported content typeYesThe content type '{contentType}' is currently not supported. Content '{contentName}' ({contentGuid:N}) will not be indexed.
Document over the size limitYesSkipped indexing document {id} due to being larger than {size} MB.
Reserved property nameYesReserved name
System content nodeNoSilent
Content type excluded by conventionNoSilent
Deleted contentNoSilent
Unpublished content, when drafts are excludedNoSilent
Not a descendant of a registered rootNoSilent

Content that no registered root reaches produces no message at all, because the full sync never encounters it. Rule that cause out by checking the content's position in the tree rather than by searching the logs.

Related documentation

This page covers which content items Graph indexes. For how the properties of those items are indexed, and how to stay within the 100,000-field limit, see Control field indexing.


Did this page help you?