<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Sujit Senapati</title><link href="http://world.optimizely.com" /><updated>2025-12-04T21:47:06.0000000Z</updated><id>https://world.optimizely.com/blogs/sujit-senapati/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Building a 360&#176; Customer Profile With AI: How Opal + Optimizely Unlock Predictive Personalization</title><link href="https://world.optimizely.com/blogs/sujit-senapati/dates/2025/12/building-a-360-customer-profile-with-ai-how-opal--optimizely-unlock-predictive-personalization/" /><id>&lt;p class=&quot;MsoNormal&quot;&gt;Creating truly relevant customer experiences requires more than collecting data&amp;mdash;it requires understanding it. Most organizations already have rich behavioral, purchase, and engagement data spread across their websites, commerce systems, customer data platforms, and marketing tools. Yet turning that raw information into &lt;strong&gt;actionable insights&lt;/strong&gt; remains a challenge.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This project demonstrates how Optimizely Data Platform (ODP) and commerce data can be combined with Opal AI to generate a &lt;strong&gt;real-time, AI-driven customer profile&lt;/strong&gt; that predicts user interests, intent, and next-best actions. The result is a 360&amp;deg; view that empowers marketers, merchandisers, and digital teams to design more relevant experiences across channels.&lt;/p&gt;
&lt;h2&gt;The Problem: Data Everywhere, Insight Nowhere&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Marketing teams often face the same obstacles:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo1; tab-stops: list .5in;&quot;&gt;Behavioral data lives in one system&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo1; tab-stops: list .5in;&quot;&gt;Purchase history lives in another&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo1; tab-stops: list .5in;&quot;&gt;Content engagement sits elsewhere&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo1; tab-stops: list .5in;&quot;&gt;Segmentation has to be updated manually&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l6 level1 lfo1; tab-stops: list .5in;&quot;&gt;Personalization rules are often static or outdated&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Even with excellent tools, teams rarely have time to analyze every customer&amp;rsquo;s journey or determine what actions to take next.&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This is where AI fills the gap.&lt;/p&gt;
&lt;h2&gt;The Solution: Opal-Powered 360&amp;deg; Customer Profile Maker&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The 360&amp;deg; profile maker uses Opal AI to unify signals from ODP and commerce data and generate:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;A complete customer summary&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Behavioral insights&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Interest and affinity prediction&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Product category tendencies&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Content preferences&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Education or learning needs&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l8 level1 lfo2; tab-stops: list .5in;&quot;&gt;Recommended actions for marketing, content, and commerce teams&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;By connecting to ODP via a custom agent tool, the Profile Maker agent retrieves customer interactions and uses AI reasoning to infer what each customer is likely to engage with next.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Think of it as having a real-time analyst for every individual visitor.&lt;/p&gt;
&lt;h2&gt;How It Works&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The architecture centers on three components:&lt;/p&gt;
&lt;h3&gt;1. ODP Customer &amp;amp; Event Data&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;ODP provides a unified customer record that includes:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Browsing history&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Purchases&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Add-to-cart behavior&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Campaign engagement&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Content interactions&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l3 level1 lfo3; tab-stops: list .5in;&quot;&gt;Device and location signals&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This is the factual backbone of the profile.&lt;/p&gt;
&lt;h3&gt;2. Profile Maker Agent (Opal AI)&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The agent performs three core steps:&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-style: normal;&quot;&gt;A. Retrieve customer profile &amp;amp; event history&lt;/span&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Uses the ODP browser tool to fetch recent activity (e.g., product views, orders, interests).&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;font-style: normal;&quot;&gt;B. Analyze patterns and infer predicted behavior&lt;/span&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The agent identifies signals such as:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo4; tab-stops: list .5in;&quot;&gt;Frequent browsing of a specific category&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo4; tab-stops: list .5in;&quot;&gt;High-value items in carts&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo4; tab-stops: list .5in;&quot;&gt;Repeated research behavior&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo4; tab-stops: list .5in;&quot;&gt;Interest in style, features, or brand families&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l1 level1 lfo4; tab-stops: list .5in;&quot;&gt;Drop-off points in past purchases&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;&lt;span style=&quot;font-style: normal;&quot;&gt;C. Generate an updated customer profile with predictive fields&lt;/span&gt;&lt;/h4&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;These predictive attributes support:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo5; tab-stops: list .5in;&quot;&gt;Product recommendation&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo5; tab-stops: list .5in;&quot;&gt;Content recommendation&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo5; tab-stops: list .5in;&quot;&gt;Customer interest tagging&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l7 level1 lfo5; tab-stops: list .5in;&quot;&gt;Education content suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This profile becomes a living document&amp;mdash;continuously enriched as behavior evolves.&lt;/p&gt;
&lt;h3&gt;3. Updated Customer Profile Stored in ODP&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;The agent writes updated attributes back to ODP.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This allows:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo6; tab-stops: list .5in;&quot;&gt;Optimizely personalization&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo6; tab-stops: list .5in;&quot;&gt;Targeted email campaigns&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo6; tab-stops: list .5in;&quot;&gt;Triggered journeys&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo6; tab-stops: list .5in;&quot;&gt;Dynamic on-site experiences&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l0 level1 lfo6; tab-stops: list .5in;&quot;&gt;Commerce recommendations&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Marketers can finally act on insights without needing a data scientist.&lt;/p&gt;
&lt;h2&gt;Real Marketing Use Cases&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This solution supports many day-to-day marketing challenges:&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;1. Predictive Product Recommendations&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If a customer frequently browses outerwear, compares materials, and revisits similar items, the profile maker can classify them as &lt;em&gt;&amp;ldquo;outerwear-intent.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This triggers personalized homepages or email recommendations.&lt;/p&gt;
&lt;h3&gt;2. Content Personalization&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If a user reads guides about sizing, care, or fit, the system can recommend:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo7; tab-stops: list .5in;&quot;&gt;Buying guides&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo7; tab-stops: list .5in;&quot;&gt;Sizing tools&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo7; tab-stops: list .5in;&quot;&gt;Comparison content&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l4 level1 lfo7; tab-stops: list .5in;&quot;&gt;Feature explainers&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This increases engagement and reduces bounce.&lt;/p&gt;
&lt;h3&gt;3. Customer Interest Tagging&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;Interests like &lt;em&gt;&amp;ldquo;workwear,&amp;rdquo; &amp;ldquo;premium casual,&amp;rdquo; &amp;ldquo;eco-friendly materials,&amp;rdquo;&lt;/em&gt; etc. can be inferred and stored as profile tags.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;These tags feed segmentation and journey triggers.&lt;/p&gt;
&lt;h3&gt;4. Education &amp;amp; Support Recommendations&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;If a customer repeatedly returns an item category or visits help documentation, the AI can suggest proactive education content.&lt;/p&gt;
&lt;h3&gt;5. Email &amp;amp; Journey Automation&lt;/h3&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;With predictive attributes stored in ODP:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l2 level1 lfo8; tab-stops: list .5in;&quot;&gt;Triggers become more accurate&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l2 level1 lfo8; tab-stops: list .5in;&quot;&gt;Journeys become more relevant&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l2 level1 lfo8; tab-stops: list .5in;&quot;&gt;Email recommendations match real behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This bridges the gap between analytics and marketing action.&lt;/p&gt;
&lt;h2&gt;Why This Matters&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;By combining ODP&amp;rsquo;s data foundation with Opal&amp;rsquo;s reasoning capabilities, businesses can:&lt;/p&gt;
&lt;ul style=&quot;margin-top: 0in;&quot;&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo9; tab-stops: list .5in;&quot;&gt;Scale personalization without manual analysis&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo9; tab-stops: list .5in;&quot;&gt;Better understand each customer&amp;rsquo;s motivation&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo9; tab-stops: list .5in;&quot;&gt;Predict what a user might need next&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo9; tab-stops: list .5in;&quot;&gt;Fuel Optimizely personalization and experimentation&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-list: l5 level1 lfo9; tab-stops: list .5in;&quot;&gt;Increase engagement, conversion, and lifetime value&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;It moves teams from &lt;strong&gt;reactive&lt;/strong&gt; to &lt;strong&gt;proactive&lt;/strong&gt; personalization&amp;mdash;at scale.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;AI isn&amp;rsquo;t replacing marketers, merchandisers, or content creators&amp;mdash;it&amp;rsquo;s empowering them with insights they&amp;rsquo;ve never had before. The 360&amp;deg; Profile Maker demonstrates how teams can convert existing customer data into meaningful predictions and turn those predictions into personalized experiences across channels.&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;This is the future of digital experience&amp;mdash;and the tools are already here.&lt;/p&gt;</id><updated>2025-12-04T21:47:06.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Efficient Catalog Metadata Management and Product Updates Using DTOs in Optimizely Commerce</title><link href="https://world.optimizely.com/blogs/sujit-senapati/dates/2024/10/process-products-nodes-and-variants-using-dtos/" /><id>&lt;p&gt;&lt;img src=&quot;/link/748ad64274e74629aba6c6deabcc8538.aspx&quot; width=&quot;1004&quot; alt=&quot;&quot; height=&quot;628&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This post explores ways to manage and update catalog metadata in Optimizely Commerce by utilizing Data Transfer Objects (DTOs). DTOs provide a lightweight, performance-optimized way to access data directly from the database, making them faster and less resource-intensive than using the Content Repository.&lt;/p&gt;
&lt;p&gt;Optimizely Commerce&amp;rsquo;s Content Repository, while feature-rich, can be processing-intensive and costly in terms of performance, especially for large catalogs. By opting for DTOs, this approach minimizes processing load and accelerates data access, making it ideal for scenarios where we need to quickly retrieve and update catalog entries, categories, or metadata.&lt;/p&gt;
&lt;h3&gt;Setting Up the Metadata Context&lt;/h3&gt;
&lt;p&gt;First, the metadata context is configured to handle specific culture and language settings. This ensures that all catalog data is accessed in the correct locale, which is critical for applications requiring consistent language or format settings:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;string originalMetaDataContextLanguage = CatalogContext.MetaDataContext.Language;
bool originalUseCurrentUiCulture = CatalogContext.MetaDataContext.UseCurrentThreadCulture;

CatalogContext.MetaDataContext.UseCurrentThreadCulture = false;
CatalogContext.MetaDataContext.Language = culture.Name;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting &lt;code&gt;UseCurrentThreadCulture&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; and explicitly specifying a language allows us to bypass the default culture settings and enforce a consistent data retrieval format.&lt;/p&gt;
&lt;h3&gt;Retrieving Categories and Entries with DTOs&lt;/h3&gt;
&lt;p&gt;Using DTOs (specifically &lt;code&gt;CatalogNodeDto&lt;/code&gt; and &lt;code&gt;CatalogEntryDto&lt;/code&gt;) offers a direct, efficient way to retrieve categories and entries without the performance overhead associated with the Content Repository. By directly accessing catalog nodes and entries, this approach reduces the time taken to retrieve and process the data.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;// Retrieve all categories
CatalogNodeDto childNodes = CatalogContext.Current.GetCatalogNodesDto(
    catalogId, 
    new CatalogNodeResponseGroup(CatalogNodeResponseGroup.ResponseGroup.CatalogNodeInfo)
);

// Retrieve entries associated with the catalog node
CatalogEntryDto catalogEntries = CatalogContext.Current.GetCatalogEntriesDto(
    catalogId, 
    new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;GetCatalogNodesDto&lt;/code&gt; and &lt;code&gt;GetCatalogEntriesDto&lt;/code&gt; methods directly query the database, returning data in a minimal format, which is much faster than loading entire content items with their associated metadata from the Content Repository.&lt;/p&gt;
&lt;h3&gt;Processing Entries and Updating Variant Associations&lt;/h3&gt;
&lt;p&gt;With the catalog data retrieved, the code iterates through each catalog entry, filtering based on specific conditions. If an entry meets the criteria, it loads a &lt;code&gt;MetaObject&lt;/code&gt; associated with that entry. The &lt;code&gt;MetaObject&lt;/code&gt; is then passed to the &lt;code&gt;UpdateProductBasedOnVariantAssociation&lt;/code&gt; method, which contains custom logic to update products based on variant relationships:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;foreach (var entry in catalogEntries.CatalogEntry)
{
    MetaClass entryMetaClass = MetaClass.Load(CatalogContext.MetaDataContext, entry.MetaClassId);

    if (!entryMetaClass.IsCatalogMetaClass)
        continue;

    if (!entryMetaClass.Name.Contains(&quot;Product&quot;))
        continue;

    MetaObject metaObject = MetaObject.Load(CatalogContext.MetaDataContext, entry.CatalogEntryId, entryMetaClass);

    // Custom update logic for products based on variant association
    this.UpdateProductBasedOnVariantAssociation(metaObject, entryMetaClass.Name, entry.Code); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By updating based on variant associations, this approach helps maintain consistency across product variations without requiring heavy content loading operations. Each &lt;code&gt;MetaObject&lt;/code&gt; represents the underlying metadata for an entry, allowing for granular updates while keeping performance in check.&lt;/p&gt;
&lt;h3&gt;Efficient Category Traversal Using In-Order Traversal&lt;/h3&gt;
&lt;p&gt;The code then proceeds to an in-order traversal of categories (nodes) within &lt;code&gt;childNodes&lt;/code&gt;. For each node, we load associated entries and apply the same update logic. Changes need to be accepted after updating the meta object on each level.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;foreach (var node in childNodes.CatalogNode)
{
    MetaClass nodeMetaClass = MetaClass.Load(CatalogContext.MetaDataContext, node.MetaClassId);

    if (!nodeMetaClass.IsCatalogMetaClass)
        continue;

    CatalogEntryDto categoryEntries = CatalogContext.Current.GetCatalogEntriesDto(
        catalogId, 
        node.CatalogNodeId, 
        new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull)
    );

    if (categoryEntries.CatalogEntry.Count == 0)
        continue;

    MetaObject nodeObject = MetaObject.Load(CatalogContext.MetaDataContext, node.CatalogNodeId, nodeMetaClass);

    foreach (var entry in categoryEntries.CatalogEntry)
    {
        MetaClass entryMetaClass = MetaClass.Load(CatalogContext.MetaDataContext, entry.MetaClassId);

        if (!entryMetaClass.IsCatalogMetaClass || !entryMetaClass.Name.Contains(&quot;Product&quot;))
            continue;

        MetaObject metaObject = MetaObject.Load(CatalogContext.MetaDataContext, entry.CatalogEntryId, entryMetaClass);

        // Update product based on variant association
        this.UpdateProductBasedOnVariantAssociation(metaObject, entryMetaClass.Name, entry.Code); 

        // Accept changes at the node level to maintain consistency
        nodeObject.AcceptChanges(CatalogContext.MetaDataContext);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using DTOs here, we avoid the overhead of the Content Repository and gain faster access to the underlying data. Each node&#39;s metadata is modified only if there are changes, and changes are accepted efficiently, reducing the need for re-indexing or reloading the full catalog.&lt;/p&gt;
&lt;h3&gt;Restoring Original Metadata Context&lt;/h3&gt;
&lt;p&gt;Once all updates are complete, we reset the metadata context to its original settings:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;CatalogContext.MetaDataContext.Language = originalMetaDataContextLanguage;
CatalogContext.MetaDataContext.UseCurrentThreadCulture = originalUseCurrentUiCulture;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;This approach to catalog metadata management and product updates is both efficient and scalable:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Efficient Data Access with DTOs&lt;/strong&gt;: DTOs offer a leaner, faster way to access and modify catalog data compared to the Content Repository, ideal for performance-critical scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Localized Context Settings&lt;/strong&gt;: Setting a specific culture and language allows for consistent data handling across different locales.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct Metadata Modification&lt;/strong&gt;: Working with &lt;code&gt;MetaObject&lt;/code&gt; and &lt;code&gt;MetaClass&lt;/code&gt; enables specific, metadata-based updates without the processing load of the full content structure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Category Traversal and Update&lt;/strong&gt;: Using in-order traversal allows for efficient updates at the node level, minimizing the need for large-scale re-indexing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By using DTOs in place of the Content Repository, this method achieves better performance, reducing the overall load on the platform while maintaining an up-to-date, accurate catalog. This technique is especially beneficial for large e-commerce sites where speed and efficiency are critical.&lt;/p&gt;</id><updated>2024-10-31T16:09:53.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Mastering clearing Cache in Optimizely CMS with ISynchronizedObjectInstanceCache &amp; MemoryCache</title><link href="https://world.optimizely.com/blogs/sujit-senapati/dates/2024/10/clear-optimizely-cache-by-its-keywords/" /><id>&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-b88u0q&quot;&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;
&lt;p&gt;In the fast-paced world of web development, efficient memory management is essential for optimizing application performance and enhancing user experience. Among the many optimization techniques available, clearing memory cache using a cache key is a fundamental approach. When storing objects in cache, developers often set an expiration policy, such as an absolute or sliding cache eviction policy, to control how long the data remains. While cache clearing is manageable in lower CMS environments, production environments can be challenging, especially when sliding cache policies make it nearly impossible to expire cached items in real time.&lt;/p&gt;
&lt;p&gt;In Optimizely, most cache management is handled by &lt;code&gt;ISynchronizedObjectInstanceCache&lt;/code&gt;. However, Optimizely doesn&amp;rsquo;t offer a straightforward method to retrieve all cache items in memory.&lt;/p&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Understanding Memory Cache and Cache Keys&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-b88u0q&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Memory Cache&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;&lt;strong&gt;:&lt;/strong&gt; This is where your web application stores data that can be accessed more quickly than if it were stored in a database or on a disk. It&#39;s a portion of RAM reserved for temporarily holding data.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-b88u0q&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Cache Key&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;: This is a unique identifier used to store and retrieve specific data from the cache. Using cache keys ensures you&#39;re managing the right data.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Why Clear the Cache?&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-b88u0q&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Performance&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;: Over time, the cache can become cluttered with outdated data, slowing down access speeds.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-b88u0q&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Data Integrity&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;: Clearing cache ensures your application uses the latest data, vital for dynamic content.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-b88u0q&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Memory Management&lt;/span&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;: Freeing up memory can prevent memory leaks and improve overall performance.&lt;/span&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Implementation Steps in Optimizely CMS&lt;/span&gt;&lt;/h3&gt;
&lt;h4&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Step 1: Identify Your Cache System&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;First, identify the caching system your application uses. In Optimizely CMS, the common choice is &lt;code&gt;ISynchronizedObjectInstanceCache&lt;/code&gt;, which relies on &lt;code&gt;MemoryCache&lt;/code&gt; internally.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Step 2: Determine the Cache Key&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static Keys&lt;/strong&gt;: For fixed data, like user profile information, a static key pattern like &lt;code&gt;&quot;user_&quot; + userID&lt;/code&gt; can be effective. In Optimizely CMS and Commerce, most caches utilize static keys, making cache management straightforward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Keys&lt;/strong&gt;: For frequently changing data (e.g., session-based), keys might incorporate timestamps or session IDs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Step 3: Clearing the Cache&lt;/span&gt;&lt;/h4&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;
&lt;p&gt;With &lt;code&gt;ISynchronizedObjectInstanceCache&lt;/code&gt;, you can only clear individual items by specifying their cache key. However, if dynamic values such as user IDs, class names, content IDs, or language IDs are used, determining the exact key can be challenging.&lt;/p&gt;
&lt;p&gt;To overcome this, you can retrieve the private property containing all cached items in &lt;code&gt;MemoryCache&lt;/code&gt; using &lt;code&gt;BindingFlags&lt;/code&gt;. This allows you to access cached entries, making it possible to locate and manage items based on a keyword search within the cache keys.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example approach to find all cache keys matching a keyword (e.g., &lt;code&gt;&quot;cacheKey&quot;&lt;/code&gt;) and then remove them from the cache using &lt;code&gt;ISynchronizedObjectInstanceCache&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-b88u0q&quot;&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-b88u0q&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var field = typeof(MemoryCache).GetProperty(&quot;StringKeyEntriesCollection&quot;, BindingFlags.NonPublic | BindingFlags.Instance);
//_memoryCache is an instance of IMemoryCache
var collection = field?.GetValue(_memoryCache) as ICollection;
var items = new List&amp;lt;string&amp;gt;();
if (collection != null)
{
	foreach (var item in collection)
	{
		var methodInfo = item.GetType().GetProperty(&quot;Key&quot;);
		var val = methodInfo?.GetValue(item);
		if (val != null)
			items.Add(val.ToString());
	}
}

var cacheKeysTobeCleaned =
	items.Where(key =&amp;gt; key.StartsWith(cacheKey, StringComparison.OrdinalIgnoreCase)).ToList(); //cacheKey is the keyword we are searching for in cacheKey

if (!cacheKeysTobeCleaned.Any())
	return NotFound();

foreach (string cacheKeyToBeCleaned in cacheKeysTobeCleaned)
{
//_cache is an instance of ISynchronizedObjectInstanceCache
	this._cache.RemoveLocal(cacheKeyToBeCleaned);
	this._cache.RemoveRemote(cacheKeyToBeCleaned);
}
			&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;This method uses reflection to access the &lt;/span&gt;&lt;span style=&quot;color: #b96ad9;&quot; class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-z32n2g r-z2wwpe r-1hkscgl r-1471scf r-1aiqnjv r-1hq4qhi r-16dba41 r-ilng1c r-trst2h r-1noe1sz r-njp1lv&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;MemoryCache&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;&lt;span style=&quot;color: #b96ad9;&quot;&gt; &lt;/span&gt;entries, finds keys matching a prefix, and clears them using &lt;/span&gt;&lt;span style=&quot;color: #b96ad9;&quot; class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-z32n2g r-z2wwpe r-1hkscgl r-1471scf r-1aiqnjv r-1hq4qhi r-16dba41 r-ilng1c r-trst2h r-1noe1sz r-njp1lv&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;ISynchronizedObjectInstanceCache&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;.&lt;/span&gt;&lt;/div&gt;

&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: Although the above approach was mostly used &lt;span&gt;only for urgent scenarios rather than regular cache management. &lt;/span&gt;Alternative and the most effective method to clear the cache for a specific list of objects is to use a master key when adding items to the cache, which serves as an additional feature of the CacheEvictionPolicy. For more information, refer to &lt;a href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/object-caching#master-keys&quot;&gt;cache objects&lt;/a&gt; documentation. While standard dependency keys within a CacheEvictionPolicy create dependencies between existing cache items, a master dependency enables grouping multiple cache entries. When a master key is specified, the cache either confirms the existence of an entry with this key or inserts a placeholder object with an indefinite timeout. This approach allows for the entire group of entries to be removed simultaneously.&lt;/span&gt;&lt;/div&gt;


&lt;h3&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Conclusion&lt;/span&gt;&lt;/h3&gt;
&lt;div class=&quot;css-146c3p1 r-bcqeeo r-1ttztb7 r-qvutc0 r-37j5jr r-a023e6 r-rjixqe r-16dba41&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;Managing cache in Optimizely CMS requires strategic approaches, especially when dealing with a large number of cache entries or dynamic keys. By understanding how to interact with &lt;/span&gt;&lt;span style=&quot;color: #b96ad9;&quot; class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-z32n2g r-z2wwpe r-1hkscgl r-1471scf r-1aiqnjv r-1hq4qhi r-16dba41 r-ilng1c r-trst2h r-1noe1sz r-njp1lv&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;ISynchronizedObjectInstanceCache&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;&lt;span style=&quot;color: #b96ad9;&quot;&gt; &lt;/span&gt;and leveraging reflection to access &lt;/span&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-z32n2g r-z2wwpe r-1hkscgl r-1471scf r-1aiqnjv r-1hq4qhi r-16dba41 r-ilng1c r-trst2h r-1noe1sz r-njp1lv&quot;&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;MemoryCache&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3&quot;&gt;, you can maintain a clean, efficient cache system. This not only enhances performance but also ensures your web application remains responsive and up-to-date with the latest data.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;</id><updated>2024-10-28T23:06:39.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>