HomeDev guideAPI ReferenceGraphQL
Dev guideUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Migrate Search & Navigation to Optimizely Graph

How to migrate from Search & Navigation to Optimizely Graph.

📘

Note

Some functionalities are not supported yet in Optimizely Graph, such as tracking, didyoumean, bestbets, spellcheck, and gdpr.

You can migrate your site to Optimizely Graph and use the Optimizely Graph .NET Client.

👍

Beta

The Optimizely Graph .NET Client is in beta. Contact your Customer Success Manager for information.

Install the Nuget package

Install the Optimizely Graph Client Nuget package. After installing the Optimizely.Graph.Client package, you can remove Episerver.Find package, then add to the service collection:

services.AddContentDeliveryApi(); // Required to sync CMS data
services.AddContentGraph(); // Core service to sync CMS data to Optimizely Graph service
services.AddContentGraphClient(); // Client AP

(Optional) Set your type mapping on Content Delivery

See How to customize data returned to clients with Optimizely Content Delivery to filter your mapping properties.

For example,to add SitePageData as a base content type in Alloy MVC:

Remove the abstract modifier in SitePageData and set AvailableInEditMode to false to not display in edit mode:

[ContentType(AvailableInEditMode = false)]
public class SitePageData : PageData, ICustomCssInContentArea
 {
 //properties of SitePageData
 ...
 }

Add CustomContentTypeFilter.cs file to your site, then copy and paste the following code:

using EPiServer;
using EPiServer.ContentApi.Core.Serialization;
using EPiServer.ContentApi.Core.Serialization.Internal;
using EPiServer.ContentApi.Core.Serialization.Models;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AlloyMvcTemplates
{
    [ServiceConfiguration(typeof(IContentApiModelFilter), Lifecycle = ServiceInstanceScope.Singleton)]
    internal class CustomContentTypeFilter : ContentApiModelFilter<ContentApiModel>
    {
        private readonly IContentTypeRepository _contentTypeRepository;
        private readonly IContentLoader _contentLoader;
        public CustomContentTypeFilter(IContentTypeRepository contentTypeRepository, IContentLoader contentLoader)
        {
            _contentTypeRepository = contentTypeRepository;
            _contentLoader = contentLoader;
        }
        public override void Filter(ContentApiModel contentApiModel, ConverterContext converterContext)
        {
            try
            {
                var contentType = GetContentType(contentApiModel);
                if (contentType != null)
                {
                    var abstractTypes = new List<Type>();
                    AddBaseTypes(contentType.ModelType, ref abstractTypes);
                    contentApiModel.ContentType.AddRange(abstractTypes.Select(x => x.Name).Append("Content").Distinct());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private void AddBaseTypes(Type type, ref List<Type> types)
        {
            if (type?.BaseType != null && type.BaseType != type && type.BaseType != typeof(IContent) && type.BaseType != typeof(Object))
            {
                types.Add(type.BaseType);
                AddBaseTypes(type.BaseType, ref types);
            }
        }

        private ContentType GetContentType(ContentApiModel contentApiModel)
        {
            if (contentApiModel?.ContentLink?.Id is null or 0)
            {
                return null;
            }

            var contentReference = new ContentReference(contentApiModel.ContentLink.Id.Value, contentApiModel.ContentLink?.WorkId.GetValueOrDefault() ?? 0, contentApiModel.ContentLink?.ProviderName);
            if (_contentLoader.TryGet<IContent>(contentReference, out var content))
            {
                var contentType = _contentTypeRepository.Load(content.ContentTypeID);
                if (contentType != null)
                {
                    return contentType;
                }
            }

            return null;
        }
    }
}

Reindex your data to Optimizely Graph

Run Optimizely Graph content synchronization job.

(Optional) Generate C# model class

Generate C# model classes from your schema using the automatic generation tool.

Update source code

Update your source code to use the Optimizely Graph .NET Client.

Test

Verify your search results.