<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AnkeInTech]]></title><description><![CDATA[AnkeInTech]]></description><link>https://blog.ankekiener.de</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1738403926461/efea9357-220e-4376-b069-2489b5d05f15.png</url><title>AnkeInTech</title><link>https://blog.ankekiener.de</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 06 May 2026 16:00:44 GMT</lastBuildDate><atom:link href="https://blog.ankekiener.de/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Closer Look at Reference Fields in SitecoreAI - and How to Work With Them Today]]></title><description><![CDATA[Update (Jan 2026):Since this article was published, the query-related issue described below has been fixed and is now working again for both the Content Editor and the Page Builder (as also reflected in the official changelog).
This resolves the conc...]]></description><link>https://blog.ankekiener.de/a-closer-look-at-reference-fields-in-sitecoreai-and-how-to-work-with-them-today</link><guid isPermaLink="true">https://blog.ankekiener.de/a-closer-look-at-reference-fields-in-sitecoreai-and-how-to-work-with-them-today</guid><category><![CDATA[Editor Experience]]></category><category><![CDATA[SitecoreAI]]></category><category><![CDATA[xmcloud]]></category><category><![CDATA[pagebuilder]]></category><category><![CDATA[references]]></category><dc:creator><![CDATA[Anke Kiener]]></dc:creator><pubDate>Thu, 27 Nov 2025 22:38:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764278566809/f2deb1aa-4ae2-4295-b1eb-60a9f1a6b9d3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>Update (Jan 2026):</strong><br />Since this article was published, the query-related issue described below has been fixed and is now working again for both the Content Editor and the Page Builder (as also reflected in the official changelog).</p>
<p>This resolves the concrete query problem discussed here. However, it does <strong>not</strong> fully address the underlying challenges I’m seeing when working with reference fields in real-world setups.</p>
<p>A deeper dive into these limitations — including differences across field types and current Page Builder support — will follow in an upcoming article in this series.</p>
</blockquote>
<p><strong>Over the past few days, I came across something in SitecoreAI that didn’t immediately seem critical, but certainly didn’t feel right either.<br />A lookup field - a fundamental element in any Sitecore solution - suddenly showed different results depending on where it was edited.<br />In the Page Builder, the field returned an empty list. In the classic Content Editor, it worked exactly as expected.</strong><br />**So I started looking for a more robust way to configure lookup fields - and to understand what’s really going on under the hood.<br />**</p>
<p>Because this felt unusual (and I’m fairly certain earlier Sitecore XM Cloud versions behaved differently), I started to take a deeper look. After my initial analysis, I opened a support ticket, exchanged findings with colleagues and our TAM, and noticed that similar discussions were already happening on Slack.<br />What initially looked like a small inconsistency quickly turned into a broader investigation into how SitecoreAI evaluates reference fields today - and why multisite scenarios, in particular, seem to expose these differences.</p>
<p>Instead of letting these observations disappear in internal chats and ticket threads, I wanted to document them here - partly to share the findings, partly because this incident highlighted a topic I’ve been meaning to explore for a while:<br /><strong>how referenced data should ideally be modeled in SitecoreAI, how editors can work with it efficiently, and how we can give them a clear, streamlined workflow - ideally without leaving the Page Builder.</strong></p>
<p>This article is the beginning of that exploration and will likely become the first part of a small series ;-)</p>
<hr />
<h2 id="heading-part-1-understanding-the-lookup-query-issue">Part 1: Understanding the Lookup Query Issue</h2>
<p>The issue surfaced while configuring a reference field that followed a very common multisite pattern:<br />allow editors to pick items either from the <strong>current site’s Data folder</strong> or from a <strong>shared Data folder</strong> used across multiple sites. (Example use case: a Quote component where you want to select a Contact Person.)</p>
<p>This is a typical SXA-style approach, and in classic XP solutions it has always been solved with a simple union query, for example:</p>
<p><code>query:$site/Data//[@@templatename='Contact Person'] | $sharedSites/Data//[@@templatename='Contact Person']</code></p>
<p>This query still works reliably in the <strong>SitecoreAI Content Editor</strong>.<br />Editors see exactly the items they expect to see:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764282264408/6531a767-419a-466b-8472-a710453e0fdd.png" alt class="image--center mx-auto" /></p>
<p>However, when the same field is opened in the <strong>Page Builder</strong>, the lookup list is completely empty. No items, no warning, no indication that the query failed:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764282585679/1159e226-2477-40bc-ae5e-b658e40f52ac.png" alt class="image--center mx-auto" /></p>
<p>To verify whether the Page Builder simply required a different syntax, I tested an alternative version (similar to what you can find on ootb renderings)):</p>
<p><code>query:$site/Data//[@@templatename='Contact Person'] | query:$sharedSites/Data//[@@templatename='Contact Person']</code></p>
<p>Somewhat surprisingly, this version <strong>does</strong> work in the Page Builder — the items appear as expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764282530605/289131b8-089a-4663-beda-4a368809fb5d.png" alt class="image--center mx-auto" /></p>
<p>But the same syntax immediately breaks the Content Editor, which throws a <code>LookupSourceException</code> due to invalid query syntax.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764281943710/cafb311a-f891-4b30-b5f6-09310b6772d4.png" alt class="image--center mx-auto" /></p>
<p>At this point, the inconsistency becomes clear:</p>
<ul>
<li><p>The Content Editor accepts the <em>standard</em> query syntax</p>
</li>
<li><p>The Page Builder only accepts the <em>modified</em> syntax</p>
</li>
<li><p>Neither editor accepts the other editor’s format</p>
</li>
<li><p>And there is currently <strong>no single query syntax that works in both environments</strong></p>
</li>
</ul>
<p>In other words: <strong>Content Editor and Page Builder evaluate the lookup source differently and expect incompatible formats.</strong></p>
<h2 id="heading-what-you-can-do-today"><strong>What you can do today</strong></h2>
<p>For now, there isn’t a single lookup query syntax that works in both the Content Editor and the Page Builder.<br />But that doesn’t mean we’re stuck — it just means we need to be explicit about how we want editors to work with referenced data today.</p>
<p>Here are a few practical options I see right now:</p>
<ul>
<li><p><strong>Prioritize the Page Builder</strong><br />  If most authoring happens inside the Page Builder (as recommended), use the <code>query:$site/... | query:$sharedSites/...</code> pattern.<br />  It’s the format the Page Builder evaluates reliably.<br />  Just make sure to <strong>educate your editors/clients</strong> that the Content Editor might show errors for this field - and that this is expected in this setup.</p>
</li>
<li><p><strong>Prioritize the Content Editor</strong><br />  If your project still relies heavily on the Content Editor, you can keep the classic union syntax and accept that the Page Builder won’t support this field for now.<br />  Not ideal for XM Cloud in the long run, but a valid choice if Page Builder isn’t in active use yet - or if your editors are comfortable jumping into the Content Editor via the <strong>“Edit in Content Editor”</strong> link for certain fields.</p>
</li>
<li><p><strong>Challenge the data model</strong><br />  Sometimes the right question is: <em>do we really need two roots here?</em><br />  If either the site-specific or the shared location is enough, simplifying the query to a single root avoids the compatibility issue altogether.</p>
</li>
<li><p><strong>Use a multi-root Treelist as an alternative field type!</strong><br />  A multi-root Treelist uses a <code>query:</code>-based multi-root source (e.g. <code>query:$site/... | query:$sharedSites/...</code>) - and unlike a standard Lookup field, this syntax works consistently in both the Content Editor and the Page Builder!<br />  It’s a perfect replacement <strong><em>if you’re currently using a Multilist</em></strong>.<br />  <strong><em>But unfortunately, there is no equivalent for Droplink fields/single select</em></strong> (More on that in part 2).</p>
</li>
<li><p><strong>Additional ideas, I would not recommend</strong><br />  You might be tempted to take other approaches - like using a query that starts at a very high-level root (or even the whole content tree) and filtering only by template. While this technically works, it will definitely hurt performance and opens the door to other issues. Another idea is to split the selection into two separate fields - one for <code>site</code> references and one for <code>sharedSites</code>. I would not recommend either of these approaches.</p>
</li>
<li><h3 id="heading-custom-field-via-marketplace-app"><strong>Custom field via Marketplace app</strong></h3>
<p>  A custom field type from the Marketplace is an interesting option - and in the long run, it might even become the cleanest solution.<br />  But building a custom field is additional effort and can quickly become quite complex. And considering the importance of referenced data in a CMS like SitecoreAI, this is likely an area that will be addressed more consistently in the product sooner or later.</p>
</li>
</ul>
<p>None of these are perfect, but being aware of the current behavior — and choosing a clear approach per project — already helps teams avoid unexpected lookup issues.</p>
<p><strong>This discovery is just the starting point and connects to a broader topic: how editors can work with referenced data efficiently inside SitecoreAI’s Page Builder — something I’ve been exploring for a while. I’ll dig deeper into that in part 2.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Sitecore Serialization: "First Match Wins"]]></title><description><![CDATA[#sitecore #sitecoreAI #xmcloud #serialization #dailywork
When I recently reviewed the serialization modules of a customer project we had taken over,I stumbled upon something that wasn’t obvious at first glance —but explained a few weird effects we’d ...]]></description><link>https://blog.ankekiener.de/sitecore-serialization-first-match-wins</link><guid isPermaLink="true">https://blog.ankekiener.de/sitecore-serialization-first-match-wins</guid><category><![CDATA[dailywork]]></category><category><![CDATA[Sitecore]]></category><category><![CDATA[SitecoreAI]]></category><category><![CDATA[xmcloud]]></category><category><![CDATA[serialization]]></category><dc:creator><![CDATA[Anke Kiener]]></dc:creator><pubDate>Sun, 19 Oct 2025 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763914247740/c681b2b0-911e-4a56-90d7-0f777df7c06b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>#sitecore #sitecoreAI #xmcloud #serialization #dailywork</p>
<p>When I recently reviewed the serialization modules of a customer project we had taken over,<br />I stumbled upon something that wasn’t obvious at first glance —<br />but explained a few weird effects we’d seen during deployments.</p>
<p>Short version:</p>
<blockquote>
<p><strong>Serialization rules are evaluated top to bottom.</strong><br /><strong>First match wins.</strong></p>
</blockquote>
<p>Sounds obvious, but in practice it’s surprisingly easy to overlook.</p>
<hr />
<h2 id="heading-a-quick-refresher">A quick refresher</h2>
<p><strong>Sitecore Content Serialization (SCS)</strong> turns Sitecore items into versionable, shareable files — and that really pays off during deployments. SCS takes care of moving those items into the target environment, which keeps deployments consistent and pleasantly predictable.</p>
<p><em>Which</em> <em>items</em> become part of your source-controlled configuration and <em>how</em> they should behave during deployments, is defined in your <code>module.json</code> files.</p>
<p>Every <code>*.module.json</code> follows the same basic structure:</p>
<ul>
<li><p><strong>path</strong> → where your module starts</p>
</li>
<li><p><strong>scope</strong> → how deep it goes (<code>ItemOnly</code>, <code>ItemAndDescendants</code>, <code>Descendants</code>, …)</p>
</li>
<li><p><strong>allowedPushOperations</strong> → what’s allowed when pushing (<code>CreateOnly</code>, <code>CreateAndUpdate</code>, etc.…)</p>
</li>
<li><p><strong>rules</strong> → exceptions or overrides inside that module</p>
</li>
</ul>
<hr />
<p>In the client project, the module had:</p>
<ul>
<li><p>a very broad rule at the top (<code>/Settings</code>)</p>
</li>
<li><p>a more specific rule below (<code>/Settings/Site Grouping</code>)</p>
</li>
<li><p>different <code>allowedPushOperations</code> on each</p>
</li>
</ul>
<p>Something like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"items"</span>: {
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/sitecore/content/MyProject"</span>,
    <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"ItemAndDescendants"</span>,
    <span class="hljs-attr">"allowedPushOperations"</span>: <span class="hljs-string">"CreateUpdateAndDelete"</span>,
    <span class="hljs-attr">"rules"</span>: [
      {
        <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/Settings"</span>,
        <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"ItemAndDescendants"</span>,
        <span class="hljs-attr">"allowedPushOperations"</span>: <span class="hljs-string">"CreateAndUpdate"</span>
      },
...
      {
        <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/Settings/Site Grouping"</span>,
        <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"ItemAndDescendants"</span>,
        <span class="hljs-attr">"allowedPushOperations"</span>: <span class="hljs-string">"CreateOnly"</span>
      }
    ]
  }
}
</code></pre>
<p><strong>The result?</strong><br />The broad <code>/Settings</code> rule matches <em>everything</em> underneath — including <code>/Settings/Site Grouping</code>.<br />Which means: the more specific “CreateOnly” rule never gets a chance to apply.</p>
<p>**And that’s exactly what we saw: the Site Grouping item was still being updated during deployments.<br />Definitely not something you want happening in higher environments.<br />**</p>
<h2 id="heading-how-to-fix-it">How to fix it</h2>
<p>Luckily, the fix is straightforward:<br /><strong>put the more specific rule first, then the broader one.</strong></p>
<p>Serialization processes rules from top to bottom, so the specific one needs to get its turn before the general one catches everything.</p>
<p>In our case, swapping the order is all it takes:</p>
<pre><code class="lang-json"><span class="hljs-string">"rules"</span>: [
  {
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/Settings/Site Grouping"</span>,
    <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"ItemAndDescendants"</span>,
    <span class="hljs-attr">"allowedPushOperations"</span>: <span class="hljs-string">"CreateOnly"</span>
  },
...
  {
    <span class="hljs-attr">"path"</span>: <span class="hljs-string">"/Settings"</span>,
    <span class="hljs-attr">"scope"</span>: <span class="hljs-string">"ItemAndDescendants"</span>,
    <span class="hljs-attr">"allowedPushOperations"</span>: <span class="hljs-string">"CreateAndUpdate"</span>
  }
]
</code></pre>
<p>Now the <code>CreateOnly</code> rule actually applies to the Site Grouping item - exactly as intended.</p>
<hr />
<h3 id="heading-extra-tip-how-to-verify-what-actually-happens">Extra Tip: How to verify what actually happens</h3>
<p>If you want to double-check which rule Serialization is actually using for an item, the CLI can tell you.<br />The <code>ser explain</code> command shows:</p>
<ul>
<li><p>which module included the item</p>
</li>
<li><p>which rule matched</p>
</li>
<li><p>and which <code>allowedPushOperations</code> were applied</p>
</li>
</ul>
<pre><code class="lang-basic">dotnet sitecore ser explain -p <span class="hljs-string">"/sitecore/content/.../Settings/Site Grouping"</span>
</code></pre>
<p>Super handy when something behaves differently than expected and debugging mysterious “why is this still updating?” situations.</p>
<hr />
<h2 id="heading-wrap-up"><strong>Wrap-up</strong></h2>
<p>It’s a tiny detail, but it can have a surprisingly big impact.</p>
<p>One misplaced line can silently break your configuration and update items you never meant to touch.<br />Understanding how Sitecore reads these rules - <strong>first come, first served</strong> -<br />can save you a whole afternoon of head-scratching.</p>
]]></content:encoded></item><item><title><![CDATA[Packing Up and Setting Off on an Uncertain Journey]]></title><description><![CDATA[“Ich packe meine Sachen und bin raus mein Kind”

– Thomas D, from the song “Rückenwind”
I've just made the spontaneous decision to set off on a new adventure and finally put my plans into action.For so long, I've been immersed in the world of softwar...]]></description><link>https://blog.ankekiener.de/2025-01-02-packing-up-and-setting-off</link><guid isPermaLink="true">https://blog.ankekiener.de/2025-01-02-packing-up-and-setting-off</guid><category><![CDATA[other]]></category><dc:creator><![CDATA[Anke Kiener]]></dc:creator><pubDate>Sat, 01 Feb 2025 11:25:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738411093584/418c1dfb-0b22-4bd4-b5c8-fed41029107d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>“Ich packe meine Sachen und bin raus mein Kind”</p>
</blockquote>
<p>– Thomas D, from the song “Rückenwind”</p>
<p>I've just made the spontaneous decision to set off on a new adventure and finally put my plans into action.<br />For so long, I've been immersed in the world of software development, with a particular focus on Sitecore. Every day brings new experiences - I learn something new, stumble upon challenges, and work through them.<br />Time and time again, the thought has crossed my mind to share these experiences with other, to help fellow “travelers” on their own journeys. But somehow, I never really took the leap. Until now.<br />So here I am, ready to embark on this journey. Let’s see where it leads.</p>
<p><strong>A Note on the Quote:</strong></p>
<p>Starting my first article with a German quote is a small hint that I’m based in Germany ;-)</p>
<p>And yes, I’m old enough that this catchy tune from 1998 can loop in my head whenever I think about setting off on a new journey.<br />But I’m also young enough to stay excited about the constant changes in the world of software and web development, always ready to explore what’s next.</p>
]]></content:encoded></item></channel></rss>