<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexey Vidanov</title>
    <description>The latest articles on DEV Community by Alexey Vidanov (@vidanov).</description>
    <link>https://dev.to/vidanov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F279573%2F2fb2b653-7b23-4378-a8ed-60e2c1776fdf.jpg</url>
      <title>DEV Community: Alexey Vidanov</title>
      <link>https://dev.to/vidanov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vidanov"/>
    <language>en</language>
    <item>
      <title>Language Aggregation in OpenSearch: Selecting One Document Per Group by Language Preference</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Wed, 26 Nov 2025 11:04:54 +0000</pubDate>
      <link>https://dev.to/aws-builders/language-aggregation-in-opensearch-selecting-one-document-per-group-by-language-preference-4kn9</link>
      <guid>https://dev.to/aws-builders/language-aggregation-in-opensearch-selecting-one-document-per-group-by-language-preference-4kn9</guid>
      <description>&lt;p&gt;Multilingual content is common in documentation systems, product catalogs, and knowledge bases. When the same item exists in several languages, search results often become cluttered with multiple versions of the same document.&lt;/p&gt;

&lt;p&gt;A typical requirement is to return &lt;strong&gt;one document per content group&lt;/strong&gt;, chosen using a &lt;strong&gt;language preference order&lt;/strong&gt; such as &lt;code&gt;de &amp;gt; en &amp;gt; fr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This blog post presents a practical pattern for handling language aggregation. The approach is part of the open-source OpenSearch project and is fully supported in Amazon OpenSearch Service, making it suitable for both self-managed clusters and AWS-managed environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If an article exists in German, English, and French, a standard search will return all three. You want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One hit per &lt;code&gt;crossLanguageGroup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The language with the highest user preference&lt;/li&gt;
&lt;li&gt;Deterministic, predictable selection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple deduplication does not work because you must apply a ranking rule across the group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution Overview
&lt;/h2&gt;

&lt;p&gt;The solution relies on three capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Field Collapse&lt;/strong&gt; Groups all translations of the same document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripted Sort&lt;/strong&gt; Applies an explicit language ranking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyword Fields&lt;/strong&gt; Enable efficient sorting and scripting on language arrays.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz61qyskl7cgh8dk313jb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz61qyskl7cgh8dk313jb.png" alt="Workflow diagram showing a 6-step multilingual document search process using collapse" width="800" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: Multi-language document search workflow using collapse functionality. The process reduces 6 duplicate documents across German, English, and French to 3 results by grouping cross-language versions and applying language preference ranking.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Index Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tmp_multi_lang&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;crossLanguageGroup&lt;/strong&gt; stores the logical ID shared by all translations of the same item. Every language variant uses the same value, so collapse can group them reliably.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;languages&lt;/strong&gt; is an array of ISO language codes (e.g., &lt;code&gt;["de", "en"]&lt;/code&gt;). Using an array lets the script evaluate multiple languages if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.keyword&lt;/code&gt; fields&lt;/strong&gt; are essential because text fields are analyzed. The analyzer splits or lowercases values, which breaks exact matching and makes sorting impossible.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.keyword&lt;/code&gt; subfield stores the raw, untouched value, enabling:

&lt;ul&gt;
&lt;li&gt;deterministic sorting&lt;/li&gt;
&lt;li&gt;exact matches (e.g., &lt;code&gt;term&lt;/code&gt; queries)&lt;/li&gt;
&lt;li&gt;using values inside Painless scripts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Without &lt;code&gt;.keyword&lt;/code&gt;, collapse and scripted sorting would not work correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Data
&lt;/h2&gt;

&lt;p&gt;Load with &lt;code&gt;POST tmp_multi_lang/_bulk&lt;/code&gt; using NDJSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"de"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Willkommen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dies ist eine Einführung. Source: Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is an introduction. Source: Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bienvenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ceci est une introduction. Source: Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xyz789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How to search in OpenSearch. Source: Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xyz789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recherche"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Comment chercher dans OpenSearch. Source: Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"de"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Produktübersicht"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dies ist eine Produktbeschreibung."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crossLanguageGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product Overview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is a product description."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Core Query
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tmp_multi_lang/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collapse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crossLanguageGroup.keyword"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"_script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"painless"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"lang_order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"de"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"""
            int best = 100;
            def order = params.lang_order;
            if (doc.containsKey('languages.keyword')) {
              for (def l : doc['languages.keyword']) {
                if (order.containsKey(l)) {
                  int ord = (int) order.get(l);
                  if (ord &amp;lt; best) { best = ord; }
                }
              }
            }
            return best;
          """&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"desc"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Query Stage
&lt;/h3&gt;

&lt;p&gt;Matches all documents containing &lt;code&gt;"Reply"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collapse Stage
&lt;/h3&gt;

&lt;p&gt;Groups documents by &lt;code&gt;crossLanguageGroup.keyword&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Script Sort Stage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Iterates the &lt;code&gt;languages&lt;/code&gt; array&lt;/li&gt;
&lt;li&gt;Checks each language in the priority map&lt;/li&gt;
&lt;li&gt;Selects the lowest value (best match)&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;100&lt;/code&gt; as fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tie Breaking
&lt;/h3&gt;

&lt;p&gt;If two documents share the same priority, &lt;code&gt;_score&lt;/code&gt; decides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Result
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abc123/de, abc123/en, abc123/fr, xyz789/en, xyz789/fr, klm654/de
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After collapse with &lt;code&gt;de &amp;gt; en &amp;gt; fr&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abc123/de, xyz789/en, klm654/de
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyword fields&lt;/strong&gt; expose doc values, making sorting fast and predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripted sort&lt;/strong&gt; applies a strict language hierarchy, not a soft boost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collapse&lt;/strong&gt; guarantees exactly one document per crossLanguageGroup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Painless&lt;/strong&gt; correctly handles multi-value arrays like &lt;code&gt;languages&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything works together to deliver deterministic, language-aware selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Errors and Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. "Text fields are not optimised for operations"
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;.keyword&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;doc['languages.keyword']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. "unknown field [lang]"
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;lang&lt;/code&gt; must be inside the script object.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Casting errors
&lt;/h3&gt;

&lt;p&gt;Use explicit casting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int ord = (int) order.get(l);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. "Illegal list shortcut value [values]"
&lt;/h3&gt;

&lt;p&gt;Iterate normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (def l : doc['languages.keyword']) { ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Approach: Score-Based Selection
&lt;/h2&gt;

&lt;p&gt;This method uses query-time boosts to &lt;em&gt;encourage&lt;/em&gt; certain languages rather than enforcing a strict order. Each language gets a different weight: German +3, English +2, French +1.&lt;/p&gt;

&lt;p&gt;When OpenSearch calculates the score, documents that match higher-boosted languages naturally rise to the top.&lt;/p&gt;

&lt;p&gt;After scoring, &lt;strong&gt;collapse&lt;/strong&gt; picks the highest-scoring document per &lt;code&gt;crossLanguageGroup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means in practice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a group has &lt;code&gt;de&lt;/code&gt;, &lt;code&gt;en&lt;/code&gt;, and &lt;code&gt;fr&lt;/code&gt;, the German version usually wins because it has the highest boost.&lt;/li&gt;
&lt;li&gt;But if the English document has stronger text relevance, its score may exceed the German one.&lt;/li&gt;
&lt;li&gt;The boosts add to the full-text score, so the effect is &lt;strong&gt;soft preference&lt;/strong&gt;, not a strict ranking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Good fit:&lt;/strong&gt; simple setups where speed matters and minor inconsistencies are acceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not ideal:&lt;/strong&gt; cases requiring deterministic de &amp;gt; en &amp;gt; fr without exceptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tmp_multi_lang/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reply"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"should"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages.keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"de"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages.keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"languages.keyword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collapse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crossLanguageGroup.keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"desc"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; fast and simple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; boosts are additive, not strict priority&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Language-aware document aggregation in OpenSearch is solved cleanly by combining collapse, a Painless script-based sort, and keyword-backed language fields. Script sorting provides reliable, deterministic selection, while score-based boosting offers a faster but less strict alternative.&lt;/p&gt;

&lt;p&gt;This pattern is useful anywhere multilingual data creates noise in search results. By grouping documents, applying a clear language hierarchy, and keeping sorting deterministic, teams can deliver cleaner UX across documentation portals, product catalogs, and knowledge bases. It also works well with personalization and dynamic language preferences.&lt;/p&gt;

&lt;p&gt;If you want to build cleaner multilingual search or explore how to apply language-aware ranking in Amazon OpenSearch Service, feel free to reach out.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://www.reply.com/storm-reply/en" rel="noopener noreferrer"&gt;Reply&lt;/a&gt;, we help teams design scalable, predictable, and user-centred search workflows — from proof of concept to fully aligned production deployments.&lt;/p&gt;

</description>
      <category>opensearch</category>
      <category>elasticsearch</category>
      <category>search</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Perceptual Color Similarity Search with Amazon OpenSearch Service</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Thu, 09 Oct 2025 09:32:01 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-perceptual-color-similarity-search-with-amazon-opensearch-service-4ph</link>
      <guid>https://dev.to/aws-builders/building-perceptual-color-similarity-search-with-amazon-opensearch-service-4ph</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Traditional keyword search fails for color matching. A customer searching for "burgundy" won't find "wine red" or "maroon," even though these colors are visually almost identical. The problem goes beyond vocabulary: human color perception is far richer than our limited naming system. While the human eye can distinguish millions of shades, we use only a few hundred common color names. Most colors exist in the unnamed spaces between "navy" and "royal blue," or "burgundy" and "crimson."&lt;/p&gt;

&lt;p&gt;Simple &lt;strong&gt;RGB (Red, Green, Blue)&lt;/strong&gt; distance calculations make this gap even wider. Two colors with nearly identical RGB values can appear very different, while visually similar ones may be far apart numerically. Because RGB describes how screens display color rather than how humans perceive it, it fails to recognize real-world similarities, especially when lighting or device conditions change.&lt;/p&gt;

&lt;p&gt;To close this gap, we should switch from RGB to &lt;strong&gt;CIELAB&lt;/strong&gt;, a color space designed to align with human vision. LAB describes color in terms of lightness and opponent color channels (green to red, blue to yellow), creating distances that reflect perceptual differences. This makes it ideal for comparing colors under varying lighting, shadows, or image quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgexjuj10ft76mb9pral4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgexjuj10ft76mb9pral4.gif" alt="Color similarity analysis of clothing images from &amp;lt;br&amp;gt;
Pexels showing four t-shirts with extracted color palettes and &amp;lt;br&amp;gt;
technical color data. Displays black, blue volunteer shirt, yellow, &amp;lt;br&amp;gt;
and blue garments with corresponding RGB, LAB, and HSV color values. &amp;lt;br&amp;gt;
Each image shows dominant and secondary colors with precise numerical &amp;lt;br&amp;gt;
color measurements for comparison." width="720" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We applied this approach in counterfeit detection. By indexing garments' colors in LAB and monitoring marketplace images, we detected suspicious listings where the perceptual distance ΔE exceeded a tuned threshold (ΔE &amp;gt; 15). Combined with metadata and text analysis, this reduced false positives and cut manual review workload in our proof of concept.&lt;/p&gt;

&lt;p&gt;This article demonstrates how to build a production-ready perceptual color similarity search using &lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt; with &lt;strong&gt;k-nearest neighbor (k-NN)&lt;/strong&gt; capabilities and the &lt;strong&gt;CIELAB color space&lt;/strong&gt;, a combination that enables systems to see color the way humans do.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why RGB Distance Fails
&lt;/h2&gt;

&lt;p&gt;RGB (Red, Green, Blue) is built for &lt;em&gt;displaying&lt;/em&gt; color on screens, not for &lt;em&gt;measuring&lt;/em&gt; how similar two colors look. Distances in RGB space often disagree with human perception.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jcy47tiqyyi81m1x0mq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jcy47tiqyyi81m1x0mq.png" alt="Comparison showing RGB distance limitations: two color pairs with identical RGB distance (52) but vastly different perceptual similarity - dark blue to olive appears clearly different (ΔE=25) while bright red tones appear only noticeable (ΔE=7), demonstrating why RGB fails for color similarity measurement" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider two pairs with the same RGB distance:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Same distance, very different perception&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark blue RGB(30, 30, 60) vs olive RGB(60, 60, 30)&lt;/li&gt;
&lt;li&gt;Euclidean distance: 52&lt;/li&gt;
&lt;li&gt;Human perception: colors are completely different (ΔE ≈ 25)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 2: Same distance, nearly identical perception&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark red RGB(200, 100, 100) vs light red RGB(230, 130, 130)&lt;/li&gt;
&lt;li&gt;Euclidean distance: 52&lt;/li&gt;
&lt;li&gt;Human perception: colors are similar (ΔE ≈ 7)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;br&gt;
Identical numerical distances can produce opposite visual outcomes. RGB distance does not predict how people see color differences because brightness and hue interactions matter far more than simple channel-wise arithmetic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this happens&lt;/strong&gt;&lt;br&gt;
RGB treats red, green, and blue as independent, equally weighted axes. Human vision does not. Our eyes respond nonlinearly to brightness (greater sensitivity in darker ranges) and encode color through opponent channels (red vs green, blue vs yellow). As a result, equal RGB distances rarely correspond to equal perceptual differences.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: CIELAB Color Space
&lt;/h2&gt;

&lt;p&gt;To align computer vision with human perception, we need a different color space. CIELAB (commonly written as LAB) is an international standard color space designed by the Commission Internationale de l'Éclairage to be perceptually uniform. In LAB, the same numerical distance corresponds to roughly the same perceived color difference, regardless of whether you're comparing dark blues, bright yellows, or muted grays. This perceptual uniformity makes LAB ideal for similarity search.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kg2k4v45dsluj2v6otd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kg2k4v45dsluj2v6otd.gif" alt="Interactive Color Galaxy visualization showing 2000 color nodes distributed in 3D space using RGB display color space. Animated scatter plot with colors transitioning from blue on the left through green, white in center, to yellow and red on the right, with purple below. Includes crosshair cursor for navigation and color space selector dropdown." width="720" height="581"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  LAB Structure
&lt;/h3&gt;

&lt;p&gt;LAB separates color into three components that mirror how human vision processes color:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L*&lt;/strong&gt; (Lightness): 0 (black) to 100 (white), roughly aligned to perceived brightness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a*&lt;/strong&gt;: green–red opponent channel; negative = green, positive = red (≈ −128 to +128)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b*&lt;/strong&gt;: blue–yellow opponent channel; negative = blue, positive = yellow (≈ −128 to +128)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ΔE (Delta E): Measuring Perceptual Distance
&lt;/h3&gt;

&lt;p&gt;In LAB space, the Euclidean distance between two colors is &lt;strong&gt;ΔE (Delta E)&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔE76 = √[(L₂ - L₁)² + (a₂ - a₁)² + (b₂ - b₁)²]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indicative interpretation based on empirical studies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ΔE ≤ 1&lt;/strong&gt;: Not perceptible under normal viewing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ΔE 1–2&lt;/strong&gt;: Perceptible with close observation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ΔE 2–10&lt;/strong&gt;: Noticeable; "similar but slightly different"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ΔE &amp;gt; 10&lt;/strong&gt;: Clearly different&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most applications, &lt;strong&gt;ΔE76&lt;/strong&gt; (simple Euclidean distance) is sufficient. For precision-critical cases (e.g., cosmetics, paint), use &lt;strong&gt;ΔE2000&lt;/strong&gt;, which compensates for known non-uniformities (notably in blue regions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The pipeline extracts representative colors, converts them to LAB, and indexes vectors for fast similarity search:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F983kbk8fsjg7cktaza9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F983kbk8fsjg7cktaza9r.png" alt="AWS architecture diagram showing color processing workflow.&amp;lt;br&amp;gt;
User connects to API Gateway, which triggers Lambda function to &amp;lt;br&amp;gt;
extract RGB and convert to LAB color space. Data flows to Amazon &amp;lt;br&amp;gt;
OpenSearch Service Domain for indexing and search. Second Lambda &amp;lt;br&amp;gt;
function handles additional RGB to LAB conversion. S3 Bucket stores &amp;lt;br&amp;gt;
processed data. All components are within AWS Cloud VPC private &amp;lt;br&amp;gt;
subnet." width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once colors are in LAB space, finding similar colors becomes a standard k-NN problem that OpenSearch's vector search capabilities handle efficiently.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: RGB to LAB Conversion
&lt;/h3&gt;

&lt;p&gt;First, extract a representative color (e.g., with OpenCV k-means clustering over product pixels, Amazon Rekognition features, or a masked region average for the product area). Then convert RGB to LAB using &lt;code&gt;colormath&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;colormath.color_objects&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sRGBColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LabColor&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;colormath.color_conversions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convert_color&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rgb_to_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Convert RGB (0-255) to normalized LAB vector.
    Normalization keeps dimensions on comparable scales for k-NN.
    Without this, L* (0-100 range) would dominate distances.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;rgb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sRGBColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_upscaled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convert_color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LabColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;lab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lab_l&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# L* [0,100] -&amp;gt; [0,1]
&lt;/span&gt;        &lt;span class="n"&gt;lab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lab_a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# a* [-128,127] -&amp;gt; ~[-1,1]
&lt;/span&gt;        &lt;span class="n"&gt;lab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lab_b&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;    &lt;span class="c1"&gt;# b* [-128,127] -&amp;gt; ~[-1,1]
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Example: Convert a burgundy coat color
&lt;/span&gt;&lt;span class="n"&gt;lab_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rgb_to_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lab_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# [0.4036, 0.4555, 0.2576]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Multi-color products:&lt;/strong&gt; For items with several prominent colors, either (a) index the &lt;strong&gt;dominant&lt;/strong&gt; color (simple, smaller index), or (b) index the &lt;strong&gt;top N&lt;/strong&gt; colors as separate docs sharing the same &lt;code&gt;product_id&lt;/code&gt; (better recall; merge duplicates at read time).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Create OpenSearch Index
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Security note (production):&lt;/strong&gt; Use VPC placement, IAM roles or fine-grained access control, and sign REST calls with AWS Signature Version 4.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/product-colors&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"index.knn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"index.number_of_shards"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"index.number_of_replicas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"product_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"color_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lab_vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"knn_vector"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dimension"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hnsw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"engine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lucene"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"space_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"l2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ef_construction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"keyword"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"float"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;lab_vector&lt;/code&gt; uses &lt;code&gt;space_type: "l2"&lt;/code&gt; (Euclidean distance), aligning with &lt;strong&gt;ΔE76&lt;/strong&gt;. HNSW provides fast approximate nearest neighbors; tune &lt;code&gt;m&lt;/code&gt;/&lt;code&gt;ef_construction&lt;/code&gt; for your scale and accuracy needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Index Products
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opensearchpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenSearch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;helpers&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;lab_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rgb_to_lab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rgb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_index&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product-colors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;color_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;color_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lab_vector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;lab_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brand&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;brand&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bulk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Indexed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Errors: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Query Similar Colors
&lt;/h3&gt;

&lt;p&gt;Basic similarity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/product-colors/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"knn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lab_vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.52&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"k"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Fetch &lt;code&gt;k=50&lt;/code&gt; candidates to improve recall, then return &lt;code&gt;size=20&lt;/code&gt; to keep payloads small.)&lt;/p&gt;

&lt;p&gt;Combine color similarity with business filters to ensure relevance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/product-colors/_search&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"knn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"lab_vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.52&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"k"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"term"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"brand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Premium Outerwear Co."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Optional ΔE2000 Re-Ranking
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;ΔE2000&lt;/strong&gt; when tiny shade differences matter (cosmetics, paint, textiles). For general e-commerce, &lt;strong&gt;ΔE76&lt;/strong&gt; is typically sufficient and faster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;colorspacious&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rerank_with_delta_e2000&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Re-rank candidates using ΔE2000 for maximum perceptual accuracy.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;query_lab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# L* [0,100]
&lt;/span&gt;        &lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# a* [-128,127]
&lt;/span&gt;        &lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;   &lt;span class="c1"&gt;# b* [-128,127]
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cand&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lab_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cand&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lab_vector&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;cand_lab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;128.0&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;delta_e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colorspacious&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deltaE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_lab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cand_lab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_space&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CIELab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;delta_e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cand&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cand&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cand&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;top_n&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fashion E-Commerce: Alternative Product Recommendations
&lt;/h3&gt;

&lt;p&gt;Index each product's dominant color in LAB and use a moderate &lt;strong&gt;ΔE&lt;/strong&gt; threshold (up to ~8) to include related shades (wine, maroon, oxblood). Combine with size/brand/category filters to keep results relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cosmetics: Precise Shade Matching
&lt;/h3&gt;

&lt;p&gt;Use tight &lt;strong&gt;ΔE&lt;/strong&gt; thresholds (&amp;lt; 2) plus &lt;strong&gt;ΔE2000&lt;/strong&gt; re-ranking. Optionally filter by undertone (warm/cool/neutral). This reduces returns and builds trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brand Protection: Counterfeit Detection
&lt;/h3&gt;

&lt;p&gt;Detect subtle color deviations in logos/branding. Index genuine logo LAB vectors and monitor marketplace listings for significant deviations; flag when &lt;strong&gt;ΔE &amp;gt; 15&lt;/strong&gt; for review. This approach reduced manual review workload by ~40% in a PoC and complements image/text analysis pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;skimage.color&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deltaE_ciede2000&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rerank_with_delta_e2000&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Re-rank candidates using true ΔE2000 for maximum perceptual accuracy.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# Prepare query
&lt;/span&gt;    &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_lab_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Build candidate array (n,3)
&lt;/span&gt;    &lt;span class="n"&gt;cand_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lab_vector&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lab_vector&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lab_vector&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;128.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Compute ΔE2000 for all candidates at once
&lt;/span&gt;    &lt;span class="n"&gt;q_rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newaxis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;cand_arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;delta_es&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deltaE_ciede2000&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_rep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cand_arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Sort and return top_n
&lt;/span&gt;    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;argsort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta_es&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="n"&gt;top_n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standardize photography&lt;/strong&gt; (D65 ~6500K) and camera settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Work in LAB; avoid raw RGB similarity.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle backgrounds&lt;/strong&gt; (segmentation/cropping to product pixels).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose color strategy&lt;/strong&gt;: dominant color vs. top-N colors per item.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance &amp;amp; Scale
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with ΔE76;&lt;/strong&gt; add ΔE2000 only if user tests require it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combine with business filters&lt;/strong&gt; (category, brand, price, size).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune HNSW&lt;/strong&gt; (&lt;code&gt;m&lt;/code&gt;, &lt;code&gt;ef_construction&lt;/code&gt;, and &lt;code&gt;ef_search&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security &amp;amp; Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secure the domain&lt;/strong&gt; (VPC, IAM/FGAC, TLS, SigV4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alarms&lt;/strong&gt; for p95 latency and memory pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate&lt;/strong&gt; using CTR, conversion, complaints, and latency telemetry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User tests&lt;/strong&gt; to calibrate ΔE thresholds per domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B pilots&lt;/strong&gt; before full rollout; monitor CTR, conversion, bounce, returns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxs1yo5obnvegbcdjo18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxs1yo5obnvegbcdjo18.png" alt="Close-up photograph of hands holding color swatches or &amp;lt;br&amp;gt;
paint samples arranged in a gradient from dark browns and burgundies &amp;lt;br&amp;gt;
on the left to bright oranges and reds on the right. Each swatch &amp;lt;br&amp;gt;
appears to have color codes or names printed on them, demonstrating &amp;lt;br&amp;gt;
the subtle variations in color tones and the challenge of color &amp;lt;br&amp;gt;
classification and naming." width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building perceptual color similarity search is about aligning technology with how humans actually see. Using CIELAB vectors and k-NN search in Amazon OpenSearch Service bridges that gap, allowing systems to understand color differences the way people do. Whether in fashion, cosmetics, or brand protection, it enables intuitive, human-centric experiences that go far beyond simple RGB filters.&lt;/p&gt;

&lt;p&gt;If you are exploring how to make your product search perceptually aware or want to prototype an OpenSearch-based similarity engine, feel free to reach out.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;&lt;a href="https://www.reply.com/storm-reply/en" rel="noopener noreferrer"&gt;Reply&lt;/a&gt;&lt;/strong&gt;, we help organizations design intelligent, scalable, and vision-aligned search solutions from proof of concept to production.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>machinelearning</category>
      <category>search</category>
    </item>
    <item>
      <title>How to Use Amazon OpenSearch Service Index Aliases with Knowledge Bases in Amazon Bedrock</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Wed, 30 Jul 2025 13:31:42 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-use-amazon-opensearch-service-index-aliases-with-knowledge-bases-in-amazon-bedrock-5gi7</link>
      <guid>https://dev.to/aws-builders/how-to-use-amazon-opensearch-service-index-aliases-with-knowledge-bases-in-amazon-bedrock-5gi7</guid>
      <description>&lt;p&gt;Many teams start experimenting with Amazon Bedrock Knowledge Bases using the default setup. It works fine — until it doesn’t.&lt;/p&gt;

&lt;p&gt;Once your workloads stabilize, you’ll likely want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To &lt;strong&gt;optimize the mapping&lt;/strong&gt; (e.g., adjust analyzers or add new fields)&lt;/li&gt;
&lt;li&gt;To &lt;strong&gt;change shard counts&lt;/strong&gt; for scaling&lt;/li&gt;
&lt;li&gt;To &lt;strong&gt;version your data and test new schema ideas safely&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without index aliases, making these changes requires &lt;strong&gt;downtime&lt;/strong&gt; or &lt;strong&gt;recreating the KB&lt;/strong&gt; — an annoying and error-prone process.&lt;/p&gt;

&lt;p&gt;Index aliases solve this by decoupling Bedrock from the physical index. You keep the Bedrock configuration pointing to a stable name (&lt;code&gt;bedrock_index&lt;/code&gt;), while swapping the backend index version (&lt;code&gt;bedrock_index_v1 → bedrock_index_v2&lt;/code&gt;) invisibly.&lt;/p&gt;

&lt;h1&gt;
  
  
  OpenSearch Vector Storage Options (At a Glance)
&lt;/h1&gt;

&lt;p&gt;Zoom image will be displayed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64rm6c025rjo4wioxl4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64rm6c025rjo4wioxl4t.png" alt="Comparison table between Amazon OpenSearch Service and Self-Managed OpenSearch, showing differences in setup, scaling, monitoring, security, cost structure, maintenance, integration, high availability, upgrades, and use case fit. Amazon OpenSearch emphasizes automation and AWS integration, while Self-Managed offers more customization and manual control." width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What Are Index Aliases and Why Use Them?
&lt;/h1&gt;

&lt;p&gt;An &lt;strong&gt;index alias&lt;/strong&gt; is a logical pointer to one or more real indices in OpenSearch. You configure Bedrock to use a fixed alias name (e.g., &lt;code&gt;bedrock_index&lt;/code&gt;), while the actual data resides in versioned indices (&lt;code&gt;bedrock_index_v1&lt;/code&gt;, &lt;code&gt;bedrock_index_v2&lt;/code&gt;, ...).&lt;/p&gt;

&lt;h1&gt;
  
  
  Benefits of Using Aliases:
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Downtime Schema Changes:&lt;/strong&gt; Swap backend index without reconfiguring Bedrock&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Rollbacks:&lt;/strong&gt; Revert to previous index in seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue/Green Deployments:&lt;/strong&gt; Test new index versions behind the same alias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Access Controls:&lt;/strong&gt; Apply policies to a single alias instead of multiple indices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Management:&lt;/strong&gt; Route hot/cold data behind one consistent alias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner Code and Integrations:&lt;/strong&gt; External tools or apps always talk to the same alias&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;**Performance note:*&lt;/em&gt;* &lt;em&gt;Aliases introduce negligible latency. Read/write operations perform the same as direct index access, unless multiple indices are targeted.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Alias Swap
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyssrze748fl0mm7wz1ds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyssrze748fl0mm7wz1ds.png" alt="A flow diagram illustrating how Amazon OpenSearch Service uses index aliases to enable zero-downtime data updates in a Knowledge Base. It shows a process where a new index is created, data is ingested, and once ready, an alias is swapped from the old index to the new one. This ensures seamless transition for applications querying the alias, without interruption or code change. Arrows indicate the alias (kb-alias) pointing first to the old index, then being updated to the new index." width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step-by-Step Guide
&lt;/h1&gt;

&lt;p&gt;Implementing index aliases for Amazon Bedrock Knowledge Bases with Amazon OpenSearch Service requires a few careful setup steps — but once done, you gain flexibility, versioning, and zero-downtime upgrades.&lt;/p&gt;

&lt;p&gt;This guide walks you through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the required permissions and access policies,&lt;/li&gt;
&lt;li&gt;how to configure OpenSearch correctly, and&lt;/li&gt;
&lt;li&gt;how to use aliases with &lt;strong&gt;existing&lt;/strong&gt; or &lt;strong&gt;new&lt;/strong&gt; Knowledge Bases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you’re retrofitting aliases into a running system or designing for future-proofing from day one, these instructions will help you avoid disruptions and enable smooth schema evolution.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;Before starting, make sure your environment meets these conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Permissions:&lt;/strong&gt; The Bedrock service role must have explicit permissions to interact with your OpenSearch domain and indices. Use the following policy as a template:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "es:ESHttpGet",
                "es:ESHttpPost", 
                "es:ESHttpPut",
                "es:ESHttpDelete"
            ],
            "Resource": [
                "arn:aws:es:&amp;lt;region&amp;gt;:&amp;lt;accountId&amp;gt;:domain/&amp;lt;domainName&amp;gt;/&amp;lt;indexName&amp;gt;/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "es:DescribeDomain"
            ],
            "Resource": [
                "arn:aws:es:&amp;lt;region&amp;gt;:&amp;lt;accountId&amp;gt;:domain/&amp;lt;domainName&amp;gt;"
            ]
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public OpenSearch Domain:&lt;/strong&gt; Bedrock Knowledge Bases &lt;strong&gt;do not yet support VPC access&lt;/strong&gt;. Ensure your domain is public and reachable from Bedrock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch Access Policy:&lt;/strong&gt; Your OpenSearch domain must allow access from the Bedrock role. Example policy:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::&amp;lt;accountId&amp;gt;:role/&amp;lt;BedrockServiceRole&amp;gt;"
            },
            "Action": [
                "es:ESHttpGet",
                "es:ESHttpPost",
                "es:ESHttpPut", 
                "es:ESHttpDelete",
                "es:DescribeDomain"
            ],
            "Resource": [
                "arn:aws:es:&amp;lt;region&amp;gt;:&amp;lt;accountId&amp;gt;:domain/&amp;lt;domainName&amp;gt;",
                "arn:aws:es:&amp;lt;region&amp;gt;:&amp;lt;accountId&amp;gt;:domain/&amp;lt;domainName&amp;gt;/*"
            ]
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace , , , , and  with your actual values.&lt;/p&gt;

&lt;h1&gt;
  
  
  Alias Integration Scenarios
&lt;/h1&gt;

&lt;p&gt;Once the IAM and access policies are in place, you’re ready to apply index aliases. There are &lt;strong&gt;two main paths&lt;/strong&gt; depending on your current state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you already have a working Bedrock Knowledge Base, follow &lt;strong&gt;Scenario A&lt;/strong&gt; to transition to aliases.&lt;/li&gt;
&lt;li&gt;If you’re starting fresh, &lt;strong&gt;Scenario B&lt;/strong&gt; shows how to set it up the right way from the beginning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  A. Using Aliases with an Existing Knowledge Base
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the current Bedrock index&lt;/strong&gt; (e.g., &lt;code&gt;bedrock_index&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a new versioned index&lt;/strong&gt; with your updated schema and settings:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT bedrock_index_v2
{
  "settings": { "number_of_shards": 1, "number_of_replicas": 1, "knn": true },
  "mappings": {
    "properties": {
      "bedrock-knowledge-base-default-vector": {
        "type": "knn_vector",
        "dimension": 1024,
        "method": { "engine": "faiss", "name": "hnsw", "space_type": "l2" }
      },
      "AMAZON_BEDROCK_TEXT": { "type": "text" },
      "AMAZON_BEDROCK_METADATA": { "type": "text", "index": false }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Reindex your data&lt;/strong&gt; from the old index into the new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST _reindex
{
  "source": { "index": "bedrock_index" },
  "dest": { "index": "bedrock_index_v2" }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;**Validation tip:*&lt;/em&gt;*&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET _cat/aliases/bedrock_index?v
GET bedrock_index/_search?size=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Switch the alias and remove the original index&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE bedrock_index
POST _aliases
{
  "actions": [
    { "add": { "index": "bedrock_index_v2", "alias": "bedrock_index" } }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  B. Setting Up a New Knowledge Base from Scratch
&lt;/h1&gt;

&lt;p&gt;If you haven’t created the Knowledge Base yet, you can start clean with the alias approach. This gives you full flexibility from day one.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a temporary placeholder index&lt;/strong&gt; to satisfy the Bedrock setup wizard:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT bedrock_index
{
  "settings": { "number_of_shards": 1, "number_of_replicas": 1, "knn": true },
  "mappings": {
    "properties": {
      "bedrock-knowledge-base-default-vector": {
        "type": "knn_vector",
        "dimension": 1024,
        "method": { "engine": "faiss", "name": "hnsw", "space_type": "l2" }
      },
      "AMAZON_BEDROCK_TEXT": { "type": "text" },
      "AMAZON_BEDROCK_METADATA": { "type": "text", "index": false }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create your production index&lt;/strong&gt; with the intended schema and settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT bedrock_index_v1
{ /* use desired schema here */ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Swap the alias to point to the real index&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE bedrock_index
POST _aliases
{
  "actions": [
    { "add": { "index": "bedrock_index_v1", "alias": "bedrock_index" } }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Schema Evolution Workflow (Using Aliases)
&lt;/h1&gt;

&lt;p&gt;Use this pattern to apply schema changes without downtime:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a new versioned index&lt;/strong&gt; Example: &lt;code&gt;bedrock_index_v2&lt;/code&gt; with updated mappings or settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reindex the data&lt;/strong&gt; Copy documents from the current index to the new one using &lt;code&gt;_reindex&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test and validate&lt;/strong&gt; Run sample queries, check document counts, and confirm relevance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update the alias&lt;/strong&gt; Point &lt;code&gt;bedrock_index&lt;/code&gt; alias to the new index using &lt;code&gt;_aliases&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST _aliases
{
 "actions": [
 { "remove": { "index": "bedrock_index_v1", "alias": "bedrock_index" } },
 { "add": { "index": "bedrock_index_v2", "alias": "bedrock_index" } }
 ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Clean up old indices&lt;/strong&gt; Delete outdated versions like &lt;code&gt;bedrock_index_v1&lt;/code&gt;(optional but recommended)&lt;/p&gt;

&lt;h1&gt;
  
  
  Error Handling &amp;amp; Troubleshooting
&lt;/h1&gt;

&lt;p&gt;Even with careful planning, issues can arise during reindexing or alias management. Here’s how to address common problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alias Update Fails&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure the alias name isn’t already assigned to another index&lt;/li&gt;
&lt;li&gt;Make alias updates atomic using the &lt;code&gt;_aliases&lt;/code&gt; API (remove+add in one request)&lt;/li&gt;
&lt;li&gt;Confirm you have write permissions for the domain and target indices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Missing or Mismatched Data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare document counts across indices using &lt;code&gt;GET /&amp;lt;index&amp;gt;/_count&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Re-run &lt;code&gt;_reindex&lt;/code&gt; with a filtered query to catch missed documents&lt;/li&gt;
&lt;li&gt;Watch for document ID collisions or field mapping mismatches&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;**Pro tip:*&lt;/em&gt;* &lt;em&gt;Always validate the final setup with:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GET _cat/aliases?v&lt;br&gt;
GET bedrock_index/_search?size=0&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Reindex Operation Fails&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;GET _tasks&lt;/code&gt; to check task status and diagnose errors&lt;/li&gt;
&lt;li&gt;Run reindex asynchronously using &lt;code&gt;wait_for_completion=false&lt;/code&gt; for better control and retry logic&lt;/li&gt;
&lt;li&gt;Check OpenSearch logs or CloudWatch for throttling or mapping issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make the &lt;code&gt;_reindex&lt;/code&gt; request &lt;strong&gt;asynchronous&lt;/strong&gt;, use the &lt;code&gt;?wait_for_completion=false&lt;/code&gt; query parameter. This allows the task to run in the background, and you can later track it using the returned task ID.&lt;/p&gt;

&lt;h1&gt;
  
  
  Asynchronous Reindex Example
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST _reindex?wait_for_completion=false
{
  "source": { "index": "bedrock_index" },
  "dest": { "index": "bedrock_index_v2" }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Response
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "task": "tUV03FsmR8Kkz5mF6J9xxxx:12345"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Check Status
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET _tasks/tUV03FsmR8Kkz5mF6J9xxxx:12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also cancel it if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST _tasks/tUV03FsmR8Kkz5mF6J9xxxx:12345/_cancel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Rollback Procedure
&lt;/h1&gt;

&lt;p&gt;If something goes wrong after an alias switch, rolling back is simple — provided you’ve kept the old index.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retain previous index versions&lt;/strong&gt; Always keep earlier versions (e.g., &lt;code&gt;bedrock_index_v1&lt;/code&gt;) until validation is complete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repoint the alias&lt;/strong&gt; If issues arise, restore the alias to the previous version:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST _aliases
{
 "actions": [
 { "remove": { "index": "bedrock_index_v2", "alias": "bedrock_index" } },
 { "add": { "index": "bedrock_index_v1", "alias": "bedrock_index" } }
 ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Verify rollback success&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET _cat/aliases?v
GET bedrock_index/_search?q=test&amp;amp;size=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Pro Tips
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Use versioned index names like &lt;code&gt;bedrock_index_v1&lt;/code&gt;, &lt;code&gt;bedrock_index_v2&lt;/code&gt; to track schema evolution&lt;/li&gt;
&lt;li&gt;Automate reindexing and alias switching in your CI/CD pipeline&lt;/li&gt;
&lt;li&gt;Always validate with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET _cat/aliases?v
GET bedrock_index/_search?size=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;During migration, consider setting:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"index.blocks.write": true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"index.blocks.read_only_allow_delete": true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to prevent unintended writes to old indices.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bottom Line
&lt;/h1&gt;

&lt;p&gt;Until Amazon Bedrock natively supports index aliases, using OpenSearch aliases is the best way to enable &lt;strong&gt;continuous schema evolution with zero downtime&lt;/strong&gt;. For anything beyond quick prototypes or minimal workloads, a &lt;strong&gt;managed OpenSearch domain&lt;/strong&gt; with &lt;strong&gt;versioned indices&lt;/strong&gt; and &lt;strong&gt;alias control&lt;/strong&gt;offers better cost-efficiency, observability, and long-term flexibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you’re unsure how to structure your Bedrock Knowledge Base or want to explore advanced OpenSearch patterns, feel free to drop me a message.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At Reply, we help organizations design scalable, secure, and future-ready AI architectures — whether you’re just getting started or optimizing production workloads.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>opensearch</category>
      <category>aws</category>
      <category>bedrock</category>
    </item>
    <item>
      <title>Building Custom Script Plugins in Amazon OpenSearch Service: A Technical Deep Dive</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Tue, 17 Jun 2025 08:22:47 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-custom-script-plugins-in-amazon-opensearch-service-a-technical-deep-dive-3lnl</link>
      <guid>https://dev.to/aws-builders/building-custom-script-plugins-in-amazon-opensearch-service-a-technical-deep-dive-3lnl</guid>
      <description>&lt;p&gt;Amazon OpenSearch Service now supports &lt;strong&gt;custom plugins&lt;/strong&gt;, allowing advanced users to extend the search engine’s functionality beyond its out-of-the-box features. In this deep dive, we focus on the newest plugin type – &lt;strong&gt;Script Plugins&lt;/strong&gt; – and explore how to create one, how they differ from built-in scripts, and best practices for developing and deploying them. This guide provides a tutorial-style walkthrough with detailed technical insights.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Custom Plugins?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OpenSearch plugins&lt;/strong&gt; are modular extensions that run within the OpenSearch cluster, enabling custom functionality such as analyzers, queries, and scoring logic. While self-managed OpenSearch (and historically Elasticsearch) has long supported these plugins, Amazon OpenSearch Service (AOS) did not allow user-developed plugins—until late 2024.&lt;/p&gt;

&lt;p&gt;That changed with the release of &lt;strong&gt;version 2.15&lt;/strong&gt;, which introduced support for &lt;strong&gt;custom plugins&lt;/strong&gt; in the managed service. This opened up new possibilities for developers to tailor AOS to meet specific application needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline of Plugin Support in Amazon OpenSearch Service
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version 2.15 (Late 2024)&lt;/strong&gt; – Custom plugin support launched with initial focus on &lt;code&gt;AnalysisPlugin&lt;/code&gt; and &lt;code&gt;SearchPlugin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;May 2025&lt;/strong&gt; – &lt;code&gt;ScriptPlugin&lt;/code&gt; support was added, enabling advanced use cases such as custom scoring, filtering, and field transformations within queries.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Currently Supported Plugin Types in AOS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AnalysisPlugin&lt;/code&gt;&lt;/strong&gt; – Add custom analyzers, tokenizers, or filters to extend text analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SearchPlugin&lt;/code&gt;&lt;/strong&gt; – Create custom query types, scoring logic, suggesters, or aggregations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MapperPlugin&lt;/code&gt;&lt;/strong&gt; – Define custom field types and control how data is indexed and stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ScriptPlugin&lt;/code&gt;&lt;/strong&gt; &lt;em&gt;(since 2.15)&lt;/em&gt; – Embed custom scripting engines to implement complex query-time logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;em&gt;As of mid-2025, other plugin types—such as &lt;code&gt;IngestPlugin&lt;/code&gt;, &lt;code&gt;ActionPlugin&lt;/code&gt;, and &lt;code&gt;EnginePlugin&lt;/code&gt;—are not supported in Amazon OpenSearch Service.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Script Plugins: Core Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Is a Script Plugin?
&lt;/h3&gt;

&lt;p&gt;In OpenSearch, scripts (written in the built-in &lt;strong&gt;Painless&lt;/strong&gt; scripting language) are often used in queries for custom scoring, filtering, or field transformations. A &lt;strong&gt;script plugin&lt;/strong&gt; allows you to go beyond what Painless scripts can do by adding new scripting logic in Java or even introducing entirely new scripting &lt;em&gt;languages&lt;/em&gt; to OpenSearch. As the Tinder engineering team put it, &lt;em&gt;a script plugin is essentially a &lt;code&gt;run()&lt;/code&gt; function that takes query parameters and a document (“lookup”) as input and produces a relevance score (or decision) as output&lt;/em&gt;. In other words, a script plugin lets you inject custom code into the scoring process of the search engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Script Plugins vs. Painless Scripts
&lt;/h3&gt;

&lt;p&gt;Script plugins offer several advantages over standard Painless inline scripts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Richer Logic&lt;/strong&gt; – You can implement complex algorithms and leverage Java libraries or external frameworks. (Painless is sandboxed and limited to basic operations.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New Scripting Languages&lt;/strong&gt; – You aren’t limited to Painless; a plugin can define a new script language or domain-specific language for OpenSearch queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; – Custom script engines are written in Java and compiled, which can yield better performance than interpreted Painless scripts for heavy computations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greater Control&lt;/strong&gt; – Script plugins run inside the OpenSearch JVM with broader privileges. This gives you more power (e.g. access to low-level APIs or optimized data structures) than the sandboxed environment of Painless. (Of course, with this power comes the responsibility to ensure safety and stability.)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  When to Choose Script Plugins
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Script Plugin&lt;/th&gt;
&lt;th&gt;Painless Script&lt;/th&gt;
&lt;th&gt;Application Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Best (compiled Java)&lt;/td&gt;
&lt;td&gt;⚠️ Moderate&lt;/td&gt;
&lt;td&gt;❌ Higher latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complex Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Full Java capabilities&lt;/td&gt;
&lt;td&gt;⚠️ Limited&lt;/td&gt;
&lt;td&gt;✅ Most flexible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Requires deployment&lt;/td&gt;
&lt;td&gt;✅ No deployment&lt;/td&gt;
&lt;td&gt;✅ No deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Requires redeployment&lt;/td&gt;
&lt;td&gt;✅ Easy to update&lt;/td&gt;
&lt;td&gt;✅ Easy to update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Not allowed&lt;/td&gt;
&lt;td&gt;❌ Not allowed&lt;/td&gt;
&lt;td&gt;✅ Full access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Optimized&lt;/td&gt;
&lt;td&gt;⚠️ Moderate&lt;/td&gt;
&lt;td&gt;❌ Higher overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before implementing a script plugin, consider these alternatives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Painless Scripts&lt;/strong&gt;: For simpler use cases, offering a good balance of flexibility and performance with no deployment required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Application Layer&lt;/strong&gt;: When you need maximum flexibility or access to external services, though it comes with higher latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built-in Features&lt;/strong&gt;: OpenSearch's built-in features like function score queries, runtime fields, and script fields might already provide what you need.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Limitations and Considerations for Script Plugins in Amazon OpenSearch Service
&lt;/h3&gt;

&lt;p&gt;Before using script plugins in Amazon OpenSearch Service, be aware of the following constraints:&lt;/p&gt;

&lt;h4&gt;
  
  
  No External API Calls
&lt;/h4&gt;

&lt;p&gt;Script plugins can't access external services or HTTP endpoints. This sandboxing ensures security and performance stability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Version Compatibility
&lt;/h4&gt;

&lt;p&gt;Only specific OpenSearch versions support custom plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supported: &lt;strong&gt;2.15&lt;/strong&gt;, &lt;strong&gt;2.17&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Not supported: &lt;strong&gt;2.19&lt;/strong&gt; (in our tests in June 2025, plugin validation failed on AWS-managed clusters)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Blue/Green Deployment Required
&lt;/h4&gt;

&lt;p&gt;Plugin installation triggers a blue/green deployment. The cluster is recreated behind the scenes. There is no downtime, but installation can take time. Plan accordingly in production.&lt;/p&gt;

&lt;h4&gt;
  
  
  Feature Limitations
&lt;/h4&gt;

&lt;p&gt;Custom plugins disable several AWS-managed features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-Cluster Search/Replication&lt;/li&gt;
&lt;li&gt;Remote Reindexing&lt;/li&gt;
&lt;li&gt;Auto-Tune&lt;/li&gt;
&lt;li&gt;Multi-AZ with Standby&lt;/li&gt;
&lt;li&gt;AWS-hosted OpenSearch Dashboards (requires self-hosting)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Performance Impact
&lt;/h4&gt;

&lt;p&gt;Script logic runs per document at query time and may increase latency or resource usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing a Custom Script Plugin (Step-by-Step)
&lt;/h2&gt;

&lt;p&gt;In this section, we’ll walk through creating a custom script plugin for OpenSearch. Our example will be a “Hello World” script plugin with a GenAI-powered scoring function. This plugin will demonstrate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Custom Scoring Logic&lt;/strong&gt; – A scoring algorithm that considers multiple factors (product rating, price, stock availability, recency of updates, etc.) to adjust relevance scores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameterized Configuration&lt;/strong&gt; – The ability to adjust scoring weights and thresholds at query time via parameters (so you can fine-tune the behavior without changing the code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Optimizations&lt;/strong&gt; – Efficient calculations, input validation, and error handling to minimize performance overhead and ensure stability.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Development Environment Setup
&lt;/h3&gt;

&lt;p&gt;For this example, we have a sample project available on GitHub that contains the full plugin implementation. You can use it as a starting point for your own plugin development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Clone the example repository
git clone https://github.com/vidanov/opensearch-script-plugin-hello-world.git
cd opensearch-script-plugin-hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Ensure you have a Java 17 JDK and Gradle available, as OpenSearch 2.x plugins use Java 17.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6maxq4tcg54ptwyaikot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6maxq4tcg54ptwyaikot.png" alt="Checking Java version in the console" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure and Organization
&lt;/h3&gt;

&lt;p&gt;The project follows a typical OpenSearch plugin layout. Key files and directories include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;genai-script-plugin-with-ai/
├── src/
│   ├── main/java/com/example/
│   │   └── HelloWorldScriptPlugin.java    # Main plugin implementation (Java)
│   ├── main/resources/
│   │   └── plugin-descriptor.properties   # Plugin metadata (name, version, type)
│   └── test/java/com/example/
│       └── HelloWorldScriptPluginTest.java # Unit tests for the plugin logic
├── build.gradle                           # Gradle build configuration for OpenSearch
└── README.md                              # Documentation and usage instructions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure is generated by the OpenSearch plugin build tools. The Java class &lt;code&gt;HelloWorldScriptPlugin.java&lt;/code&gt; is our primary focus – it defines the plugin and the custom script engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Implementation
&lt;/h3&gt;

&lt;p&gt;Our plugin class needs to extend the base &lt;code&gt;Plugin&lt;/code&gt; class and implement the &lt;code&gt;ScriptPlugin&lt;/code&gt; interface provided by OpenSearch. This requires us to supply a custom &lt;strong&gt;Script Engine&lt;/strong&gt;. Essentially, the script engine is where we define the logic of our new scripting language. Below is a key part of the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorldScriptPlugin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Plugin&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ScriptPlugin&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ScriptEngine&lt;/span&gt; &lt;span class="nf"&gt;getScriptEngine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Settings&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ScriptContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;contexts&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HelloWorldScriptEngine&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this snippet, we override &lt;code&gt;getScriptEngine(...)&lt;/code&gt; to return an instance of our custom &lt;code&gt;HelloWorldScriptEngine&lt;/code&gt;. This engine (implemented as an inner class or separate class) registers a new script language – in our case called &lt;code&gt;"hello_world"&lt;/code&gt; – with OpenSearch. The script engine is responsible for compiling script source code and producing a &lt;code&gt;ScoreScript&lt;/code&gt; that OpenSearch can execute for each document during queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the script engine works:&lt;/strong&gt; Inside &lt;code&gt;HelloWorldScriptEngine&lt;/code&gt;, we define how to handle different script contexts. For a score script, our engine provides a factory that uses the parameters and document fields to calculate a score. For example, if the script &lt;code&gt;source&lt;/code&gt; is &lt;code&gt;"custom_score"&lt;/code&gt;, our engine’s &lt;code&gt;ScoreScript&lt;/code&gt; will read the document’s fields (rating, price, stock, etc.) and the provided params (thresholds, boosts, penalties) and compute a final score. All of this logic is written in Java, giving us full flexibility in how scoring is done. (You could also implement other script functions or additional script &lt;code&gt;source&lt;/code&gt; names, e.g. different scoring strategies, within the same plugin.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Parameterized Scoring Implementation
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of script plugins in Amazon OpenSearch Service is the ability to &lt;strong&gt;parameterize the scoring logic&lt;/strong&gt;. Instead of hard-coding thresholds and weights, the plugin can &lt;strong&gt;read parameters from the query&lt;/strong&gt; at runtime.&lt;/p&gt;

&lt;p&gt;This makes your scoring configurable, testable, and adaptive — ideal for scenarios like A/B testing, personalization, or multi-tenant ranking logic.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why Use Parameterized Scoring?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Tuning at Query Time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Plugin Redeploy Required&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple Strategies via One Plugin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support for A/B Testing and Experiments&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How It Works (With Code Example)
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;GenAIScoreScriptFactory&lt;/code&gt;, parameters are parsed using helpers like &lt;code&gt;pDouble()&lt;/code&gt; and &lt;code&gt;pString()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;ratingThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pDouble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rating_threshold"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;priceThreshold&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pDouble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"price_threshold"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;ratingWeight&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pDouble&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rating_weight"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ratingField&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rating_field"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rating"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values are passed at query time. You can modify them without changing the plugin code.&lt;/p&gt;




&lt;h3&gt;
  
  
  Example: Passing Parameters in a Query
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"script_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"match_all"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weighted_score"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello_world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"rating_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"avg_rating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"price_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"discounted_price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"rating_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"price_weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"max_price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;500.0&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We invoke the &lt;code&gt;weighted_score&lt;/code&gt; strategy inside the plugin.&lt;/li&gt;
&lt;li&gt;We override field names and scoring weights at query time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Switching Between Scoring Strategies
&lt;/h3&gt;

&lt;p&gt;The plugin supports different scoring strategies (&lt;code&gt;weighted_score&lt;/code&gt;, &lt;code&gt;custom_score&lt;/code&gt;, &lt;code&gt;popularity_score&lt;/code&gt;) based on the script source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExplanationHolder&lt;/span&gt; &lt;span class="n"&gt;explanation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"weighted_score"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scriptSource&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;weightedScore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"popularity_score"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scriptSource&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;popularityScore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;customScore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// fallback&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can switch strategies with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popularity_score"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to rebuild or redeploy — simply change the script source in the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Amazon Q Developer to Create and Implement a Java Score Script Plugin
&lt;/h2&gt;

&lt;p&gt;If you're building custom scoring logic for Amazon OpenSearch Service, you don’t have to start from scratch. &lt;strong&gt;Amazon Q Developer&lt;/strong&gt; can generate the entire Java class for your plugin — including parameterized scoring logic, plugin structure, and runtime selection of different strategies — from a single, well-crafted prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Define the Logic in Plain English
&lt;/h3&gt;

&lt;p&gt;Start by describing your goal clearly. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I want to create a scoring plugin that boosts well-rated and cheap products, penalizes out-of-stock items, and includes a popularity score based on views, sales, and review count. All thresholds and weights should be configurable via query parameters."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Use a Single Prompt in Q Developer
&lt;/h3&gt;

&lt;p&gt;You can paste the following prompt into Q Developer to generate the entire plugin code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a Java ScoreScript plugin for Amazon OpenSearch Service (Java 11 compatible) named `HelloWorldScriptPlugin`.

The plugin should:

1. Support a strategy called `popularity_score` with the following logic:
   - Normalize `views`, `sales`, `review_count`, and `rating`
   - Use logarithmic scaling for `views`, `sales`, and `review_count`
   - Use: log(value + 1) / log(max_value + 1)
   - Normalize `rating` by dividing by 5.0

2. Allow configurable weights via params:
   - `views_weight`, `sales_weight`, `reviews_weight`, `rating_weight`
   - Provide default weights (e.g., 0.25 for each)

3. Compute the final score as the weighted sum of the normalized values.

4. Parse parameters using helper method `pDouble(params, key, defaultValue)`

5. Extract document field values using `docDouble(field, defaultValue)`.

6. Add a fallback strategy `custom_score` with simplified logic: multiply three boosts based on rating, price, and stock.

7. Add support for passing `scriptSource` as a string (e.g. "popularity_score") to select between scoring strategies.

Generate the full plugin, including the `Plugin`, `ScriptPlugin`, `ScoreScript.Factory`, and `ScoreScript` logic.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: What You'll Get from Q Developer
&lt;/h3&gt;

&lt;p&gt;Q Developer will typically generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A plugin class implementing &lt;code&gt;ScriptPlugin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A custom &lt;code&gt;ScriptEngine&lt;/code&gt; with support for &lt;code&gt;ScoreScript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A factory that reads parameters and selects logic&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ScoreScript&lt;/code&gt; that implements:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;customScore()&lt;/code&gt; logic with boost/penalty&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;popularityScore()&lt;/code&gt; logic using weighted normalized values&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute()&lt;/code&gt; method with strategy selection logic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Helper methods for safe parameter and field access&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Adjust and Compile
&lt;/h3&gt;

&lt;p&gt;After you get the code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review field names and adjust if needed.&lt;/li&gt;
&lt;li&gt;Place the code inside a Gradle-based plugin scaffold.&lt;/li&gt;
&lt;li&gt;Ensure Java 17 and OpenSearch 2.x compatibility.&lt;/li&gt;
&lt;li&gt;Use the OpenSearch Gradle plugin to build your &lt;code&gt;.zip&lt;/code&gt; package.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can then deploy this plugin to your Amazon OpenSearch Service cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and Operations
&lt;/h2&gt;

&lt;p&gt;Once your custom script plugin is developed and tested, the next step is to deploy it to an Amazon OpenSearch Service domain. Deploying a plugin on AWS involves preparing the plugin as a zip package, uploading it, and then instructing the OpenSearch Service to install it on your domain. Here we outline the requirements and steps for a successful deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites and Requirements
&lt;/h3&gt;

&lt;p&gt;Before deploying a custom plugin, ensure your target OpenSearch domain meets the following requirements (these are mandated by AWS for custom plugins):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch Version 2.15 or 2.17&lt;/strong&gt; – Custom plugins are supported only on versions 2.15+ (and remember, not on 2.19 yet).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node-to-node encryption enabled&lt;/strong&gt; – Your domain must have node-to-node encryption turned on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption at rest enabled&lt;/strong&gt; – The domain must have encryption of data at rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS enforced&lt;/strong&gt; – Only HTTPS access is allowed (no plaintext HTTP).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS security policy&lt;/strong&gt; – The domain should use a modern TLS security policy (e.g. &lt;code&gt;Policy-Min-TLS-1-2-PFS-2023-10&lt;/code&gt; or newer).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Upload to S3
aws s3 cp build/distributions/hello-world-genai-script-plugin.zip s3://your-bucket/plugins/

# Create package
aws opensearch create-package \
  --package-name hello-world-genai-script-plugin \
  --package-type ZIP-PLUGIN \
  --package-source S3BucketName=genai-plugin-bucket,S3Key=plugins/hello-world-genai-script-plugin.zip \
  --engine-version OpenSearch_2.15 \
  --region &amp;lt;YOUR_AWS_REGION&amp;gt;

# Wait till the package is validated, associate
aws opensearch associate-package \
    --package-id &amp;lt;PACKAGE_ID&amp;gt; \
    --domain-name &amp;lt;OPENSEARCH_DOMAIN_NAME&amp;gt; \
    --region &amp;lt;YOUR_AWS_REGION&amp;gt;

# Verify
aws opensearch list-packages-for-domain --domain-name &amp;lt;OPENSEARCH_DOMAIN_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Plugin installation triggers &lt;strong&gt;blue/green deployment&lt;/strong&gt;—no downtime but takes time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkup0t5krjnah51ncxnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkup0t5krjnah51ncxnt.png" alt="Customer plugins" width="800" height="460"&gt;&lt;/a&gt;&lt;br&gt;
 You can filter the custom plugins in the AWS management console&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodholc005hk5ech0vtw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodholc005hk5ech0vtw5.png" alt="Plugin Package ID" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flnouz71dp5pau21o2yqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flnouz71dp5pau21o2yqb.png" alt="Custom Plugin Page" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Plugin usage examples
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Example: Basic Script Score Query
&lt;/h3&gt;

&lt;p&gt;To illustrate, consider an e-commerce product search scenario. We want to boost products that are highly rated, reasonably priced, in stock, and recently updated. We have deployed our &lt;code&gt;HelloWorldScriptPlugin&lt;/code&gt; which defines a script language &lt;code&gt;"hello_world"&lt;/code&gt; with a script function called &lt;code&gt;"custom_score"&lt;/code&gt;. Here’s how a search query might use this custom script with parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# Let us create an example product index

PUT products_test
{
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "rating": {
        "type": "float"
      },
      "price": {
        "type": "float"
      },
      "stock": {
        "type": "integer"
      },
      "last_updated": {
        "type": "date"
      },
      "views": {
        "type": "integer"
      },
      "sales": {
        "type": "integer"
      }
    }
  }
}

# Let us add some products

POST products_test/_bulk?refresh=true
{"index":{}}
{"name":"Alpha Wireless Headphones","rating":4.6,"price":45.0,"stock":12,"last_updated":"2025-05-20","views":1000,"sales":150}
{"index":{}}
{"name":"Beta Noise-Cancelling Headphones","rating":4.9,"price":120.0,"stock":5,"last_updated":"2025-05-05","views":5000,"sales":400}
{"index":{}}
{"name":"Gamma Budget Earbuds","rating":3.9,"price":25.0,"stock":30,"last_updated":"2025-04-15","views":250,"sales":50}
{"index":{}}
{"name":"Delta Premium Over-Ear","rating":4.3,"price":220.0,"stock":0,"last_updated":"2025-04-01","views":3000,"sales":250}
{"index":{}}
{"name":"Epsilon Sport Earbuds","rating":4.1,"price":60.0,"stock":8,"last_updated":"2025-05-30","views":1800,"sales":300}

# And test the first query

GET products_test/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "name": "wireless headphones"
        }
      },
      "script_score": {
        "script": {
          "lang": "hello_world",
          "source": "custom_score",
          "params": {
            "rating_threshold": 4.0,
            "rating_boost": 1.5,
            "price_threshold": 50.0,
            "cheap_boost": 1.3,
            "expensive_penalty": 0.8,
            "out_of_stock_penalty": 0.3,
            "base_multiplier": 2.0,
            "fallback_score": 0.5,
            "recency_factor": 0.1,
            "popularity_weight": 0.7,
            "price_weight": 0.3
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this query, we search for products with descriptions matching “wireless headphones,” then apply a &lt;code&gt;script_score&lt;/code&gt; to modify the relevance score of each result using our plugin’s logic. We pass a number of parameters to &lt;code&gt;custom_score&lt;/code&gt; that control how the scoring works. Here’s what each parameter means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rating Threshold &amp;amp; Boost:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rating_threshold&lt;/code&gt; – The minimum rating (e.g. average customer review) for a product to be considered “highly rated” and receive a boost. In our example, 4.0 stars.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rating_boost&lt;/code&gt; – The multiplier to apply if the product’s rating exceeds the threshold. (1.5x in this case, meaning highly-rated products get a 1.5× score boost from the rating factor.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Price Parameters:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;price_threshold&lt;/code&gt; – A price cutoff to distinguish “cheap” vs “expensive” products (here $50).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cheap_boost&lt;/code&gt; – Multiplier for products priced under the threshold (1.3x, giving cheaper items a boost).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expensive_penalty&lt;/code&gt; – Multiplier for products over the threshold (0.8x, slightly penalizing pricier items).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Stock Parameter:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;out_of_stock_penalty&lt;/code&gt; – Multiplier to apply if an item is out of stock (0.3x in the example, significantly reducing the score for items that aren’t available to purchase).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Scoring Weights:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;popularity_weight&lt;/code&gt; – Weight (relative importance) of the item’s popularity in the overall score calculation (e.g. 0.7).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;price_weight&lt;/code&gt; – Weight of the price factor in the overall score (e.g. 0.3). &lt;em&gt;(These weights might be used inside the script to combine factors like popularity vs price impact. In our simple example, they could control a weighted sum, but how they’re applied depends on the script’s code.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Recency Factor:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;recency_factor&lt;/code&gt; – A decay factor for recency (e.g. 0.1). This could be used to give a small boost to newer or recently updated products, or conversely to decay older items’ scores over time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Base Multiplier:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;base_multiplier&lt;/code&gt; – An overall score multiplier applied at the end of the calculation (in our case 2.0, meaning after all other factors the score is doubled). This can be useful to calibrate the output of the script to a desired range or importance relative to the original query score.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Fallback Score:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fallback_score&lt;/code&gt; – A default score to return if the script cannot compute a meaningful score for a document (for example, if required fields are missing or an error occurs). Here it’s 0.5. Using a fallback ensures that an error in script execution doesn’t completely drop the document from results; it still gets a baseline score.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These parameters correspond to how we wrote the script logic in the plugin. For instance, the plugin might check each document’s &lt;code&gt;rating&lt;/code&gt; field against &lt;code&gt;rating_threshold&lt;/code&gt; to decide whether to apply &lt;code&gt;rating_boost&lt;/code&gt;. It likely multiplies factors like rating boost, price boost/penalty, and stock penalty together (as we implemented) and then multiplies by &lt;code&gt;base_multiplier&lt;/code&gt;. The &lt;code&gt;fallback_score&lt;/code&gt; would be returned if any exception or missing data prevents the normal calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Scoring Strategies
&lt;/h3&gt;

&lt;p&gt;The real power of parameterized scripts is that you can adjust the scoring to different scenarios by simply changing the parameters. You might even store and reuse parameter sets for various contexts. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Holiday Season:&lt;/strong&gt; During a holiday shopping season, you might want to &lt;strong&gt;aggressively boost highly-rated products&lt;/strong&gt;(assuming reviews matter more during gift shopping) and also &lt;strong&gt;raise the price threshold&lt;/strong&gt; (people may spend more on gifts). You could use parameters like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET products_test/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "lang": "hello_world",
        "source": "custom_score",
        "params": {
          "rating_threshold": 4.0,
          "rating_boost": 2.0,
          "price_threshold": 100.0,
          "cheap_boost": 1.5,
          "expensive_penalty": 0.9,
          "out_of_stock_penalty": 0.1
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parameter Explanations:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rating_threshold&lt;/code&gt;: 4.0 — Only highly rated items get boosted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rating_boost&lt;/code&gt;: 2.0 — Extra score for items above the threshold.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;price_threshold&lt;/code&gt;: 100.0 — Defines "cheap" items during promotions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cheap_boost&lt;/code&gt;: 1.5 — Boost cheaper items more.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expensive_penalty&lt;/code&gt;: 0.9 — Slight penalty for costly products.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;out_of_stock_penalty&lt;/code&gt;: 0.1 — Heavy penalty if the item is unavailable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this holiday configuration, we doubled the rating boost and increased the cheap boost, while being more lenient on expensive items (0.9 penalty is a mild reduction) because shoppers might splurge more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clearance Sale:&lt;/strong&gt; For a clearance sale scenario, you might want to &lt;strong&gt;heavily favor cheaper items&lt;/strong&gt; and &lt;strong&gt;don’t require as high a rating&lt;/strong&gt; (since clearance items might not all be top-rated). A parameter set could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET products_test/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "lang": "hello_world",
        "source": "custom_score",
        "params": {
          "rating_threshold": 3.5,
          "rating_boost": 1.2,
          "price_threshold": 25.0,
          "cheap_boost": 2.0,
          "expensive_penalty": 0.5,
          "out_of_stock_penalty": 0.2
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation of Parameters:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rating_threshold: 3.5&lt;/code&gt; – Includes more moderately rated products.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rating_boost: 1.2&lt;/code&gt; – Smaller positive impact for meeting rating.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;price_threshold: 25.0&lt;/code&gt; – Marks very cheap items.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cheap_boost: 2.0&lt;/code&gt; – Strong push for clearance deals.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expensive_penalty: 0.5&lt;/code&gt; – Heavy penalty for high-cost items.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;out_of_stock_penalty: 0.2&lt;/code&gt; – Medium penalty for unavailable items.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, anything above $25 is considered expensive and heavily penalized (0.5 multiplier), encouraging cheaper items to rise to the top. Highly-rated isn’t as important (threshold 3.5 and only 1.2x boost), reflecting that during clearance, price and availability might matter more.&lt;/p&gt;

&lt;p&gt;By adjusting parameters in this way, you can reuse the same plugin for very different ranking behaviors. Enterprise architects can define a few parameter sets (perhaps stored in the application or a config file) for various situations (seasonal promotions, different markets, etc.), and developers can apply them as needed in queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Alexey Vidanov&lt;/strong&gt; - &lt;strong&gt;&lt;a href="https://github.com/vidanov/opensearch-script-plugin-hello-world" rel="noopener noreferrer"&gt;A simple “Hello World” script plugin for OpenSearch&lt;/a&gt;&lt;/strong&gt; Template in Github for the Amazon OpenSearch Service managed domain, written in Java. A great starting point if you want to learn how to create and integrate custom script plugins into your OpenSearch cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amitai Stern – “&lt;a href="https://logz.io/blog/opensearch-plugins/" rel="noopener noreferrer"&gt;Taking the Leap: My First Steps in OpenSearch Plugins&lt;/a&gt;” (Logz.io Blog)&lt;/strong&gt; – Introduction to building a simple OpenSearch REST plugin, with prerequisites like Java and Gradle and step-by-step examples of a “Hello World” plugin. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon AWS – “&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-opensearch-service-custom-plugins/" rel="noopener noreferrer"&gt;Amazon OpenSearch Service now supports Custom Plugins&lt;/a&gt;” (Nov 21, 2024)&lt;/strong&gt; – AWS announcement of custom plugin support in the managed service, including the motivation for custom plugins and the scope of supported plugin types. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch Project – “&lt;a href="https://opensearch.org/blog/plugins-intro/" rel="noopener noreferrer"&gt;https://opensearch.org/blog/plugins-intro/&lt;/a&gt;” (Dec 2, 2021)&lt;/strong&gt; – OpenSearch official blog post explaining the plugin architecture, how plugins are installed and loaded, and the role of the Security Manager and plugin policy files. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch Forum – “&lt;a href="https://forum.opensearch.org/t/set-up-communication-with-external-service-in-opensearch-plugin/24497" rel="noopener noreferrer"&gt;Set up communication with external service in OpenSearch plugin&lt;/a&gt;” (Discussion, May 2025)&lt;/strong&gt; – A community discussion highlighting the challenges of making external network calls from within a plugin (SecurityManager restrictions and potential workarounds). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/opensearch-project/opensearch-plugin-template-java" rel="noopener noreferrer"&gt;OpenSearch Plugin Template (GitHub)&lt;/a&gt;&lt;/strong&gt; – The official OpenSearch plugin template repository, useful as a starting point for new plugins. It contains the boilerplate code and files needed for a basic plugin project. &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>opensearch</category>
      <category>aws</category>
      <category>plugins</category>
    </item>
    <item>
      <title>Document Versioning in Amazon OpenSearch Service: OpenSearch as the Source of Truth. Part 3</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Fri, 11 Apr 2025 20:39:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/document-versioning-in-amazon-opensearch-service-opensearch-as-the-source-of-truth-part-3-144m</link>
      <guid>https://dev.to/aws-builders/document-versioning-in-amazon-opensearch-service-opensearch-as-the-source-of-truth-part-3-144m</guid>
      <description>&lt;p&gt;In our previous discussion, we emphasized using a primary database as the source of truth, with OpenSearch serving as a search layer. However, certain scenarios necessitate managing document versioning directly within OpenSearch. This article explores strategies for handling document versioning in OpenSearch.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Two-Indices Approach
&lt;/h1&gt;

&lt;p&gt;One effective method for managing document versioning involves using two separate indices:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Immutable Index:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Stores every document version as an immutable record, providing a complete audit trail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advantage:&lt;/strong&gt; Ensures that no version is overwritten, which is crucial for compliance and historical analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Search Interface Index:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Contains only the latest version of each document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advantage:&lt;/strong&gt; Optimized for fast retrieval and efficient queries, as it reduces the amount of data to search through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-Off:&lt;/strong&gt; While this dual-index method simplifies compliance and auditability, it significantly increases data storage and indexing operations. Maintaining two indices means higher ingestion costs, increased storage consumption, and more complex query execution, as both indices must remain synchronized.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Single-Index Approach for Versioned Documents in OpenSearch
&lt;/h1&gt;

&lt;p&gt;When handling immutable documents with versioning in OpenSearch, a key challenge is ensuring search results reflect only the latest document versions while preserving older content for historical reference. Instead of modifying indices or adding flags like &lt;code&gt;is_latest&lt;/code&gt;, we can achieve this with a single optimized query that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finds documents where the search term appears in either the latest (&lt;code&gt;searchableText&lt;/code&gt;) or previous versions (&lt;code&gt;oldVersionsText&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Excludes outdated documents where the term appears only in &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensures that only the latest document per &lt;code&gt;relationId&lt;/code&gt; is returned.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Index Structure and Data Handling
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Index Name:&lt;/strong&gt; &lt;code&gt;test_index&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stored Fields:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;relationId&lt;/code&gt; (keyword) – Groups multiple versions of a document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;searchableText&lt;/code&gt; (text) – Stores the most recent searchable content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oldVersionsText&lt;/code&gt; (text) – Stores previous versions of the content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update_time&lt;/code&gt; (date) – Timestamp of the document's last update.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Data is Managed:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Document Updates:&lt;/strong&gt; When a document is updated, a new version is inserted. The previous version’s content is moved to &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determining Latest Version:&lt;/strong&gt; The &lt;code&gt;update_time&lt;/code&gt; field is used to identify the most recent version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Consideration:&lt;/strong&gt; Storing older versions in every document increases the index size significantly. Over time, this can impact performance and storage costs. This method, while effective in some scenarios, introduces a multi-step query, which may become a performance bottleneck at scale.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why a Refined Query is Necessary
&lt;/h1&gt;

&lt;p&gt;If we only search in &lt;code&gt;searchableText&lt;/code&gt;, we may miss relevant results because the latest version might not contain the search term, while an older version does.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A document initially contains “OpenSearch performance optimization” in &lt;code&gt;searchableText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Later, the document is updated to “OpenSearch advanced techniques”, moving the previous text to &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A search for “performance optimization” would only find the outdated document unless we refine the query.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Optimized Query: How It Works
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Searches in &lt;code&gt;searchableText&lt;/code&gt; and &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensures that if the search term appears only in &lt;code&gt;oldVersionsText&lt;/code&gt;, the outdated document is excluded.&lt;/li&gt;
&lt;li&gt;Retrieves only the most recent version of each document.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step-by-Step Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Step 1: Create the Index
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT test_index
{
  "mappings": {
    "properties": {
      "relationId": {"type": "keyword"},
      "latestContent": {"type": "text"},
      "oldVersionsText": {"type": "text"},
      "update_time": {"type": "date"}
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Insert Sample Documents
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST test_index/_bulk
{"index": {"_id": "1"}}
{"relationId": "doc1", "latestContent": "OpenSearch advanced techniques", "oldVersionsText": ["OpenSearch performance optimization"], "update_time": "2025-03-12T12:00:00Z"}
{"index": {"_id": "2"}}
{"relationId": "doc2", "latestContent": "OpenSearch index tuning", "oldVersionsText": [], "update_time": "2025-03-12T13:00:00Z"}
{"index": {"_id": "3"}}
{"relationId": "doc1", "latestContent": "OpenSearch performance optimization", "oldVersionsText": [], "update_time": "2025-03-11T10:00:00Z"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Execute the Optimized Query
&lt;/h2&gt;

&lt;p&gt;This query ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The search term appears in &lt;code&gt;searchableText&lt;/code&gt; or &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Documents where the term appears only in &lt;code&gt;oldVersionsText&lt;/code&gt; are excluded.&lt;/li&gt;
&lt;li&gt;Only the latest document version per &lt;code&gt;relationId&lt;/code&gt; is returned.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET test_index/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "latestContent": "performance optimization" } },
        { "match": { "oldVersionsText": "performance optimization" } }
      ],
      "minimum_should_match": 1,
      "must_not": {
        "bool": {
          "must": [
            { "match": { "oldVersionsText": "performance optimization" } },
            { "bool": { "must_not": { "match": { "latestContent": "performance optimization" } } } }
          ]
        }
      }
    }
  },
  "sort": [{ "update_time": "desc" }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  How This Query Works
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dual-Field Coverage:&lt;/strong&gt; The &lt;code&gt;should&lt;/code&gt; clause ensures that a document is considered if it contains the term "performance optimization" in either the latest content (&lt;code&gt;latestContent&lt;/code&gt;) or in the older versions (&lt;code&gt;oldVersionsText&lt;/code&gt;). This guarantees that we capture any document that might be relevant regardless of which field holds the term.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exclusion of Outdated Matches:&lt;/strong&gt; The &lt;code&gt;must_not&lt;/code&gt; clause is crucial—it specifically excludes documents where the term appears &lt;strong&gt;only&lt;/strong&gt; in &lt;code&gt;oldVersionsText&lt;/code&gt;. This means that if a document's latest version does not contain the search term, even if an older version does, that document will not be returned. The inner structure checks for documents matching in &lt;code&gt;oldVersionsText&lt;/code&gt; but missing a match in &lt;code&gt;latestContent&lt;/code&gt;. Only those documents are filtered out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sorting by Update Time:&lt;/strong&gt; The &lt;code&gt;sort&lt;/code&gt; parameter orders the results by &lt;code&gt;update_time&lt;/code&gt; in descending order, ensuring that the most recent versions are prioritized.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The Key Points
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieves all relevant documents&lt;/strong&gt; — Ensures we don’t miss documents where the term appears in both &lt;code&gt;searchableText&lt;/code&gt; and &lt;code&gt;oldVersionsText&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevents returning outdated documents alone&lt;/strong&gt; — If the term appears only in an old version, we exclude it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No need for&lt;/strong&gt; &lt;code&gt;**is_latest**&lt;/code&gt; &lt;strong&gt;flags or index modifications&lt;/strong&gt; – Simplifies indexing by handling filtering at the query level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balances accuracy and efficiency&lt;/strong&gt; — Uses OpenSearch’s filtering capabilities without extra processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Considerations and Trade-Offs
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index Size Impact:&lt;/strong&gt; Storing previous versions in &lt;code&gt;oldVersionsText&lt;/code&gt;increases the index size over time. If document updates are frequent, this may require a cleanup strategy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Complexity:&lt;/strong&gt; This approach involves multiple steps in query execution (searching in both fields, filtering, and sorting), which could lead to performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; For high-update environments or large-scale deployments, consider periodic cleanup strategies or even alternative architectures (e.g., the two-indices approach) to maintain performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Managing document versioning directly within OpenSearch is inherently complex. While OpenSearch can serve as the source of truth for versioned documents, it isn’t the optimal standalone solution for all production environments. There’s no one-size-fits-all answer; as many experienced consultants say, “it depends.” By deeply understanding the trade-offs, you can select and tailor the approach that best fits your specific use case.&lt;/p&gt;

&lt;p&gt;This refined single-index strategy, leveraging the optimized query above, provides a powerful means to retrieve only the latest relevant document versions while still maintaining a comprehensive history of changes.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>versioning</category>
    </item>
    <item>
      <title>Document Versioning in OpenSearch: Database as the Source of Truth. Part 2</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Fri, 11 Apr 2025 20:37:19 +0000</pubDate>
      <link>https://dev.to/aws-builders/document-versioning-in-opensearch-database-as-the-source-of-truth-part-2-5a9p</link>
      <guid>https://dev.to/aws-builders/document-versioning-in-opensearch-database-as-the-source-of-truth-part-2-5a9p</guid>
      <description>&lt;h1&gt;
  
  
  &lt;em&gt;Best Approach: Database as the Source of Truth &amp;amp; OpenSearch as a Search Layer&lt;/em&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71q5oh8e2cz4i9v83udk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71q5oh8e2cz4i9v83udk.png" alt="img" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;A key consideration in this strategy is &lt;strong&gt;document versioning&lt;/strong&gt;. OpenSearch is not designed to maintain a history of document versions, and its handling of updates introduces important trade-offs. By leveraging a database for version control and OpenSearch for fast retrieval, applications can ensure both accuracy and performance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Separate the Search Layer from the Database?
&lt;/h1&gt;

&lt;p&gt;A database and OpenSearch serve different purposes, and using them correctly results in a more efficient system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data integrity and versioning&lt;/strong&gt;: A relational or NoSQL database ensures strict data consistency, transaction safety, and historical tracking. This is essential for applications where version control is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search performance&lt;/strong&gt;: OpenSearch optimizes full-text search and fast lookups but lacks strong consistency mechanisms and built-in version tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Keeping OpenSearch lightweight by only storing relevant indexed data makes scaling search clusters more manageable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backups and restoration&lt;/strong&gt;: Since OpenSearch is not the source of truth, it can be entirely recreated from the database without requiring complex backup strategies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How to Store and Organize Data Effectively
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Versioning and OpenSearch’s Update Model
&lt;/h2&gt;

&lt;p&gt;OpenSearch does not truly update documents in place. Instead, each update:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates a new document version.&lt;/li&gt;
&lt;li&gt;Updates the index reference.&lt;/li&gt;
&lt;li&gt;Deletes the older version asynchronously.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;This means:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The latest version is always accessible through indexing mechanisms.&lt;/li&gt;
&lt;li&gt;A slight delay in search availability is introduced, dependent on &lt;code&gt;refresh_interval&lt;/code&gt;, cluster performance, and index size.&lt;/li&gt;
&lt;li&gt;Storing multiple versions inside OpenSearch leads to unnecessary storage overhead and increased indexing complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices for Versioning and Indexing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store &lt;strong&gt;only the latest version&lt;/strong&gt; of a document in OpenSearch.&lt;/li&gt;
&lt;li&gt;Keep a &lt;strong&gt;full version history&lt;/strong&gt; in the database to ensure traceability and compliance.&lt;/li&gt;
&lt;li&gt;For real-time accuracy, use backend logic to verify OpenSearch results against the database before presenting data to the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: Using DynamoDB and a Lambda Indexer
&lt;/h2&gt;

&lt;p&gt;A common approach for handling versioning and indexing efficiently is using &lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; as the primary database and an &lt;strong&gt;AWS Lambda function&lt;/strong&gt; to update OpenSearch asynchronously.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB as the Source of Truth&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Stores all document versions, maintaining full historical records.&lt;/li&gt;
&lt;li&gt;Uses DynamoDB Streams to capture item modifications in real time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Lambda Indexer for OpenSearch&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function is triggered by DynamoDB Streams whenever an item is modified.&lt;/li&gt;
&lt;li&gt;The function extracts the latest version and updates OpenSearch via the OpenSearch API.&lt;/li&gt;
&lt;li&gt;Ensures OpenSearch only contains the most recent document, preventing unnecessary versioning overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Handling Deletes and Expired Versions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Lambda function removes outdated versions from OpenSearch while retaining historical versions in DynamoDB.&lt;/li&gt;
&lt;li&gt;Ensures efficient query performance without cluttering OpenSearch with redundant versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Code for a Lambda Indexer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth

# Configuration: update these with your details.
region = 'your-region'  # e.g., 'us-east-1'
host = 'your-opensearch-domain'  # e.g., 'search-mydomain.us-east-1.es.amazonaws.com'
index_name = 'your-index-name'

# Set up AWS authentication for SigV4 signing.
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(
    credentials.access_key,
    credentials.secret_key,
    region,
    'es',
    session_token=credentials.token
)

# Initialize the OpenSearch client.
client = OpenSearch(
    hosts=[{'host': host, 'port': 443}],
    http_auth=awsauth,
    use_ssl=True,
    verify_certs=True,
    connection_class=RequestsHttpConnection
)

def lambda_handler(event, context):
    for record in event["Records"]:
        if record["eventName"] in ["INSERT", "MODIFY"]:
            document = record["dynamodb"]["NewImage"]
            doc_id = document["id"]["S"]
            data = {
                "id": doc_id,
                "title": document["title"]["S"],
                "content": document["content"]["S"],
                "timestamp": document["timestamp"]["S"]
            }
            response = client.index(index=index_name, id=doc_id, body=data)
            print("Updated document:", response)
        elif record["eventName"] == "REMOVE":
            doc_id = record["dynamodb"]["Keys"]["id"]["S"]
            response = client.delete(index=index_name, id=doc_id)
            print("Deleted document:", response)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Handling Real-Time Accuracy
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;OpenSearch’s eventual consistency model means changes are not immediately available for search.&lt;/li&gt;
&lt;li&gt;If exact real-time accuracy is required, consider implementing backend logic that cross-checks OpenSearch results against the database.&lt;/li&gt;
&lt;li&gt;The trade-off is complexity versus performance: OpenSearch provides ultra-fast queries, but perfect real-time accuracy requires extra processing steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Example Scenarios for Reducing Update Frequency
&lt;/h1&gt;

&lt;p&gt;Reducing the number of updates to OpenSearch can significantly improve performance. Here are some real-world strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shop Inventory Search:&lt;/strong&gt; Instead of storing the exact number of available products in OpenSearch, categorize availability into broader ranges like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Out of Stock”&lt;/li&gt;
&lt;li&gt;“Limited Stock”&lt;/li&gt;
&lt;li&gt;“Moderate Stock”&lt;/li&gt;
&lt;li&gt;“Plentiful”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces the frequency of updates and indexing workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Pricing Optimization:&lt;/strong&gt; Instead of storing the exact price of each item, group prices into predefined buckets that allow efficient filtering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;50&lt;/code&gt; → Represents prices between &lt;code&gt;0-5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;100&lt;/code&gt; → Represents prices between &lt;code&gt;50-100&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;200&lt;/code&gt; → Represents prices between &lt;code&gt;100-200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;500&lt;/code&gt; → Represents prices between &lt;code&gt;200-500&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method significantly reduces indexing load while maintaining the ability to perform efficient range-based searches in OpenSearch. Filtering documents based on these predefined price groups is computationally inexpensive and does not require constant reindexing when prices fluctuate.&lt;/p&gt;

&lt;h1&gt;
  
  
  Example: OpenSearch Index Mapping and Data Storage
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Index Mapping:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mappings": {
    "properties": {
      "id": { "type": "keyword" },
      "title": { "type": "text" },
      "content": { "type": "text" },
      "timestamp": { "type": "date" },
      "stock_level": { "type": "keyword" },
      "price_range": { "type": "integer" }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Storing a Document:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id": "12345",
  "title": "High-Performance Laptop",
  "content": "A powerful laptop with 16GB RAM and 512GB SSD.",
  "timestamp": "2024-03-17T12:00:00Z",
  "stock_level": "Moderate Stock",
  "price_range": 200
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of This Approach
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimizes Indexing Overhead:&lt;/strong&gt; Price changes do not require frequent document updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Filtering:&lt;/strong&gt; OpenSearch can efficiently retrieve documents based on predefined price ranges without additional computation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Suitable for large datasets with frequently changing prices and inventory levels.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Structuring Data for Performance and Scalability
&lt;/h1&gt;

&lt;p&gt;OpenSearch benefits from a &lt;strong&gt;flat, denormalized structure&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid deeply nested objects that require complex queries.&lt;/li&gt;
&lt;li&gt;Eliminate the need for multiple joins across indices by storing relevant information in a single index document.&lt;/li&gt;
&lt;li&gt;Keeping data denormalized reduces indexing complexity and improves search performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Backup and Restoration Strategies
&lt;/h1&gt;

&lt;p&gt;A key advantage of this approach is that OpenSearch can be &lt;strong&gt;entirely recreated from the database&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If an OpenSearch cluster is lost, documents can be reindexed from the database without risk of data loss.&lt;/li&gt;
&lt;li&gt;This minimizes the need for frequent OpenSearch snapshots, simplifying disaster recovery and reducing operational costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Key Benefits of This Approach
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Data Consistency:&lt;/strong&gt; The database remains the single source of truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Performance:&lt;/strong&gt; OpenSearch is leaner, avoiding unnecessary writes and updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; OpenSearch clusters remain manageable as they only store relevant indexed data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Maintenance:&lt;/strong&gt; Easier disaster recovery since OpenSearch can be rebuilt from the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Version Control:&lt;/strong&gt; The database maintains a full history of document versions, while OpenSearch serves only the latest, reducing storage bloat and complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method is strongly recommended for applications that demand precise version control and rapid search functionality.&lt;/p&gt;

&lt;p&gt;The subsequent sections explore alternative strategies where OpenSearch itself must manage document versioning.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>versioning</category>
    </item>
    <item>
      <title>Understanding Document Versioning in OpenSearch. Part 1</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Fri, 11 Apr 2025 20:35:37 +0000</pubDate>
      <link>https://dev.to/aws-builders/understanding-document-versioning-in-opensearch-part-1-co8</link>
      <guid>https://dev.to/aws-builders/understanding-document-versioning-in-opensearch-part-1-co8</guid>
      <description>&lt;h1&gt;
  
  
  What is Document Versioning?
&lt;/h1&gt;

&lt;p&gt;Document versioning refers to the practice of tracking and managing multiple versions of a document over time. In many applications, document changes need to be recorded rather than overwritten, ensuring historical integrity and compliance with regulations. Versioning is critical in industries such as finance, healthcare, legal, and content management, where keeping an accurate record of past document states is essential for audits, accountability, and compliance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Understanding Versioning and Immutable Storage
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Versioning Approaches in Traditional Systems
&lt;/h1&gt;

&lt;p&gt;Traditional databases and content management systems typically handle versioning through methods such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Row-based historical tracking&lt;/strong&gt;: Storing each document version with timestamps and unique identifiers in database tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event sourcing&lt;/strong&gt;: Capturing all changes as immutable events in an append-only log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot and delta storage&lt;/strong&gt;: Storing periodic full snapshots with incremental changes recorded between versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Amazon OpenSearch Service does not natively support these traditional versioning mechanisms.&lt;/p&gt;

&lt;h1&gt;
  
  
  Immutable Storage and Compliance in OpenSearch
&lt;/h1&gt;

&lt;p&gt;Immutable document storage, where records cannot be modified or deleted, is essential for compliance with regulations such as GDPR, HIPAA, and SOC 2. Immutable storage ensures data integrity, auditability, and tamper resistance, particularly important for regulated sectors like healthcare and finance.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSearch Compliance Capabilities on AWS
&lt;/h2&gt;

&lt;p&gt;Amazon OpenSearch Service offers multiple compliance-supporting features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Grained Access Control&lt;/strong&gt;: Role-Based Access Control (RBAC) and AWS IAM integration restrict modifications to authorized users only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Logging&lt;/strong&gt;: Built-in audit logs integrated with AWS CloudTrail provide comprehensive records for audit purposes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Snapshots&lt;/strong&gt;: Automated incremental backups allow immutable point-in-time data recovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance Certifications&lt;/strong&gt;: Officially audited and certified compliance with standards including HIPAA, SOC 2, ISO, and FedRAMP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applications leveraging OpenSearch typically adopt a “write-once” approach, preserving historical records unmodified after creation.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS Services Complementing OpenSearch for Versioning and Compliance
&lt;/h1&gt;

&lt;p&gt;AWS provides integrated solutions that complement OpenSearch by enhancing document versioning, data retention, and compliance:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3 with Versioning&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores each version of documents, ensuring robust data integrity.&lt;/li&gt;
&lt;li&gt;Provides lifecycle policies for automatic management of document versions.&lt;/li&gt;
&lt;li&gt;Integrates with AWS Backup for efficient archival and retention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Amazon DynamoDB for Immutable Data Storage&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintains historical records with strong consistency through timestamped entries.&lt;/li&gt;
&lt;li&gt;Serves as an authoritative source of truth, seamlessly integrated with OpenSearch for indexing and retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS Backup and AWS Audit Manager&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automates comprehensive backups, supporting compliance with regulations such as GDPR, HIPAA, and SOC 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining OpenSearch or Amazon OpenSearch Service with AWS managed storage, security, and compliance services, organizations achieve secure, compliant, and efficient document versioning and storage.&lt;/p&gt;

&lt;h1&gt;
  
  
  OpenSearch as a Search Layer, Not a Versioning System
&lt;/h1&gt;

&lt;p&gt;OpenSearch is designed primarily as a powerful distributed search and analytics engine rather than a document version control system. It includes a built-in &lt;code&gt;_version&lt;/code&gt; field intended primarily for optimistic concurrency control. This built-in versioning mechanism increments a version number upon each document update but does not retain historical document states.&lt;/p&gt;

&lt;p&gt;Applications needing comprehensive audit trails, compliance tracking, or historical data retrieval must implement custom versioning approaches. The recommended best practice is to utilize OpenSearch strictly as a high-performance search and analytics layer while maintaining the authoritative historical data in a separate, persistent database designed specifically for version control.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why OpenSearch is Not Optimized for Versioning
&lt;/h1&gt;

&lt;p&gt;Unlike traditional databases, OpenSearch follows a distributed architecture that makes version tracking challenging:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Eventual Consistency:&lt;/strong&gt; Updates are indexed asynchronously, meaning that documents may not appear updated in search results immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sharding Complexity:&lt;/strong&gt; Data is split across multiple shards, making atomic updates and transactions difficult to implement at scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized for Read Performance:&lt;/strong&gt; OpenSearch is built for fast, scalable search operations, not transactional integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Native Version History:&lt;/strong&gt; The &lt;code&gt;_version&lt;/code&gt; field only tracks the latest version, with no capability to retrieve past document states.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, if an application tracks legal contracts or medical records, simply relying on OpenSearch’s &lt;code&gt;_version&lt;/code&gt; field would &lt;strong&gt;not&lt;/strong&gt; provide a verifiable audit history—previous versions would be irreversibly lost.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using the Cloud for Compliance and Versioning
&lt;/h1&gt;

&lt;p&gt;For organizations that need &lt;strong&gt;regulatory compliance, security, and versioning best practices&lt;/strong&gt;, leveraging cloud services is the most effective approach. Cloud providers like AWS offer managed solutions that help achieve compliance while maintaining performance and scalability.&lt;/p&gt;

&lt;h1&gt;
  
  
  Understanding OpenSearch’s &lt;code&gt;_version&lt;/code&gt; Field
&lt;/h1&gt;

&lt;p&gt;While OpenSearch assigns each document a &lt;code&gt;_version&lt;/code&gt;, this does not function like traditional version control systems such as Git or database transaction logs. Instead, &lt;code&gt;_version&lt;/code&gt; is used to &lt;strong&gt;prevent conflicts&lt;/strong&gt; when multiple clients attempt to update the same document.&lt;/p&gt;

&lt;h1&gt;
  
  
  How &lt;code&gt;_version&lt;/code&gt; Works
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;A document is indexed for the first time → &lt;code&gt;_version = 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A client updates the document → &lt;code&gt;_version&lt;/code&gt; increments (&lt;code&gt;_version = 2&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Another update occurs → &lt;code&gt;_version = 3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;However, &lt;strong&gt;previous versions are overwritten&lt;/strong&gt;, not stored.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If two clients try to update the same document simultaneously, OpenSearch can reject changes that do not match the expected &lt;code&gt;_version&lt;/code&gt;. This ensures &lt;strong&gt;concurrent updates do not overwrite each other&lt;/strong&gt;, but &lt;strong&gt;it does not provide a way to retrieve historical versions&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Example: Updating a Document
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT /my_index/_doc/1
{
  "title": "First Version",
  "content": "This is the first version of the document."
}

PUT /my_index/_doc/1
{
  "title": "Updated Version",
  "content": "This is the updated version of the document."
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the second &lt;code&gt;PUT&lt;/code&gt; request, the original version is &lt;strong&gt;completely replaced&lt;/strong&gt;. The &lt;code&gt;_version&lt;/code&gt; number increases, but the old data is lost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if you try to retrieve version 1?&lt;/strong&gt; Unlike databases that store historical states, OpenSearch only retains the latest version. Querying for an old version (e.g., &lt;code&gt;GET /my_index/_doc/1?version=1&lt;/code&gt;) will not work—only the most recent document is available.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion: Key Takeaways from Document Versioning in OpenSearch
&lt;/h1&gt;

&lt;p&gt;In this article, we explored the challenges and solutions for &lt;strong&gt;document versioning in OpenSearch&lt;/strong&gt;. Key takeaways include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenSearch &lt;strong&gt;is not a versioning system&lt;/strong&gt;; it is optimized for search, not for maintaining historical records.&lt;/li&gt;
&lt;li&gt;The built-in &lt;code&gt;_version&lt;/code&gt; field is only for &lt;strong&gt;optimistic concurrency control&lt;/strong&gt; and does not store historical versions.&lt;/li&gt;
&lt;li&gt;Applications requiring &lt;strong&gt;audit trails, compliance, and historical tracking&lt;/strong&gt;should maintain a &lt;strong&gt;separate source of truth&lt;/strong&gt;, such as a database or object storage.&lt;/li&gt;
&lt;li&gt;AWS provides robust tools for compliance, including &lt;strong&gt;Amazon S3 with versioning, DynamoDB, OpenSearch Service with IAM control, and AWS Backup&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The best strategy for OpenSearch versioning depends on the use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this foundation, we are now ready to explore &lt;strong&gt;specific strategies for managing document versions&lt;/strong&gt; in OpenSearch. Stay tuned for the next article: &lt;em&gt;**Using a Database as the Source of Truth: Best Practices for OpenSearch Integration.*&lt;/em&gt;*&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>versioning</category>
    </item>
    <item>
      <title>API Gateway and Lambda Throttling with Terraform. Part 2</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Mon, 14 Oct 2024 12:26:26 +0000</pubDate>
      <link>https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-part-2-3i84</link>
      <guid>https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-part-2-3i84</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-a-comprehensive-guide-6d0"&gt;the previous post&lt;/a&gt; we covered the basics of setting up throttling for your API Gateway and Lambda functions. In this follow-up, we’ll take it to the next level, adding budget controls, time-based throttling adjustments, and AWS WAF security integration to safeguard your API while optimizing both performance and cost-efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Budget and Billing Alerts
&lt;/h3&gt;

&lt;p&gt;Managing cloud expenses is essential to avoiding surprises, especially when usage spikes unexpectedly. AWS Budgets allows you to set up monthly spending limits and receive alerts before overspending occurs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Setting up Budget Alerts
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_budgets_budget"&lt;/span&gt; &lt;span class="s2"&gt;"monthly_budget"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"monthly-budget-${var.environment}"&lt;/span&gt;
  &lt;span class="nx"&gt;budget_type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"COST"&lt;/span&gt;
  &lt;span class="nx"&gt;time_unit&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MONTHLY"&lt;/span&gt;
  &lt;span class="nx"&gt;limit_amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1000"&lt;/span&gt;
  &lt;span class="nx"&gt;limit_unit&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"USD"&lt;/span&gt;

  &lt;span class="nx"&gt;notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;comparison_operator&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GREATER_THAN"&lt;/span&gt;
    &lt;span class="nx"&gt;threshold&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;threshold_type&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PERCENTAGE"&lt;/span&gt;
    &lt;span class="nx"&gt;notification_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FORECASTED"&lt;/span&gt;
    &lt;span class="nx"&gt;subscriber_email_addresses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"alert@example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;comparison_operator&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GREATER_THAN"&lt;/span&gt;
    &lt;span class="nx"&gt;threshold&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="nx"&gt;threshold_type&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PERCENTAGE"&lt;/span&gt;
    &lt;span class="nx"&gt;notification_type&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ACTUAL"&lt;/span&gt;
    &lt;span class="nx"&gt;subscriber_email_addresses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"alert@example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also configure alerts to trigger responses in other AWS services, such as &lt;strong&gt;SNS topics&lt;/strong&gt; or &lt;strong&gt;CloudWatch Alarms&lt;/strong&gt;, to better manage your Lambda functions during traffic spikes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# SNS Topic for Alerts&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sns_topic"&lt;/span&gt; &lt;span class="s2"&gt;"budget_alerts_topic"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"budget-alerts-topic-${var.environment}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Subscribe an email address to the SNS topic&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sns_topic_subscription"&lt;/span&gt; &lt;span class="s2"&gt;"email_subscription"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;topic_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alarms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"email"&lt;/span&gt;
  &lt;span class="nx"&gt;endpoint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alarms_email&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Example CloudWatch Alarm for Lambda Throttling (to control costs)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_metric_alarm"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_throttles"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-throttles-${aws_lambda_function.example.function_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GreaterThanThreshold"&lt;/span&gt;
  &lt;span class="nx"&gt;evaluation_periods&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;metric_name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Throttles"&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;
  &lt;span class="nx"&gt;period&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;statistic&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Alert for Lambda throttling due to budget constraints"&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_actions&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;budget_alerts_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;dimensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;FunctionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Time-Based Throttling Adjustments
&lt;/h3&gt;

&lt;p&gt;For high-traffic applications, one-size-fits-all throttling can be inefficient. You can dynamically adjust API Gateway throttling limits during peak and off-peak hours using &lt;strong&gt;AWS EventBridge&lt;/strong&gt; and &lt;strong&gt;Lambda&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Time-Based Throttling with Lambda and EventBridge
&lt;/h4&gt;

&lt;p&gt;Below is the Python code that dynamically adjusts API Gateway throttling limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;api_gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apigateway&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;api_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_ID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;STAGE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;burst_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BURST_LIMIT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;rate_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RATE_LIMIT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;# Update method settings with new throttling limits
&lt;/span&gt;    &lt;span class="n"&gt;api_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;restApiId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;api_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;stageName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;patchOperations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/*/*/throttling/burstLimit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;burst_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/*/*/throttling/rateLimit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate_limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Throttling updated: Burst=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;burst_limit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Rate=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;rate_limit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Lambda Creation in Terraform
&lt;/h4&gt;

&lt;p&gt;To automate the creation of the Lambda function for adjusting throttling limits, use the following Terraform configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"api_throttling_configs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
    &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;peak&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
      &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;off_peak&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
      &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_peak"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_peak_${var.environment}"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.11"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_exec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_peak.zip"&lt;/span&gt;

  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;API_ID&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="nx"&gt;STAGE&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage_name&lt;/span&gt;
      &lt;span class="nx"&gt;BURST_LIMIT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;burst_limit&lt;/span&gt;
      &lt;span class="nx"&gt;RATE_LIMIT&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_limit&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_off_peak"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_off_peak_${var.environment}"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.11"&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_exec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_off_peak.zip"&lt;/span&gt;

  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;API_ID&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="nx"&gt;STAGE&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage_name&lt;/span&gt;
      &lt;span class="nx"&gt;BURST_LIMIT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;off_peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;burst_limit&lt;/span&gt;
      &lt;span class="nx"&gt;RATE_LIMIT&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;off_peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_limit&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a more understandable version:&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating Time-Based Adjustments
&lt;/h3&gt;

&lt;p&gt;To automatically adjust settings based on the time of day (for example, peak hours vs. off-peak hours), you can use &lt;strong&gt;EventBridge Rules&lt;/strong&gt; to trigger Lambda functions at specific times. This way, your system can automatically adjust to handle more traffic during peak times and reduce capacity during off-peak hours.&lt;/p&gt;

&lt;p&gt;In this example, we set up rules that run at 8:00 AM (peak hours) and 8:00 PM (off-peak hours). Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Peak Hours Rule&lt;/strong&gt;: This triggers at 8:00 AM to increase capacity during busy times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Off-Peak Hours Rule&lt;/strong&gt;: This triggers at 8:00 PM to reduce capacity during slower times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define a rule for peak hours (8:00 AM)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_rule"&lt;/span&gt; &lt;span class="s2"&gt;"peak_hours_rule"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"peak-hours-rule"&lt;/span&gt;
  &lt;span class="nx"&gt;schedule_expression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cron(0 8 * * ? *)"&lt;/span&gt;  &lt;span class="c1"&gt;# Adjust to your peak hours&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Define a rule for off-peak hours (8:00 PM)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_rule"&lt;/span&gt; &lt;span class="s2"&gt;"off_peak_hours_rule"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"off-peak-hours-rule"&lt;/span&gt;
  &lt;span class="nx"&gt;schedule_expression&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cron(0 20 * * ? *)"&lt;/span&gt;  &lt;span class="c1"&gt;# Adjust to your off-peak hours&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Set the target for peak hours (a Lambda function to handle peak traffic)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_target"&lt;/span&gt; &lt;span class="s2"&gt;"peak_hours_target"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rule&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_event_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;peak_hours_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;target_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_peak"&lt;/span&gt;
  &lt;span class="nx"&gt;arn&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adjust_throttling_peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Set the target for off-peak hours (a Lambda function to handle lower traffic)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_event_target"&lt;/span&gt; &lt;span class="s2"&gt;"off_peak_hours_target"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rule&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudwatch_event_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;off_peak_hours_rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;target_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"adjust_throttling_off_peak"&lt;/span&gt;
  &lt;span class="nx"&gt;arn&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adjust_throttling_off_peak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup automatically changes your system’s capacity based on time. At 8:00 AM, the system prepares for heavy traffic, and at 8:00 PM, it scales down for lighter usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Adding AWS WAF for Extra Protection
&lt;/h3&gt;

&lt;p&gt;Integrating &lt;strong&gt;AWS WAF&lt;/strong&gt; (Web Application Firewall) into your API Gateway helps protect it from malicious traffic, such as bots or potential DDoS (Distributed Denial of Service) attacks. It acts as a security layer that filters out harmful requests before they reach your API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Blocking IPs and Controlling Bots with WAF
&lt;/h4&gt;

&lt;p&gt;This example shows how to set up a WAF rule that protects your API Gateway by blocking bot traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_wafv2_web_acl"&lt;/span&gt; &lt;span class="s2"&gt;"api_gateway_acl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"api-gateway-waf-acl"&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"REGIONAL"&lt;/span&gt;  &lt;span class="c1"&gt;# Use "REGIONAL" to protect your regional API Gateway&lt;/span&gt;

  &lt;span class="c1"&gt;# Default action to allow all requests unless a rule blocks them&lt;/span&gt;
  &lt;span class="nx"&gt;default_action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Enable visibility so you can monitor activity using CloudWatch metrics&lt;/span&gt;
  &lt;span class="nx"&gt;visibility_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cloudwatch_metrics_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;metric_name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"apiGatewayAcl"&lt;/span&gt;
    &lt;span class="nx"&gt;sampled_requests_enabled&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Bot Protection Rule to block bad bot traffic&lt;/span&gt;
  &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"BotProtectionRule"&lt;/span&gt;  &lt;span class="c1"&gt;# Name of the rule&lt;/span&gt;
    &lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="c1"&gt;# Order of execution for this rule (lower number = higher priority)&lt;/span&gt;

    &lt;span class="c1"&gt;# No override action means the default allow/block settings apply&lt;/span&gt;
    &lt;span class="nx"&gt;override_action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;none&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Managed rule group to use AWS's pre-built bot control rules&lt;/span&gt;
    &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;managed_rule_group_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWSManagedRulesBotControlRuleSet"&lt;/span&gt;
        &lt;span class="nx"&gt;vendor_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;  &lt;span class="c1"&gt;# AWS provides these bot protection rules&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Enable visibility for this specific rule as well&lt;/span&gt;
    &lt;span class="nx"&gt;visibility_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cloudwatch_metrics_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;metric_name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"botProtection"&lt;/span&gt;
      &lt;span class="nx"&gt;sampled_requests_enabled&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup adds protection to your API by detecting and blocking bot traffic, while also giving you the ability to monitor suspicious activity via CloudWatch metrics. &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By incorporating &lt;strong&gt;Budget Alerts&lt;/strong&gt;, &lt;strong&gt;Time-Based Throttling Adjustments&lt;/strong&gt;, and &lt;strong&gt;WAF Integration&lt;/strong&gt;, you're ensuring that your API is not only efficient and cost-effective but also secure. These advanced practices complement each other, providing a well-rounded solution for managing traffic, costs, and security in a scalable AWS environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitoring and Fine-Tuning&lt;/strong&gt;: After deploying these configurations, it's crucial to monitor their performance. Use CloudWatch metrics and logs to verify that throttling adjustments and security rules are working as expected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Testing&lt;/strong&gt;: Perform load testing during both peak and off-peak hours to ensure your dynamic throttling setup aligns with your traffic patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Further Optimization&lt;/strong&gt;: Explore other AWS services like &lt;strong&gt;AWS Shield&lt;/strong&gt; for advanced DDoS protection and &lt;strong&gt;AWS X-Ray&lt;/strong&gt; for tracing API Gateway requests to identify potential bottlenecks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By continually monitoring and adjusting, you’ll be able to maintain an API infrastructure that scales with traffic, optimizes costs, and maintains high levels of security.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>apigateway</category>
      <category>waf</category>
    </item>
    <item>
      <title>Amazon API Gateway and AWS Lambda Throttling with Terraform. Part 1</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Fri, 11 Oct 2024 09:53:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-a-comprehensive-guide-6d0</link>
      <guid>https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-a-comprehensive-guide-6d0</guid>
      <description>&lt;p&gt;In today's cloud-native world, effectively managing API and serverless function performance is crucial for building reliable and cost-effective applications. This guide explores advanced throttling techniques for AWS API Gateway and Lambda using Terraform, incorporating best practices from the AWS Well-Architected Framework and real-world implementation patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Throttling Matters
&lt;/h2&gt;

&lt;p&gt;When building cloud-native applications, it's easy to focus on the business logic and forget key infrastructure components such as throttling and usage limits. However, this oversight can lead to unpredictable performance, increased costs, and even service outages. Setting the right throttling limits ensures that your applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adapt to varying load patterns and sudden traffic spikes&lt;/li&gt;
&lt;li&gt;Protect backend services from overload and cascading failures&lt;/li&gt;
&lt;li&gt;Optimize costs across different environments while maintaining service quality&lt;/li&gt;
&lt;li&gt;Provide meaningful monitoring and alerting for proactive management&lt;/li&gt;
&lt;li&gt;Support different user tiers and business requirements effectively&lt;/li&gt;
&lt;li&gt;Enable graceful degradation during high-load scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real-world projects, teams often realize the importance of throttling limits only after encountering performance or cost issues. It can be challenging to set proper limits without historical data, but this guide provides you with the framework to get those metrics in place, simplifying budgeting and operational planning. Furthermore, proper throttling configuration serves as a critical defense mechanism against denial-of-service attacks, whether intentional or accidental.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Overview
&lt;/h2&gt;

&lt;p&gt;Let's dive into a comprehensive throttling implementation that addresses these needs using Terraform. We'll build a solution that's both flexible and production-ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dynamic Configuration Management
&lt;/h3&gt;

&lt;p&gt;One of the key challenges in multi-environment setups is managing environment-specific configurations. In the example below, we set up environment-specific throttling limits for API Gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name (e.g., dev, staging, prod)"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"api_throttling_configs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
    &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
      &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;prod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
      &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows you to manage different limits for development and production environments. For example, the dev environment has lower limits, ensuring that your resources are not overwhelmed during testing, while prod has higher limits to handle real-world traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lambda Configuration
&lt;/h3&gt;

&lt;p&gt;AWS Lambda has a default maximum concurrency limit of 1,000 concurrent executions per account per region. You can control concurrency at the function level to avoid unplanned spikes and protect backend services. Here's how to set concurrency limits for Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my_lambda_function_${var.environment}"&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// other lambda function configurations&lt;/span&gt;
  &lt;span class="nx"&gt;reserved_concurrent_executions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_concurrency_limits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;reserved_concurrent_executions&lt;/code&gt;, we control how many instances of the Lambda function can run simultaneously, protecting backend services from excessive traffic. The default value in this example is 100 concurrent executions, but this can be adjusted depending on the environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Real-World Observation
&lt;/h4&gt;

&lt;p&gt;A common oversight in development is forgetting to configure these limits early on. Teams often realize the importance of limiting Lambda concurrency when they see unexpected bills or performance degradation. This configuration helps prevent such scenarios by establishing clear boundaries from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Comprehensive Monitoring
&lt;/h3&gt;

&lt;p&gt;Monitoring is critical for ensuring that throttling is functioning as expected. Use Amazon CloudWatch alarms to proactively track throttling metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_metric_alarm"&lt;/span&gt; &lt;span class="s2"&gt;"lambda_throttles"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda-throttles-${aws_lambda_function.example.function_name}"&lt;/span&gt;
  &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GreaterThanThreshold"&lt;/span&gt;
  &lt;span class="nx"&gt;evaluation_periods&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;metric_name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Throttles"&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;
  &lt;span class="nx"&gt;period&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;statistic&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_description&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Lambda function throttling detected"&lt;/span&gt;
  &lt;span class="nx"&gt;alarm_actions&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;dimensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;FunctionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Amazon API Gateway Configuration
&lt;/h3&gt;

&lt;p&gt;Amazon API Gateway can experience heavy loads during peak traffic times. To prevent backend services from being overwhelmed, we use throttling limits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_method_settings"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rest_api_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;stage_name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage_name&lt;/span&gt;
  &lt;span class="nx"&gt;method_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*/*"&lt;/span&gt;

  &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;metrics_enabled&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;logging_level&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"INFO"&lt;/span&gt;
    &lt;span class="nx"&gt;data_trace_enabled&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;throttling_burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"burst_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;throttling_rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"rate_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Monitoring with Amazon CloudWatch Dashboards
&lt;/h3&gt;

&lt;p&gt;Once the throttling is configured, it's important to visualize the data to make informed decisions. Amazon CloudWatch dashboards offer a great way to monitor both Amazon API Gateway and AWS Lambda throttling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_dashboard"&lt;/span&gt; &lt;span class="s2"&gt;"throttling_monitoring"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;dashboard_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"throttling-monitoring-${var.environment}"&lt;/span&gt;

  &lt;span class="nx"&gt;dashboard_body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;widgets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"metric"&lt;/span&gt;
        &lt;span class="nx"&gt;width&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
        &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
        &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"AWS/ApiGateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ThrottleCount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ApiName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"AWS/Lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Throttles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"FunctionName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
          &lt;span class="nx"&gt;stat&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sum"&lt;/span&gt;
          &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;
          &lt;span class="nx"&gt;title&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Throttling Overview"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Cost Management with Usage Plans
&lt;/h3&gt;

&lt;p&gt;Amazon API Gateway usage plans are a practical way to control costs and ensure that API consumers do not abuse the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_api_gateway_usage_plan"&lt;/span&gt; &lt;span class="s2"&gt;"tiered"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tiered-usage-plan-${var.environment}"&lt;/span&gt;

  &lt;span class="nx"&gt;api_stages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_rest_api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;stage&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_api_gateway_stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage_name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;quota_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
    &lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MONTH"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;throttle_settings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;burst_limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"burst_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;rate_limit&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_throttling_configs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"rate_limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;CostCenter&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API-Gateway"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without proper throttling in place, API Gateway costs can spiral out of control. Budgeting becomes much easier when you set usage plans early and monitor actual consumption via dashboards.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Budget and Billing Alerts
&lt;/h3&gt;

&lt;p&gt;An important aspect of managing API Gateway and Lambda throttling is cost control. By setting up budget and billing alerts, you can monitor and track usage costs to avoid unexpected charges. Here’s how you can approach it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS Budgets&lt;/strong&gt;: Set a monthly budget for API Gateway and Lambda usage, and configure notifications to alert you when costs exceed a certain threshold. This allows proactive management of expenses and ensures that your application remains cost-efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost Anomaly Detection&lt;/strong&gt;: Enable AWS Cost Anomaly Detection to spot unusual usage patterns that may indicate misconfigurations or unexpected traffic spikes, helping you address cost-related issues promptly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These measures, combined with your throttling configurations, provide a robust approach to managing both application performance and cost efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and Validation
&lt;/h2&gt;

&lt;p&gt;Testing your throttling configurations ensures reliability in production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Load Testing&lt;/strong&gt;: Simulate high traffic to verify the throttling limits are being respected, including edge cases and boundary conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scenario Testing&lt;/strong&gt;: Test burst traffic and sustained load to validate both limits and system resilience, particularly focusing on recovery patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring Validation&lt;/strong&gt;: Ensure your CloudWatch alarms are firing during test scenarios and verify the accuracy of metrics collection.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Best Practices for Production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Regular Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Continuously monitor usage trends and adjust throttling settings as your traffic patterns evolve&lt;/li&gt;
&lt;li&gt;Periodically review cost implications of your throttling configurations&lt;/li&gt;
&lt;li&gt;Analyze throttling patterns to identify potential optimization opportunities&lt;/li&gt;
&lt;li&gt;Consider seasonal variations in traffic when setting limits&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maintain detailed runbooks for handling throttling-related incidents&lt;/li&gt;
&lt;li&gt;Document any configuration changes, including justifications, in a version-controlled manner&lt;/li&gt;
&lt;li&gt;Keep a historical record of throttling adjustments and their impacts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Compliance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Perform regular audits of your throttling configurations to ensure they meet compliance and security standards&lt;/li&gt;
&lt;li&gt;Document throttling decisions as part of your compliance framework&lt;/li&gt;
&lt;li&gt;Ensure throttling mechanisms align with SLA commitments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further Considerations and Possible Ways to Go
&lt;/h2&gt;

&lt;p&gt;As you refine your throttling strategy, here are some additional techniques you can consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Budget and Billing Alerts&lt;/strong&gt;: Set up budget limits and enable cost anomaly detection to avoid unexpected charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Based Throttling Adjustments&lt;/strong&gt;: Use AWS EventBridge to adjust throttling limits during peak hours vs. off-hours to optimize resource allocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAF Integration&lt;/strong&gt;: Add an extra layer of security by integrating AWS WAF for IP-based throttling, blocking suspicious IP addresses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Validation&lt;/strong&gt;: Ensure that API requests conform to expected formats to reduce the chances of invalid requests causing backend overload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead Letter Queues (DLQs)&lt;/strong&gt;: Ensure that throttled requests are not lost by sending them to DLQs for later reprocessing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the follow up  &lt;a href="https://dev.to/aws-builders/api-gateway-and-lambda-throttling-with-terraform-part-2-3i84"&gt;Amazon API Gateway and AWS Lambda Throttling with Terraform. Part 2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Advanced throttling is a critical aspect of modern cloud applications. By implementing these patterns with Terraform, you can create a robust, scalable, and maintainable throttling solution that protects your applications while optimizing costs. The key is to approach throttling as a dynamic system that requires ongoing attention and refinement, rather than a set-and-forget configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Reminders:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adapt and Review&lt;/strong&gt;: Continuously evaluate and adjust throttling configurations based on real-world usage patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor and Alert&lt;/strong&gt;: Track throttling metrics for actionable insights and maintain comprehensive dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and Compliance&lt;/strong&gt;: Maintain rigorous security checks and documentation while ensuring throttling aligns with business requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Balance&lt;/strong&gt;: Strike the right balance between protection and performance to avoid over-throttling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the configurations and best practices outlined in this guide, you can ensure your applications are prepared to handle varying traffic loads while maintaining predictable performance and cost efficiency. Remember that throttling is not just about limiting requests – it's about creating a resilient system that can gracefully handle any load condition while protecting your infrastructure and budget.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>apigateway</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Comprehensive Guide to AWS AI/ML Services: The Ultimate Decision Maker’s Playbook</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Wed, 09 Oct 2024 11:50:35 +0000</pubDate>
      <link>https://dev.to/aws-builders/comprehensive-guide-to-aws-aiml-services-the-ultimate-decision-makers-playbook-5d1i</link>
      <guid>https://dev.to/aws-builders/comprehensive-guide-to-aws-aiml-services-the-ultimate-decision-makers-playbook-5d1i</guid>
      <description>&lt;p&gt;Looking to integrate artificial intelligence and machine learning into your business strategy? AWS has an ever-growing suite of AI/ML services, which can sometimes feel overwhelming to navigate. But don’t worry—this playbook is designed to provide a clear, simple guide to help you find the right tools for your business. Explore how AWS AI/ML services can empower your business and simplify your processes—no expert knowledge needed!&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy-to-Start Innovations in AWS AI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Amazon PartyRock – Rapid AI Prototyping
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobib21ls02vmsiicdc8p.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobib21ls02vmsiicdc8p.jpeg" alt="Promotional image for PartyRock showcasing its AI-powered app building platform with features like rapid app development, generative AI, and zero coding requirement." width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built on Amazon Bedrock, PartyRock is revolutionizing how we experiment with AI. This no-code environment enables quick application development using pre-trained foundation models, making AI accessible to everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero coding required&lt;/li&gt;
&lt;li&gt;Built-in integration with Bedrock foundation models&lt;/li&gt;
&lt;li&gt;Support for Retrieval Augmented Generation (RAG)&lt;/li&gt;
&lt;li&gt;Rapid prototyping capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing AI models in a business context&lt;/li&gt;
&lt;li&gt;Prototyping AI-driven customer solutions&lt;/li&gt;
&lt;li&gt;Automating simple business tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed up innovation cycles&lt;/li&gt;
&lt;li&gt;Reduce time to market for AI applications&lt;/li&gt;
&lt;li&gt;Democratize AI development for non-technical teams&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amazon Q – Your AI-Powered Assistant
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk9mwkjl1hnyf3qz24qd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk9mwkjl1hnyf3qz24qd.jpeg" alt="Screenshot of Amazon Q integrations with hyper, terminal app, and VS Code terminal, showing enabled options to add Amazon Q to these applications, with a comment at the bottom about potential support for other software." width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Q&lt;/strong&gt; is a generative AI-powered assistant designed to deliver insights, content generation, and task automation across various business functions. It provides &lt;strong&gt;immediate, contextual responses&lt;/strong&gt; by pulling data from your company’s repositories, such as &lt;strong&gt;Amazon S3&lt;/strong&gt;, Google Drive, or &lt;strong&gt;Amazon Kendra&lt;/strong&gt;. It can handle complex queries using natural language and is available in flavors tailored for different use cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flavors of Amazon Q:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Q Business&lt;/strong&gt; Supports marketing, HR, and other departments to automate tasks such as generating summaries, creating content, and streamlining workflows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Q Developer&lt;/strong&gt; Offers AI-powered assistance for software development, from generating code to scanning for vulnerabilities and even modernizing legacy code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Q in QuickSight&lt;/strong&gt; Provides insights directly within &lt;strong&gt;Amazon QuickSight&lt;/strong&gt;, allowing business analysts to use natural language to generate dashboards and analyze data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Amazon Bedrock: Enterprise-Grade Generative AI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetyhwgnszjeqvkehdx40.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetyhwgnszjeqvkehdx40.jpeg" alt="Screenshot of Amazon Bedrock AI service webpage showcasing foundation models, playgrounds for chat, text, and image generation, and features like Bedrock Studio for building scalable AI solutions." width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Amazon Bedrock provides seamless access to a diverse set of foundation models, empowering businesses to build and scale custom AI solutions with ease. Whether you need advanced text generation, image creation, or specialized AI models, Bedrock offers a flexible, enterprise-ready platform to meet your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to popular AI models through a single API&lt;/li&gt;
&lt;li&gt;Integration with enterprise data and applications&lt;/li&gt;
&lt;li&gt;Customizable models for specific business use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automating content generation&lt;/li&gt;
&lt;li&gt;Enhancing data analytics and reporting&lt;/li&gt;
&lt;li&gt;Creating visual content for marketing and design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Business Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed up innovation with cutting-edge AI capabilities&lt;/li&gt;
&lt;li&gt;Personalize customer interactions with tailored solutions&lt;/li&gt;
&lt;li&gt;Maintain robust security and privacy standards at scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amazon Bedrock supports leading models from providers such as &lt;strong&gt;AI21 Labs&lt;/strong&gt;, &lt;strong&gt;Anthropic&lt;/strong&gt;, &lt;strong&gt;Mistral&lt;/strong&gt;, &lt;strong&gt;Stability AI&lt;/strong&gt;, &lt;strong&gt;Cohere&lt;/strong&gt;, and &lt;strong&gt;Amazon’s own models&lt;/strong&gt;, allowing you to choose the best fit for your text and image generation needs. Whether it’s creating high-quality text, summarizing information, or generating detailed visuals, Bedrock gives you the flexibility to harness generative AI for a wide range of business applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS AI/ML Service Categories
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qhhdq7sgw62c4ruip0g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qhhdq7sgw62c4ruip0g.jpeg" alt="Stylized brain halves with circuit patterns connected to cloud representations on a colorful gradient background." width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Ready-to-Use AI Services
&lt;/h3&gt;

&lt;p&gt;These services are designed to be easy to implement with minimal setup, allowing businesses to quickly incorporate AI into their operations. The table below breaks down key AI services, their purposes, and how long they typically take to implement.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Purpose &amp;amp; What it does&lt;/th&gt;
&lt;th&gt;Popular use cases&lt;/th&gt;
&lt;th&gt;Implementation Time (minimum)&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Polly&lt;/td&gt;
&lt;td&gt;Converts text into lifelike speech.&lt;/td&gt;
&lt;td&gt;Voice-enabled apps, content narration, accessibility tools.&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Rekognition&lt;/td&gt;
&lt;td&gt;Analyzes images and videos for objects, people, and activities.&lt;/td&gt;
&lt;td&gt;Security facial recognition, content moderation, media analysis.&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Transcribe&lt;/td&gt;
&lt;td&gt;Converts speech to text from audio and video files.&lt;/td&gt;
&lt;td&gt;Customer call transcription, video subtitles, automated note-taking.&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Comprehend&lt;/td&gt;
&lt;td&gt;Natural language processing to analyze text and extract insights.&lt;/td&gt;
&lt;td&gt;Sentiment analysis, document categorization, entity extraction.&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Kendra&lt;/td&gt;
&lt;td&gt;Provides intelligent search powered by machine learning.&lt;/td&gt;
&lt;td&gt;Enterprise search, customer support portals, research databases.&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Textract&lt;/td&gt;
&lt;td&gt;Extracts text and data from scanned documents, tables, and forms.&lt;/td&gt;
&lt;td&gt;Automated document processing, invoice extraction, identity verification.&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Fraud Detector&lt;/td&gt;
&lt;td&gt;Detects fraudulent online activities using machine learning.&lt;/td&gt;
&lt;td&gt;Fraud detection in payments, account takeovers, identity theft prevention.&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Translate&lt;/td&gt;
&lt;td&gt;Real-time translation of text between languages.&lt;/td&gt;
&lt;td&gt;Website localization, multilingual customer service.&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Personalize&lt;/td&gt;
&lt;td&gt;Provides personalized recommendations based on user behavior.&lt;/td&gt;
&lt;td&gt;E-commerce product recommendations, content streaming suggestions.&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Lex&lt;/td&gt;
&lt;td&gt;Builds conversational interfaces using voice and text (chatbots).&lt;/td&gt;
&lt;td&gt;Customer support chatbots, virtual assistants, voice apps for contact centers.&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. ML Development Platforms
&lt;/h3&gt;

&lt;p&gt;For organizations requiring custom ML solutions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Purpose &amp;amp; What it does&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Implementation Time (minimum)&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon SageMaker&lt;/td&gt;
&lt;td&gt;Provides an end-to-end platform for developing, training, and deploying ML models, with full control and advanced MLOps tools.&lt;/td&gt;
&lt;td&gt;ML teams requiring complete control&lt;/td&gt;
&lt;td&gt;Weeks&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Bedrock&lt;/td&gt;
&lt;td&gt;Enables deployment of foundation models, including custom model fine-tuning and integration with APIs and knowledge bases.&lt;/td&gt;
&lt;td&gt;Teams building GenAI applications&lt;/td&gt;
&lt;td&gt;Days-Weeks&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Canvas&lt;/td&gt;
&lt;td&gt;Offers a no-code platform for creating ML models through a visual interface, integrating business data for fast predictions.&lt;/td&gt;
&lt;td&gt;Business analysts&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Amazon SageMaker&lt;/strong&gt; SageMaker is a comprehensive platform that enables ML teams to develop, train, and deploy models with full control over every aspect of the machine learning lifecycle. Unlike Amazon Bedrock, which is focused on deploying and fine-tuning pre-built foundation models, SageMaker allows for the creation of highly customized ML solutions. With built-in algorithms, auto ML capabilities, distributed training, and advanced MLOps tools, it’s ideal for teams looking to build bespoke models from scratch or with complex training data. SageMaker also offers granular model monitoring, giving ML teams detailed insights into model performance post-deployment. If an organization needs full control over the entire ML pipeline and prefers a deep, hands-on approach to model development, SageMaker is the platform of choice. In contrast, Bedrock simplifies the deployment of foundation models, making it more suited for teams that prioritize quick results with less customization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon Canvas&lt;/strong&gt; For teams without a strong technical background in machine learning, Amazon Canvas offers a no-code solution that allows business analysts to create and deploy models through a simple, visual interface. While SageMaker and Bedrock are designed for technical teams, Canvas focuses on democratizing ML by making it accessible to non-developers. Its automated model creation and integration with business data sources make it an excellent tool for quickly generating insights without writing code. In comparison to Bedrock, Canvas lacks the advanced features for model fine-tuning and complex training but offers a faster way to start generating predictions for business use cases. For organizations looking to empower business users with ML capabilities without involving the technical overhead of SageMaker or the foundational focus of Bedrock, Canvas is the ideal choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Specialized AI Applications
&lt;/h3&gt;

&lt;p&gt;AWS offers purpose-built solutions for specific use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Augmented AI (Amazon A2I) for human review workflows&lt;/li&gt;
&lt;li&gt;Amazon Comprehend Medical for healthcare insights&lt;/li&gt;
&lt;li&gt;Amazon DevOps Guru for application optimization&lt;/li&gt;
&lt;li&gt;Various Lookout services for anomaly detection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Integration with the AWS Ecosystem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpvdtxiizlqlw4z4kzq5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpvdtxiizlqlw4z4kzq5.jpeg" alt="Graphic banner showing AWS integration with various AI and ML services, depicted as interconnected icons over a network diagram." width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS AI/ML services are not standalone solutions but are seamlessly integrated into the broader AWS ecosystem, allowing businesses to manage, scale, and secure their AI workloads efficiently. Whether you’re storing data, ensuring network connectivity, or monitoring system performance, AWS has the capabilities to support every aspect of your IT needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt; A scalable object storage ideal for storing large datasets such as training data and model outputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon EFS (Elastic File System&lt;/strong&gt; A managed file storage that allows shared access to data across multiple AI instances.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon FSx&lt;/strong&gt; Provides specialized file systems for workloads requiring high-performance storage, including FSx for Lustre for data-heavy AI tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Glacier&lt;/strong&gt; Ideal for long-term, low-cost archival of data used for AI model history or compliance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Aurora&lt;/strong&gt; A highly available relational database, perfect for storing structured data required by AI models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon Neptune&lt;/strong&gt; A graph database suited for complex relationships, such as powering recommendation engines or social network analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt; A fully managed NoSQL database for high-performance, low-latency data retrieval.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt; Leverage OpenSearch as a backend for real-time data search and analytics, critical for retrieval-augmented generation (RAG) systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Networking
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon VPC (Virtual Private Cloud)&lt;/strong&gt; Create secure, isolated cloud environments for your AI applications. Connect on-premises resources using &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Direct Connect&lt;/strong&gt;, enabling hybrid cloud setups where your AI/ML systems can securely communicate with existing infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring &amp;amp; Logging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Amazon CloudWatch&lt;/strong&gt; Monitor your AI applications and infrastructure in real-time. Track system performance, set alarms, and gain insights to improve application health and reduce downtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS X-Ray Trace&lt;/strong&gt; requests as they travel through your AI services, making it easier to diagnose bottlenecks or errors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security &amp;amp; Compliance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS IAM (Identity and Access Management)&lt;/strong&gt; Implement fine-grained security controls to manage access to your AI/ML services. With &lt;strong&gt;AWS Key Management Service (KMS)&lt;/strong&gt; and built-in encryption, your data is secured both in transit and at rest. Compliance with GDPR, HIPAA, and other standards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Shield&lt;/strong&gt; and &lt;strong&gt;WAF (Web Application Firewall)&lt;/strong&gt; Protect your AI-driven web applications from DDoS attacks, ensuring availability and reliability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  On-Premise Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS supports hybrid cloud solutions, making it easy to integrate with your existing IT infrastructure. Services like &lt;strong&gt;AWS Outposts&lt;/strong&gt; allow you to run AWS AI services on-premise, while &lt;strong&gt;AWS Snowball&lt;/strong&gt; and &lt;strong&gt;Snowcone&lt;/strong&gt; enable secure data migration to the cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, AWS has the capability to host virtually every IT service your business needs, whether in the cloud or integrated with your existing infrastructure. By leveraging its vast ecosystem, you can ensure that your AI solutions are scalable, secure, and highly efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Considerations
&lt;/h2&gt;

&lt;p&gt;AWS AI/ML services follow a pay-as-you-go model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ready-to-use services:&lt;/strong&gt; Generally cents to dollars per use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ML platforms:&lt;/strong&gt; Hundreds to thousands monthly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom infrastructure:&lt;/strong&gt; Higher costs based on scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Case Studies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bayer AG&lt;/strong&gt; &lt;a href="https://www.tecracer.com/wp-content/uploads/2022/03/tecRacer-Success-Story-Bayer_EN.pdf" rel="noopener noreferrer"&gt;tecRacer helped Bayer AG to implement&lt;/a&gt; an open platform for  AI/ML development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hapag-Lloyd Hapag-Lloyd&lt;/strong&gt; leveraged &lt;strong&gt;Amazon Q Business&lt;/strong&gt; to automate responses to internal procedure queries, significantly reducing support time. With response times as fast as 1–3 seconds, Hapag-Lloyd aims to redirect support staff to higher-value tasks, enhancing efficiency and employee satisfaction.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/solutions/case-studies/lg-ai-research-case-study/" rel="noopener noreferrer"&gt;&lt;strong&gt;LG AI Research&lt;/strong&gt;&lt;/a&gt;: LG AI Research adopted &lt;strong&gt;Amazon SageMaker&lt;/strong&gt; to accelerate the development of AI models for various applications. By utilizing SageMaker’s powerful tools, LG significantly reduced the time required for training models, enabling faster innovation and the deployment of cutting-edge AI solutions across multiple industries.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/blogs/machine-learning/slack-delivers-native-and-secure-generative-ai-powered-by-amazon-sagemaker-jumpstart/" rel="noopener noreferrer"&gt;&lt;strong&gt;Slack&lt;/strong&gt;&lt;/a&gt;: &lt;strong&gt;Slack&lt;/strong&gt; integrated &lt;strong&gt;Amazon SageMaker JumpStart&lt;/strong&gt; to power its native generative AI features, ensuring secure and scalable AI-driven solutions. This enabled Slack to offer enhanced user experiences by quickly delivering AI-powered insights, automating tasks, and providing personalized assistance, all while maintaining high levels of security and compliance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Beginners:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start with Amazon PartyRock for quick experiments&lt;/li&gt;
&lt;li&gt;Utilize ready-to-use services like Polly and Rekognition&lt;/li&gt;
&lt;li&gt;Explore Amazon Q Business for immediate business insights&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  For Advanced Users:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Leverage Amazon Bedrock for custom AI applications&lt;/li&gt;
&lt;li&gt;Use SageMaker for end-to-end ML development&lt;/li&gt;
&lt;li&gt;Implement specialized services for specific use cases&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Remember to start small, experiment frequently, and scale based on your success. AWS's flexible pricing model allows you to grow your AI capabilities alongside your business needs.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;tecRacer&lt;/strong&gt;, we specialize in helping businesses unlock the full potential of AWS AI/ML services. With years of experience across various industries, we have successfully implemented real-world AI/ML solutions that accelerate innovation and deliver tangible business results. &lt;/p&gt;

&lt;p&gt;Let us help you transform your business with AWS AI/ML services today.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>machinelearning</category>
      <category>ai</category>
      <category>guide</category>
    </item>
    <item>
      <title>Performance Boost: 10 Expert Tips for Optimizing Your Amazon OpenSearch Service Cluster</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Wed, 02 Oct 2024 08:43:24 +0000</pubDate>
      <link>https://dev.to/aws-builders/performance-boost-10-expert-tips-for-optimizing-your-amazon-opensearch-service-cluster-30h9</link>
      <guid>https://dev.to/aws-builders/performance-boost-10-expert-tips-for-optimizing-your-amazon-opensearch-service-cluster-30h9</guid>
      <description>&lt;p&gt;By implementing these recommendations, you can maximize the potential of your Amazon OpenSearch Service domain, delivering an improved search experience while optimizing costs and maintaining security. Let's explore these expert tips to supercharge your OpenSearch cluster. &lt;/p&gt;

&lt;p&gt;This guide updates our 10 expert tips for 2024, grouped into key areas: Hardware, Indexing, Monitoring, Sharding, and Query Optimization. We’ll also discuss why keeping your OpenSearch version up to date is crucial for unlocking performance improvements. &lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwmmfsz8n283tmtv20sq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwmmfsz8n283tmtv20sq.png" alt="abstract hardware" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Graviton3 and OR1 Instances for Better Performance&lt;/strong&gt; 
The new &lt;strong&gt;Graviton3-based instances&lt;/strong&gt;—&lt;strong&gt;C7g&lt;/strong&gt; (compute-optimized), &lt;strong&gt;M7g&lt;/strong&gt; (general-purpose), &lt;strong&gt;R7g&lt;/strong&gt; (memory-optimized), and &lt;strong&gt;R7gd&lt;/strong&gt; (with local SSD storage)—offer significant performance boosts over their predecessors. They deliver up to &lt;strong&gt;30% better compute performance&lt;/strong&gt; and improved &lt;strong&gt;energy efficiency&lt;/strong&gt;, making them ideal for a variety of OpenSearch workloads.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C7g&lt;/strong&gt; is ideal for compute-heavy search operations and analytics workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M7g&lt;/strong&gt; suits a balanced mix of compute, memory, and storage needs, perfect for general-purpose OpenSearch domains.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R7g&lt;/strong&gt; and &lt;strong&gt;R7gd&lt;/strong&gt; are designed for memory-intensive use cases, with &lt;strong&gt;R7gd&lt;/strong&gt; also offering local SSD storage for high-speed access to frequently used data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use OR1 Instances for Cost-Effective Long-Running Workloads&lt;/strong&gt; 
The &lt;strong&gt;OR1&lt;/strong&gt; family is designed to handle long-running, steady-state workloads like log ingestion and monitoring at a lower cost. While not as powerful as the Graviton3-based instances, OR1 instances are optimized for cost savings and long-term log storage, balancing performance with reduced pricing for operational workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start big&lt;/strong&gt; 
Remember, it's easier to measure the excess capacity in an overpowered cluster than the deficit in an underpowered one, so it's recommended to start with a larger cluster than you think you need, then test and scale down to an efficient cluster that has the extra resources to ensure stable operations during periods of increased activity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Indexing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnh3zm2bptfgoh5gozpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnh3zm2bptfgoh5gozpf.png" alt="abstract indexing big data" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use bulk ingest requests and employ multi-threading&lt;/strong&gt; 
Bulk requests are more efficient than individual index requests. For example, a single thread can index 1000 small documents per second, but with bulk requests, it can index 100,000 to 250,000 documents per second. The bulk API is a powerful tool for indexing multiple documents in a single request, reducing the overhead of individual indexing requests. The optimal bulk size varies depending on the use case, but a good starting point is between 5-15MB.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To enhance indexing throughput, employ multi-threading. This can be achieved using OpenSearch SDKs and libraries like &lt;code&gt;opensearch-py&lt;/code&gt;. By creating 10-20 threads per node, you can significantly boost your indexing performance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Optimize&lt;/strong&gt; 
Minimize frequent updates: To maximize efficiency in OpenSearch, minimize frequent updates to the same document. This prevents the accumulation of deleted documents and large segment sizes. Instead, collect necessary updates in your application and selectively transmit them to OpenSearch, reducing overhead and improving performance. As an example, when storing stock information in the index, it's recommended to represent it using levels (e.g., available, low, not available) instead of numerical values. This approach ensures efficient storage and retrieval of stock data in OpenSearch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not index everything: Disabling indexing for specific fields by setting &lt;code&gt;"index": false&lt;/code&gt; in the field mapping can help optimize storage, improve indexing performance.&lt;/p&gt;

&lt;p&gt;Tune your _source field :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;_source&lt;/code&gt; field in OpenSearch is a special field that holds the original JSON object that was indexed. This field is automatically stored for each indexed document and is returned by default in search results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The primary advantage of the &lt;code&gt;_source&lt;/code&gt; field is that it allows you to access the original document directly from the search results. This can be particularly useful for debugging purposes or for performing partial updates to documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;However, storing the &lt;code&gt;_source&lt;/code&gt; field does increase storage requirements. Each indexed document essentially gets stored twice: once in the inverted index for searching and once in the &lt;code&gt;_source&lt;/code&gt; field.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your use case doesn't require accessing the original document in search results, you can disable storing the &lt;code&gt;_source&lt;/code&gt; field to save storage space. This can be done by setting &lt;code&gt;"enabled": false&lt;/code&gt; in the &lt;code&gt;_source&lt;/code&gt; field mapping.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoring
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2row85559vekcf40wyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2row85559vekcf40wyt.png" alt="monitoring" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use CloudWatch&lt;/strong&gt; 
Monitoring tools like Amazon CloudWatch can be used to track indexing performance and identify bottlenecks. Enabling Slow Logs can save a lot of time. Set up &lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/cloudwatch-alarms.html" rel="noopener noreferrer"&gt;the recommended CloudWatch alarms for Amazon OpenSearch Service&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile queries&lt;/strong&gt;
Profiling your OpenSearch queries can provide valuable insight into how your queries are being executed and where potential performance bottlenecks may be occurring. The Profile API in OpenSearch is a powerful tool for this purpose.

&lt;ul&gt;
&lt;li&gt;To use the Profile API, simply append &lt;code&gt;?profile=true&lt;/code&gt; to your search queries. This will return a detailed breakdown of your query's execution, including information about how long each operation took and how the query was rewritten internally.&lt;/li&gt;
&lt;li&gt;The output of the Profile API is divided into sections for each shard that participated in the response. Within each shard section, you'll find details about the query and aggregation trees.&lt;/li&gt;
&lt;li&gt;The query tree shows how the query was executed across the inverted index, including the time taken by each term. The aggregation tree, on the other hand, shows how the aggregations were computed, including the time taken by each bucket.&lt;/li&gt;
&lt;li&gt;By analyzing this information, you can identify which parts of your query are taking the most time and adjust them accordingly. This could involve changing the structure of your query, adjusting your index mappings, or modifying your OpenSearch cluster configuration.&lt;/li&gt;
&lt;li&gt;Remember, profiling adds overhead to your queries, so it's best to use it sparingly and only in a testing or debugging environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sharding
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo4i79u2a0wymbz1xvcs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo4i79u2a0wymbz1xvcs.png" alt="sharding" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find an optimal shard number and size:&lt;/strong&gt; The ideal shard size in OpenSearch is typically between 10GB and 50GB for workloads where search latency is a key performance objective, and 30-50GB for write-heavy workloads such as log analytics. Large shards can make it difficult for OpenSearch to recover from failure, but having too many small shards can cause performance issues and out of memory errors. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The number of primary shards for an index should be determined based on the amount of data you have and your expected data growth. A general guideline is to try to keep shard size between 10–30 GiB for workloads where search latency is a key performance objective, and 30–50 GiB for write-heavy workloads such as log analytics.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Optimize shard locating:&lt;/strong&gt; Overallocating shards can lead to wasted resources. On a given node, have no more than 25 shards per GiB of Java heap. For example, an m5.large.search instance has a 4-GiB heap, so each node should have no more than 100 shards. At that shard count, each shard is roughly 5 GiB in size, which is well below the recommended size range.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Search and Query Performance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use filters:&lt;/strong&gt; Filters are faster than queries because they don’t calculate relevance (_score). They simply include or exclude documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use search templates:&lt;/strong&gt; One effective way to boost your Amazon OpenSearch Service is by utilizing search templates. Search templates allow you to predefine and reuse complex search queries, reducing the processing time and improving search performance. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bonus Tip: Regularly Update Your Cluster for Optimal Performance
&lt;/h2&gt;

&lt;p&gt;Keeping your Amazon OpenSearch Service cluster up to date is one of the easiest ways to ensure peak performance. Each new version of OpenSearch introduces significant performance improvements, optimizations, and bug fixes. For example, OpenSearch &lt;strong&gt;2.14&lt;/strong&gt; brings major enhancements to query speed, indexing efficiency, and resource management, which can significantly reduce costs while improving overall cluster responsiveness.&lt;/p&gt;

&lt;p&gt;To ensure you're taking advantage of the latest improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan Regular Updates:&lt;/strong&gt; Schedule periodic updates for your OpenSearch clusters. AWS makes it easy to upgrade with minimal downtime using blue/green deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test in a Staging Environment:&lt;/strong&gt; Before applying updates in production, always test new versions in a staging environment to ensure compatibility with your existing setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage New Features:&lt;/strong&gt; Take advantage of the latest features and optimizations in the newer versions, such as better memory management, faster queries, and enhanced index recovery processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regularly updating your OpenSearch cluster will keep your environment running smoothly and allow you to leverage the latest innovations for improved performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Reading&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/bp.html" rel="noopener noreferrer"&gt;Operational best practices for Amazon OpenSearch Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/big-data/get-started-with-amazon-opensearch-service-t-shirt-size-your-domain/" rel="noopener noreferrer"&gt;Get started with Amazon OpenSearch Service: T-shirt-size your domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/opensearch-service/faqs" rel="noopener noreferrer"&gt;Amazon OpenSearch Service FAQs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/blog/fine-dining-for-indices/" rel="noopener noreferrer"&gt;Fine Dining for Indices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://repost.aws/knowledge-center/opensearch-indexing-performance" rel="noopener noreferrer"&gt;How can I improve the indexing performance on my Amazon OpenSearch Service cluster?&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://repost.aws/knowledge-center/opensearch-resolve-429-error" rel="noopener noreferrer"&gt;How do I resolve search or write rejections in OpenSearch Service?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opster.com/blogs/improve-opensearch-indexing-rate/" rel="noopener noreferrer"&gt;How to Improve your OpenSearch Indexing performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opster.com/blogs/improve-opensearch-search-performance/" rel="noopener noreferrer"&gt;How to Improve OpenSearch Search Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://d1.awsstatic.com/events/reinvent/2021/Sizing_Amazon_OpenSearch_Service_domains_REPEAT_ANT402-R2.pdf" rel="noopener noreferrer"&gt;Sizing Amazon OpenSearch Service Domains&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/08/amazon-opensearch-service-graviton3-c7g-m7g-r7g-r7gd-instances/" rel="noopener noreferrer"&gt;Amazon OpenSearch Service now supports Graviton3-based C7g, M7g, R7g, and R7gd instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/or1.html" rel="noopener noreferrer"&gt;OR1 Instance Type for Amazon OpenSearch Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/blog/opensearch-performance-2.14/" rel="noopener noreferrer"&gt;OpenSearch Project update: A look at performance progress through version 2.14&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind, these tips and improvements are just the starting point. The ultimate effectiveness of their application depends on your specific scenario and use case. While we've focused on the areas of Hardware, Indexing, Sharding, Monitoring, and Optimization, there are many more facets to consider. For instance, security, which is a critical component we haven't delved into here. Remember, your OpenSearch Service should be as unique as your needs.&lt;/p&gt;

&lt;p&gt;If you require assistance in optimizing your OpenSearch Service deployment, &lt;a href="https://www.tecracer.com/en/consulting/amazon-opensearch-service/" rel="noopener noreferrer"&gt;tecRacer&lt;/a&gt;, an Amazon OpenSearch Service Delivery Partner, is here to provide expert guidance. Our team of professionals specializes in designing, deploying and securely managing OpenSearch Service infrastructures tailored to individual needs. Whether you need support in selecting the right instance types, fine-tuning indexing strategies, monitoring performance, or optimizing search and query operations, tecRacer can provide the expertise you need.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo of &lt;a href="https://unsplash.com/de/fotos/gluhbirne-illustration-_kdTyfnUFAc?utm_content=creditShareLink&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Alessandro Bianchi&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>optimize</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why and How to Migrate from Solr to Amazon OpenSearch Service in 2024</title>
      <dc:creator>Alexey Vidanov</dc:creator>
      <pubDate>Mon, 30 Sep 2024 14:58:33 +0000</pubDate>
      <link>https://dev.to/aws-builders/why-and-how-to-migrate-from-solr-to-amazon-opensearch-service-in-2024-43hk</link>
      <guid>https://dev.to/aws-builders/why-and-how-to-migrate-from-solr-to-amazon-opensearch-service-in-2024-43hk</guid>
      <description>&lt;p&gt;This guide is for organizations currently using Solr and considering a migration to Amazon OpenSearch Service. We will explore the benefits of migrating, the process, and the key challenges, offering insights from real-world migrations to help you make an informed decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction: The Evolution of Enterprise Search
&lt;/h2&gt;

&lt;p&gt;In today’s fast-paced digital landscape, having a scalable and easily manageable search infrastructure is essential for smooth user experiences and efficient operations. &lt;strong&gt;Apache Solr&lt;/strong&gt; and &lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt; are two robust platforms offering advanced search capabilities. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8doakbjg0a30asm2tp4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8doakbjg0a30asm2tp4w.png" alt="Google Trends graph comparing interest over time for search terms " width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Though &lt;strong&gt;Apache Solr&lt;/strong&gt; has been a trusted solution for years, there is growing interest in cloud-native platforms like &lt;strong&gt;Amazon OpenSearch Service&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This shift reflects a broader demand for managed services that simplify operations, making OpenSearch an attractive choice for organizations looking to modernize their search infrastructure.&lt;/p&gt;

&lt;p&gt;This guide is for organizations currently using Solr and considering a migration to Amazon OpenSearch Service. We will explore the benefits of migrating, the process, and the key challenges, offering insights from real-world migrations to help you make an informed decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Migrate?
&lt;/h2&gt;

&lt;p&gt;Migrating from Solr to OpenSearch offers several compelling benefits, especially for AWS-centric organizations. The advantages of moving to Amazon OpenSearch Service include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Management&lt;/strong&gt;: OpenSearch is a fully managed service, reducing the burden of maintaining your search infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-Native Scalability&lt;/strong&gt;: OpenSearch’s integration with other AWS services allows for smooth scaling and performance optimization without additional effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Features&lt;/strong&gt;: OpenSearch provides out-of-the-box solutions such as ML integrations, anomaly detection, real-time analytics, and powerful &lt;strong&gt;dashboards&lt;/strong&gt; for intuitive data visualization—features that extend beyond the capabilities of Solr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and Compliance&lt;/strong&gt;: Amazon OpenSearch Service integrates with AWS Identity and Access Management (IAM) and offers encryption at rest and in transit, among other security features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration Process: From Solr to Amazon OpenSearch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Assess Your Priorities, Train Your Team, and Choose a Migration Strategy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before diving into technical details, it’s crucial to define your migration priorities and ensure your team is prepared for the transition to OpenSearch. Whether you're opting for a &lt;strong&gt;lift-and-shift&lt;/strong&gt; migration or planning a &lt;strong&gt;modernization&lt;/strong&gt; strategy, it’s essential that your team understands how to use and manage OpenSearch effectively.&lt;/p&gt;

&lt;p&gt;Start by organizing &lt;strong&gt;training sessions&lt;/strong&gt; or &lt;strong&gt;workshops&lt;/strong&gt; to familiarize your team with OpenSearch’s features, query language, and management tools. This will help ensure a smoother transition and enable your team to take full advantage of OpenSearch’s capabilities from the outset. &lt;strong&gt;tecRacer&lt;/strong&gt; offers tailored workshops that can help your team get up to speed quickly.&lt;/p&gt;

&lt;p&gt;Once your team is prepared, you can decide between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Downtime Migration&lt;/strong&gt;: Opt for an &lt;strong&gt;active-active&lt;/strong&gt; setup where both Solr and OpenSearch run in parallel, ensuring a smooth cutover with zero downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downtime-Tolerant Migration&lt;/strong&gt;: If your application can tolerate some downtime, you can simplify and expedite the migration process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, for cases where Solr isn’t the system of record, consider rebuilding indices directly in OpenSearch. This gives you the opportunity to redesign schemas, mappings, and search functionalities, allowing for a cleaner, modern implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS&lt;/strong&gt; recommends modernization for long-term scalability and flexibility. From our experience, modernization can be achieved rapidly, with most of the complexity typically residing in the application layer rather than the search engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Cluster Sizing and Cost Estimation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once you’ve committed to modernization and your team is trained, the next step is to right-size your OpenSearch cluster. Start by assessing your current Solr setup in terms of performance and cost, then project these needs into OpenSearch. Consider how scaling requirements will affect performance and costs in a cloud-native environment.&lt;/p&gt;

&lt;p&gt;After this assessment, plan a &lt;strong&gt;Proof of Concept (PoC)&lt;/strong&gt; &lt;strong&gt;after completing the schema mapping and key configurations&lt;/strong&gt; (discussed in step 5). This PoC will help validate your cluster sizing and performance estimates, ensuring you’re working with the right setup before the actual migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Core Functionality and Future Needs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Review the core functionalities of your existing search infrastructure. Identify which features are critical to your business and explore ways OpenSearch’s advanced capabilities can improve your search infrastructure. For example, OpenSearch offers &lt;strong&gt;vector search&lt;/strong&gt;, &lt;strong&gt;semantic search&lt;/strong&gt;, &lt;strong&gt;hybrid search&lt;/strong&gt;, and more, which could open new opportunities for your organization.&lt;/p&gt;

&lt;p&gt;Also, plan for future scaling and feature needs. &lt;strong&gt;tecRacer&lt;/strong&gt; can offer workshops and training to help your team fully leverage OpenSearch’s potential.&lt;/p&gt;

&lt;p&gt;At this point, focus on planning and be prepared to conduct the following tests after the initial setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relevance and Accuracy&lt;/strong&gt;: Ensuring that the migrated queries produce high-quality search results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search Latency&lt;/strong&gt;: Measuring response times under typical and peak usage conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Experience (UX)&lt;/strong&gt;: Validating that the search results align with user expectations in terms of speed and relevance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Proof of Concept (PoC), Sizing Decisions, and Final Testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With your migration strategy in place, you can now perform a &lt;strong&gt;Proof of Concept (PoC)&lt;/strong&gt;. During the PoC, test different cluster configurations and query behaviors to ensure they align with your performance needs and cost projections. This phase allows you to fine-tune your cluster size and performance settings based on real-world workloads.&lt;/p&gt;

&lt;p&gt;Key considerations for the PoC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance Testing&lt;/strong&gt;: Simulate typical and peak traffic scenarios to ensure your OpenSearch cluster scales efficiently and maintains the required performance levels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Quality and UX Testing&lt;/strong&gt;: Evaluate the quality of search results, ensuring accuracy and relevance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Efficiency&lt;/strong&gt;: Ensure your cluster sizing and performance optimizations meet both operational and cost requirements, factoring in future growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the PoC results, finalize your decision on whether the cluster is sized appropriately and whether OpenSearch can handle your workload effectively. If all criteria are met, you can confidently move forward with the migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Execution: Schema Mapping and Best Practices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once you have the correct sizing from the PoC, the next step is to begin the actual migration process, starting with schema mapping. OpenSearch uses a more flexible, dynamic mapping system than Solr, allowing you to optimize field types and indexing behavior.&lt;/p&gt;

&lt;p&gt;During this phase, focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema-to-Mapping Translation&lt;/strong&gt;: Convert your Solr schema into OpenSearch mappings, leveraging OpenSearch’s flexible field definitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyzers and Normalizers&lt;/strong&gt;: Choose and configure the appropriate analyzers and normalizers for your data to ensure proper indexing and search functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index and Shard Management&lt;/strong&gt;: Plan the number of indices and shards carefully to optimize performance and resource allocation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thoroughly test your mapping and indexing configurations to ensure they align with your business needs before migrating production data.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Security and Compliance Best Practices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As you set up the OpenSearch environment, make security a priority. OpenSearch integrates seamlessly with AWS Identity and Access Management (IAM), allowing granular permission control. Additionally, you can secure your cluster within a VPC and monitor it with &lt;strong&gt;Amazon CloudWatch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Key steps include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Roles and Permissions&lt;/strong&gt;: Implement the appropriate permissions for your OpenSearch cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt;: Enable encryption for data both at rest and in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt;: Set up &lt;strong&gt;CloudWatch&lt;/strong&gt; to monitor security and performance metrics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As security configurations can vary between Solr and OpenSearch, thoroughly test your security settings to ensure compliance before migrating live data.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. &lt;strong&gt;Final Migration and Continuous Operations Setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With the PoC validated, schema mappings tested, and security in place, it’s time for the actual migration. Depending on your chosen strategy (active-active or downtime-tolerant), you’ll either phase in OpenSearch or switch over entirely.&lt;/p&gt;

&lt;p&gt;During the final migration, you need to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workload Runbooks&lt;/strong&gt;: Define procedures for handling day-to-day operations, covering search queries, index management, and performance monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alarms and Monitoring&lt;/strong&gt;: Set up alarms in &lt;strong&gt;CloudWatch&lt;/strong&gt; to alert your team of any anomalies in performance or security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Tuning&lt;/strong&gt;: Continuously monitor and fine-tune your OpenSearch cluster to ensure it operates efficiently, adjusting based on real-world workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup and Disaster Recovery Plans&lt;/strong&gt;: Establish regular snapshot schedules, and ensure that your disaster recovery plan is robust and regularly tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By ensuring that monitoring and automation are in place, you can maintain a smooth-running system post-migration. Regular performance and cost evaluations, as well as ongoing query and data quality improvements, will help keep your OpenSearch cluster optimized.&lt;/p&gt;

&lt;p&gt;Through this structured process—starting with clear migration priorities, validating configurations via a PoC, training your team, and finally executing a carefully monitored migration—you can ensure a successful transition from Solr to OpenSearch with minimal disruption and maximum long-term benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminology Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Solr Term&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OpenSearch Term&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Comment&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Collection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Index&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Both represent a set of documents to be searched. Solr’s &lt;em&gt;Collection&lt;/em&gt; is equivalent to an &lt;em&gt;Index&lt;/em&gt; in OpenSearch.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Both define the structure of documents. Solr uses a &lt;em&gt;Schema&lt;/em&gt;, while OpenSearch uses &lt;em&gt;Mapping&lt;/em&gt; to manage field types and document structure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FieldType&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Analyzer (and more)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Solr’s &lt;em&gt;FieldType&lt;/em&gt; defines both data types and how fields are analyzed. OpenSearch’s &lt;em&gt;Analyzer&lt;/em&gt; only handles text analysis.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamicField&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Solr’s &lt;em&gt;DynamicField&lt;/em&gt; and OpenSearch’s &lt;em&gt;Dynamic Mapping&lt;/em&gt; both apply rules automatically to new fields that are not explicitly defined.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query (Standard Syntax)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Query DSL (JSON-based)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Solr uses a standard syntax for queries, while OpenSearch uses a &lt;em&gt;Query DSL&lt;/em&gt;, which is more flexible and expressed in JSON.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Schema and Query Translation: Key Differences
&lt;/h2&gt;

&lt;p&gt;While Solr and OpenSearch both leverage &lt;strong&gt;Lucene&lt;/strong&gt;, the two platforms handle schemas and queries differently, which presents challenges during migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema Translation
&lt;/h3&gt;

&lt;p&gt;Solr uses a &lt;strong&gt;global schema&lt;/strong&gt; for each collection, which simplifies management but can lead to limitations when different documents require varied settings. OpenSearch, on the other hand, employs &lt;strong&gt;index-based mappings&lt;/strong&gt;, offering flexibility in handling diverse data types across multiple indices. &lt;/p&gt;

&lt;h3&gt;
  
  
  Query Translation
&lt;/h3&gt;

&lt;p&gt;OpenSearch and Solr differ significantly in their querying approaches, but OpenSearch provides a wide range of powerful options, which you can explore in its API reference. For instance, the simple_query_string query has a syntax similar to Solr’s, making it an easy starting point for users less familiar with OpenSearch’s DSL. In many cases, it can perform well and simplify the migration process.&lt;/p&gt;

&lt;p&gt;That said, we’ve observed with one of our customers that using simple_query_string in OpenSearch is not exactly the same as in Solr. While it may seem similar at first glance, there are key differences in behavior, such as handling term-specific boosts.&lt;/p&gt;

&lt;p&gt;Here’s an example of where these differences come into play:&lt;/p&gt;

&lt;p&gt;In Solr, you can easily boost individual terms in a multi-term query using the caret (^) symbol. But in OpenSearch’s simple_query_string, this functionality is not fully supported, and queries relying on heavy term-specific boosting may need more advanced solutions like the bool query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solr Query with Term-Specific Boosting&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;q=title:(OpenSearch^2 AWS) OR description:(search engine)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Solr query, only the term &lt;code&gt;OpenSearch&lt;/code&gt; is boosted within the &lt;code&gt;title&lt;/code&gt; field, while &lt;code&gt;AWS&lt;/code&gt; is left unboosted. This term-specific boosting within a multi-term query is not natively supported in OpenSearch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempted OpenSearch Equivalent&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"simple_query_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title:(OpenSearch^2 AWS) OR description:(search engine)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this query will run, &lt;strong&gt;the boost on &lt;code&gt;OpenSearch&lt;/code&gt; (&lt;code&gt;^2&lt;/code&gt;) will be ignored&lt;/strong&gt; because &lt;code&gt;simple_query_string&lt;/code&gt; in OpenSearch does not support boosting individual terms within a multi-word query.&lt;/p&gt;

&lt;p&gt;To accurately replicate this behavior, you would need to use more advanced query types like &lt;code&gt;bool&lt;/code&gt; or &lt;code&gt;match&lt;/code&gt;, where you can apply specific boosts to each term individually.&lt;/p&gt;

&lt;h4&gt;
  
  
  OpenSearch &lt;code&gt;bool&lt;/code&gt; Query with Term-Specific Boosting:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"should"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
              &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpenSearch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
              &lt;/span&gt;&lt;span class="nl"&gt;"boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
              &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS"&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
              &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search engine"&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, each term (&lt;code&gt;OpenSearch&lt;/code&gt; and &lt;code&gt;AWS&lt;/code&gt;) is treated separately, with explicit boosting applied only to &lt;code&gt;OpenSearch&lt;/code&gt;. This approach is required because OpenSearch does not support inline term-specific boosts within a single multi-word query in the way Solr does.&lt;/p&gt;

&lt;p&gt;Thus, while &lt;code&gt;simple_query_string&lt;/code&gt; can be a helpful starting point for migrating basic queries from Solr, more complex queries—especially those involving term-specific boosting—require transitioning to OpenSearch's &lt;strong&gt;full Query DSL&lt;/strong&gt; for accurate query behavior and control..&lt;/p&gt;

&lt;h4&gt;
  
  
  More Query Translation Examples
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Basic Field Query&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solr&lt;/strong&gt;: &lt;code&gt;q=title:OpenSearch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpenSearch"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Range Query&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solr&lt;/strong&gt;: &lt;code&gt;q=price:[10 TO 100] AND popularity:[5 TO *]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"bool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"must"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nl"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nl"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="nl"&gt;"popularity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nl"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fuzzy Search&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solr&lt;/strong&gt;: &lt;code&gt;q=title:OpenSerch~2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"fuzzy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OpenSerch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"fuzziness"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Phrase Query with Slop&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solr&lt;/strong&gt;: &lt;code&gt;q="open source search"~3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenSearch&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open source search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"slop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Case: MemoMeister's Solr Migration to Amazon OpenSearch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghsn2yyavwk1cns9ill1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghsn2yyavwk1cns9ill1.png" alt="Logo of MemoMeister featuring a stylized clipboard and wing design on a blue background." width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Freiraum GmbH, based in Stuttgart, manages the digital documentation platform &lt;a href="https://memomeister.com" rel="noopener noreferrer"&gt;MemoMeister&lt;/a&gt;. Recognizing that efficient enterprise search was critical for its customers, the company decided to migrate its search infrastructure from Solr on EC2 to Amazon OpenSearch Service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Objectives of the Migration:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reduce operational overhead through a fully managed service.&lt;/li&gt;
&lt;li&gt;Improve search performance and scalability.&lt;/li&gt;
&lt;li&gt;Leverage OpenSearch’s integration with other AWS services for enhanced resilience and ease of management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Proof of Concept (PoC) focused on testing OpenSearch in a &lt;strong&gt;Multi-AZ&lt;/strong&gt; environment, ensuring the solution met high availability and performance benchmarks. The results demonstrated that OpenSearch not only met but exceeded expectations in terms of ease of use, resilience, and drastically reduced operational overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jan Schurkus, CEO bei Freiraum GmbH&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;During the Proof of Concept Project for the migration of Solr to Amazon OpenSearch Service, we were not only be able to assess the viability of this solution but also initiated the migration process itself. tecRacer helped us to untangle the knots in our minds and unleash the full potential of the service. The impulses they provided were crucial in developing a solution aligned with best practices. We work with a tecRacer on various cloud improvement projects simultaneously, which serves as building blocks for achieving greater success with our customers.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Migration Highlights:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Savings on Total Cost of Ownership (TCO)&lt;/strong&gt;: Operational overhead was reduced by 50%x, thanks to OpenSearch’s ease of management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Stability&lt;/strong&gt;: The stability provided by OpenSearch’s managed service ensured a seamless migration experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Search Capabilities&lt;/strong&gt;: Despite initial concerns, OpenSearch offered all the necessary search functionalities, along with improved resource efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overcoming Challenges in Migration
&lt;/h2&gt;

&lt;p&gt;Migrating from Solr to OpenSearch can involve various challenges, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema Differences&lt;/strong&gt;: Addressing the differences between Solr’s global schema and OpenSearch’s index-specific mappings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Language Adjustments&lt;/strong&gt;: Rewriting queries to use OpenSearch’s Query DSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminology Translation&lt;/strong&gt;: Understanding and adapting the differences in terminology between Solr and OpenSearch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Integrity&lt;/strong&gt;: Ensuring that field mappings and data ingestion processes don’t lead to inconsistencies during the migration process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Migrating from Solr to Amazon OpenSearch Service offers numerous long-term benefits, including simplified management, enhanced search functionalities, and better scalability. With the right tools and planning, the migration process can be seamless and rewarding. By adopting a &lt;strong&gt;proof of concept&lt;/strong&gt; approach, organizations can ensure that they are set up for success, both in terms of technical performance and operational efficiency.&lt;/p&gt;

&lt;p&gt;Whether you're looking to reduce operational overhead, improve search capabilities, or leverage AWS's ecosystem, migrating to OpenSearch is a forward-thinking choice for enterprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/" rel="noopener noreferrer"&gt;AWS Documentation on OpenSearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/big-data/migrate-from-apache-solr-to-opensearch/" rel="noopener noreferrer"&gt;AWS Blog: Migrate from Apache Solr to OpenSearch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tecracer.com/training/amazon-opensearch-service-fundamentals/" rel="noopener noreferrer"&gt;tecRacer Training Amazon OpenSearch Service Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://memomeister.com" rel="noopener noreferrer"&gt;MemoMeister&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/" rel="noopener noreferrer"&gt;OpenSearch Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/java/" rel="noopener noreferrer"&gt;OpenSearch Java Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/javascript/" rel="noopener noreferrer"&gt;OpenSearch JavaScript Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/python/" rel="noopener noreferrer"&gt;OpenSearch Python Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solr.apache.org/guide/" rel="noopener noreferrer"&gt;Solr Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>opensearch</category>
      <category>enterprise</category>
      <category>solr</category>
    </item>
  </channel>
</rss>
