日本語版
最新ニュース
企業

Optimizely Graph での下駄カテゴリのインデックス作成

下駄カテゴリーは素晴らしいパッケージです。これにより、タクソノミーの操作がわかりやすく直感的になりました。また、カテゴリには必要なものをすべて含めることができるため、非常に高い柔軟性が得られます。検索のフィルタリングにも役立ちます。ただし、これには小さな問題があり、Optimizely Graph を中心点としてヘッドレス アーキテクチャに取り組んでいる場合に当てはまります。 Geta カテゴリはグラフ内でインデックス付けされません。これは、スキーマには作成されたカテゴリ タイプが含まれていないことを意味します。最も重要なのは、カテゴリへのコンテンツ参照がある場合、 展開されたオブジェクトはnullです。通常、他のコンテンツ タイプと同様に、すべてのカテゴリ データが含まれます。残念ながら、カテゴリ タイプをインデックス レジスタに追加するための簡単なスイッチや構成オプションはありません。 解決する必要がある問題があります。フロントエンド アプリケーションは、カテゴリの名前または重要なデータを知る必要があります。考えられる解決策は何ですか? REST API グラフには、カテゴリ データの最も重要な部分、つまりコンテンツ参照が含まれています。これにより、FE は、ID に基づいてカテゴリ情報を返す特別に用意された API エンドポイントから必要な情報を取得できます。または、FE アプリがローカルに保存しているすべての利用可能なカテゴリに関する完全なデータ セットを返すこともできます。しかし、これは圧倒的に感じられます。カテゴリ名を取得するためだけに追加の HTTP 呼び出しが必要ですか?見た目が良くない… カスタム グラフ プロパティ特定のコンテンツ タイプの追加プロパティを使用してグラフ データを強化する方法があります。このアプローチは Optimizely Commerce コードから借用し、市場の価格設定や在庫データなどの情報を追加するために使用されます。 基本的に、カスタム プロパティは以下を実装する必要があります。 ITypedContentApiModelProperty インターフェイス…

Optimizely Graph での下駄カテゴリのインデックス作成

1767955873
2026-01-09 08:23:00

下駄カテゴリーは素晴らしいパッケージです。これにより、タクソノミーの操作がわかりやすく直感的になりました。また、カテゴリには必要なものをすべて含めることができるため、非常に高い柔軟性が得られます。検索のフィルタリングにも役立ちます。ただし、これには小さな問題があり、Optimizely Graph を中心点としてヘッドレス アーキテクチャに取り組んでいる場合に当てはまります。 Geta カテゴリはグラフ内でインデックス付けされません。これは、スキーマには作成されたカテゴリ タイプが含まれていないことを意味します。最も重要なのは、カテゴリへのコンテンツ参照がある場合、 展開されたオブジェクトはnullです。通常、他のコンテンツ タイプと同様に、すべてのカテゴリ データが含まれます。残念ながら、カテゴリ タイプをインデックス レジスタに追加するための簡単なスイッチや構成オプションはありません。

解決する必要がある問題があります。フロントエンド アプリケーションは、カテゴリの名前または重要なデータを知る必要があります。考えられる解決策は何ですか?

REST API

グラフには、カテゴリ データの最も重要な部分、つまりコンテンツ参照が含まれています。これにより、FE は、ID に基づいてカテゴリ情報を返す特別に用意された API エンドポイントから必要な情報を取得できます。または、FE アプリがローカルに保存しているすべての利用可能なカテゴリに関する完全なデータ セットを返すこともできます。しかし、これは圧倒的に感じられます。カテゴリ名を取得するためだけに追加の HTTP 呼び出しが必要ですか?見た目が良くない…

カスタム グラフ プロパティ

特定のコンテンツ タイプの追加プロパティを使用してグラフ データを強化する方法があります。このアプローチは Optimizely Commerce コードから借用し、市場の価格設定や在庫データなどの情報を追加するために使用されます。

基本的に、カスタム プロパティは以下を実装する必要があります。 ITypedContentApiModelProperty インターフェイス (ただし、DI に次のように登録します) IContentApiModelProperty)。サポートされている型を見つけるなどの目的で、すべてのプロパティに重複したコードを含める必要があることがすぐにわかります。その場合は、すべての共通関数を含む基本クラスを最初に準備することをお勧めします。

internal abstract class CustomGraphPropertyBaseTGraphModel> : ITypedContentApiModelProperty where TGraphModel : notnull
{
    private readonly ContentTypeModelRepository _contentTypeModelRepository;
    private readonly Lazy> _supportedTypes;
    
    protected IContentLoader ContentLoader { get; }

    protected CustomGraphPropertyBase(ContentTypeModelRepository contentTypeModelRepository, IContentLoader contentLoader)
    {
        _contentTypeModelRepository = contentTypeModelRepository;
        ContentLoader = contentLoader;
        
        _supportedTypes = new Lazy>(ResolveSupportedTypes);
    }

    public Type[] ContentTypes => _supportedTypes.Value.ToArray();
    
    public object GetValue(ContentApiModel contentApiModel)
    {
        var guidValue = contentApiModel.ContentLink?.GuidValue;
        if (!guidValue.HasValue)
            return NoValue;

        try
        {
            var content = ContentLoader.Get(guidValue.Value, GetLanguage(contentApiModel.Language));
            return GetValue(content);
        }
        catch
        {
            return NoValue;
        }
    }

    public abstract string Name { get; }
    protected abstract IEnumerable GetSupportedTypes();
    protected abstract TGraphModel GetValue(IContentData content);
    protected abstract TGraphModel NoValue { get; }
    
    private HashSet ResolveSupportedTypes()
    {
        var typeSet = new HashSet();
        var supportedTypes = GetSupportedTypes().ToList();

        foreach (var contentTypeModel in _contentTypeModelRepository.List())
        {
            var modelType = contentTypeModel.ModelType;
            if (supportedTypes.Any(x => x.IsAssignableFrom(modelType)))
            {
                typeSet.Add(modelType);
            }
        }
        
        return typeSet;
    }
    
    private static CultureInfo GetLanguage(LanguageModel languageModel)
    {
        var langName = languageModel.Name;
        if (string.IsNullOrWhiteSpace(langName))
        {
            return CultureInfo.InvariantCulture;
        }

        try
        {
            return CultureInfo.GetCultureInfo(langName);
        }
        catch (CultureNotFoundException)
        {
            return CultureInfo.InvariantCulture;
        }
    }
}

これにより、特定のプロパティを作成することが可能になります。たとえば、特別なインターフェイスがあるかもしれません。 IHas分類カテゴリ、コンテンツをマークし、必要なカテゴリ プロパティを強制的に含めます。その後、新しいグラフ フィールドを追加するインターフェイス実装用にカスタム プロパティを登録できます。 分類上のカテゴリ拡張、名前や説明など、必要なデータがすべてここにあります。

internal class TaxonomicCategoriesGraphProperty : CustomGraphPropertyBaseListCategoryGraphModel>>
{
    private readonly ICategoryContentLoader _categoryLoader;
    
    public TaxonomicCategoriesGraphProperty(
        ContentTypeModelRepository contentTypeModelRepository,
        IContentLoader contentLoader, 
        ICategoryContentLoader categoryLoader) : base(contentTypeModelRepository, contentLoader)
    {
        _categoryLoader = categoryLoader;
    }

    public override string Name => "TaxonomicCategoriesExpanded";
    
    protected override IEnumerableType> GetSupportedTypes()
    {
        yield return typeof(IHasTaxonomicCategories);
    }

    protected override ListCategoryGraphModel> GetValue(IContentData content)
    {
        if (content is not IHasTaxonomicCategories hasCategories)
        {
            return NoValue;
        }
        
        return hasCategories
            .TaxonomicCategories
            ?.Select(x => _categoryLoader.GetCategoryData>(x))
            .Select(x => new CategoryGraphModel(x.Name))
            .ToList() ?? [];
    }

    protected override ListCategoryGraphModel> NoValue => [];

これは非常にうまく機能しますが、重要な欠点があります。名前の更新などのカテゴリの変更は、すべてのカテゴリに自動的に入力されるわけではありません。 分類上のカテゴリ拡張 フィールド。その更新を確認するには、このフィールドを含むすべてのコンテンツ インスタンスのインデックスを再作成する必要があります。

これは、適切なコンテンツ イベントに接続し、依存するコンテンツ インスタンスを見つけて、それらのグラフの再インデックスをトリガーすることによっても修正できます。私はこの流れを避けようと努力してきましたが、常にそれが可能であるとは限りません。ただし、これについては別の記事で取り上げます。


カテゴリコンテンツタイプの登録

最近、真剣なデバッグを行った結果、別の方法を発見しました。新しいコンテンツ タイプを登録することができます。つまり、すべてのカテゴリ タイプがグラフ スキーマに追加され、展開されたオブジェクトに必要なデータが含まれることになります。他のブロック、ページ、メディアの場合と同じように機能します。しかし、 ビッグレッド ここにフラグを立てます。内部の Optimizely 名前空間からサービスのコードを登録する必要があります。

そうは言っても、技術的な詳細を詳しく見てみましょう。グラフ解決ロジックは、コンテンツ タイプ リポジトリから利用可能なすべてのコンテンツ タイプを取得し、基本タイプのインデックス作成が許可されているかどうかを確認します。ここでの最初の課題は、Geta カテゴリにはこのデータで使用できる基本型が存在せず、単に null であることです。したがって、最初のステップはそれを登録することです。コマースがコマース タイプの基本タイプを登録する方法と同様の方法でこれにアプローチすると、新しい構造アイテムを追加してプロバイダーに登録するだけで十分です。それは単に私たちの新しいものをマッピングするだけです CustomContentTypeBase 構造から下駄まで カテゴリデータ モデル。

using EPiServer.DataAbstraction.RuntimeModel;
using Geta.Optimizely.Categories;

internal static class CustomContentTypeBase
{
    
    
    
    internal static readonly ContentTypeBase Category = new(nameof(Category));
}

internal class CustomContentTypeBaseProvider : IContentTypeBaseProvider
{
    private static readonly Dictionary ContentTypeBasesMapping = new()
    {
        {
            CustomContentTypeBase.Category,
            typeof(CategoryData)
        }
    };

    public IEnumerable ContentTypeBases => ContentTypeBasesMapping.Keys;

    public Type? Resolve(ContentTypeBase contentTypeBase) => 
        ContentTypeBasesMapping.TryGetValue(contentTypeBase, out var type) ? type : null;
}

しかし、それだけでは十分ではありません。ここで、この新しい基本型にインデックスを付けることができることを Graph に伝える必要があります。そして、ここは内部コードを少し調整する必要がある場所です。 Optimizely.ContentGraph.Cms.NetCore.Internal を追加する必要があります。これは特に何もせず、リストに新しい項目を追加するだけです。

internal class CustomContentSerializer : ContentSerializer
{
    public CustomContentSerializer(
        IContentConverterResolver contentConverterResolver,
        ContentApiOptions contentApiOptions, 
        IContentTypeRepository contentTypeRepository,
        IContentGraphConverterContextFactory cgConverterContextFactory,
        IEnumerable contentApiModelFilters, 
        ISiteDefinitionResolver siteDefinitionResolver, 
        ContentGraphContextAccessor contentGraphContextAccessor,
        IOptions queryOptions,
        ConventionRepository? conventionRepository = null)
        : base(
            contentConverterResolver, 
            contentApiOptions, 
            contentTypeRepository,
            cgConverterContextFactory,
            contentApiModelFilters,
            siteDefinitionResolver,
            contentGraphContextAccessor,
            queryOptions, 
            conventionRepository)
    {
    }
    
    public override string[] SupportedContentBaseTypes
    {
        get
        {
            var supportedTypes = base.SupportedContentBaseTypes.ToList();
            
            supportedTypes.Add(CustomContentTypeBase.Category.ToString());
            return supportedTypes.ToArray();
        }
    }
}

カスタム ロジックはそれほど多くありません。新しいものを追加するだけです CustomContentTypeBase サポートされているタイプの辞書に追加します。ただし、それでも、コンストラクターに挿入された多くのサービスに依存しており、これらのサービスは簡単に変更できます。そして、Graph パッケージをアップグレードした後、私はすでにその問題に直面していました – コンパイル エラーを修正する必要がありました…

これらを用意したら、最後のステップはすべてを DI コンテナに登録することです。その後、グラフ スキーマ エクスプローラーでカテゴリを確認できるようになります。


展開されたオブジェクトにはカテゴリの詳細が含まれます。ここで重要なことは、実際のカテゴリが更新されるたびに更新されることです。


まとめ

ヘッドレス アーキテクチャの方向に進むと、新たな課題が生じます。その一つが下駄カテゴリーの力を最大限に活用することです。場合によっては、上記のような回避策が必要になります。私は、インデックスを作成する新しいコンテンツ タイプを登録するという最も最適な方法が、近い将来完全にサポートされると心から信じています。

#Optimizely #Graph #での下駄カテゴリのインデックス作成

執筆者について: nipponese

Nipponese News編集部は、国内外のニュースを日本語で分かりやすくお届けします。