<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Elastic Cloud Hosted - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Elastic Cloud Hosted - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/search-labs/blog/category/elastic-cloud-hosted</link>
    </image>
    <link>https://www.elastic.co/search-labs/blog/category/elastic-cloud-hosted</link>
    <atom:link href="https://www.elastic.co/search-labs/rss/category/elastic-cloud-hosted.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Wed, 16 Sep 2026 23:41:38 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Faster Elasticsearch issue triage with redesigned AutoOps]]></title>
    <description><![CDATA[AutoOps introduces clearer severity, updated page layouts, and simpler issue triage for Elastic Cloud Hosted deployments and Cloud Connect clusters.]]></description>
    <content:encoded><![CDATA[<p>AutoOps has a redesigned experience for Elastic Cloud Hosted deployments and Cloud Connect clusters. The update adds a new Critical severity level and refreshes every page, including Template Optimizer, Nodes, Shards and Overview. Updated layouts and navigation make Elasticsearch issues easier to scan and triage. This post covers the redesigned UI and where AutoOps is headed next, including a headless, agentic experience.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd9be3378b7b18d00/6a6a33c6d137a512563e106b/86ddf69cfc68919fb0f708eb190c18fdc2b9479a-1999x1200.png" alt="AutoOps Deployment view for an Elasticsearch cluster showing events over time, open events list and resource metrics including JVM memory, CPU and storage across hot and cold tiers" /><h2>Why AutoOps for Elasticsearch needs clearer prioritization</h2><p>Running Elasticsearch at scale requires administrators to monitor cluster health, performance, capacity, and configuration at the same time. AutoOps now provides a clearer way to distinguish conditions that threaten cluster functionality from significant but less urgent degradation. The redesigned interface also follows familiar Elastic Cloud Console patterns, making active issues easier to find and investigate.</p><h2>What changed in AutoOps: severity, navigation, configuration, and page design</h2><p>The monitoring engine remains the same. The redesigned layout, navigation, and workflows now follow familiar Elastic patterns.</p><h3>A clearer severity model</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd9cd94b620ce367/6a6a33c70a222b3af3877f27/0304e67b9028d72ea31408712e6e778c866edfac-1780x632.png" alt="AutoOps events over time heatmap showing Critical Status Red, High Cluster Pending Tasks and Medium severity events including Unbalanced Shards and Template Optimization across an Elasticsearch deployment over 10 days" /><p>We added <strong>Critical</strong> as a new severity level for conditions that pose an immediate threat to cluster functionality and require urgent intervention. Several events previously classified as High are now Critical. Others are now Medium because they represent potential risk rather than active, significant degradation. The reclassified events are:</p><ul><li><p><strong>Promoted from High to Critical:</strong> Disk Watermark Flood Stage, Master Not Discovered, and Status Red.</p></li><li><p><strong>Demoted from High to Medium:</strong> Disk Watermark Low Threshold, Disk Watermark Low, and Disk Watermark Configuration Incorrect.</p></li></ul><p>Severity</p><p>What it means</p><p>Critical</p><p>Immediate threat to cluster functionality. Urgent intervention required.</p><p>High</p><p>Significant degradation to usability, performance, or stability.</p><p>Medium</p><p>Potential risk that can escalate if left unaddressed.</p><p>Low</p><p>Minor anomalies with minimal operational impact.</p><p>Info</p><p>Routine operational updates and configuration changes. No action required. (Coming in a near-future update).</p><p>Every severity level ships with an updated icon set and color palette. Levels are fixed so teams can build consistent runbooks and notification filters: route Critical and High events to PagerDuty or Slack, keep Medium and Low in the console for periodic review, and when Info arrives, use it for awareness without alert fatigue.</p><h3>Deployment view: open events and history, side by side</h3><p>The redesigned deployment view presents the existing Open events and Event history tabs in a clearer layout.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4e5132e8519703e1/6a6a33c88c87dc30dc0d0678/54a6c72a7024dc46a6c458d801d85a066fd3af8f-1780x1664.png" alt="AutoOps Deployment view showing the Event history tab with an events over time heatmap for Critical, High and Medium Elasticsearch events including Status Red, Data Node Disconnected and Index Queue Size" /><h3>Event flyout: a clearer view of what matters</h3><p>The event detail flyout is redesigned around action. High-severity events include a notification callout and an interactive badge that shows whether alerts are configured and links directly to setup. Recommendations collapse by default so the core event stays in focus. Settings live in the flyout menu; share is a separate icon in the header. The Dismiss action appears only when your role has the required admin permissions and the event is dismissible.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18f642f08729fef9/6a6a33c940a4941189ca5c96/c275a87fc7ba3125599f7be5dfa170915aa0c567-1999x1202.png" alt="AutoOps Deployment view showing an open High severity event flyout for a high index queue on an Elasticsearch node, with recommendations and event timeline" /><h3>AutoOps overview: triage active events across your Elasticsearch fleet</h3><p>The Overview page is reorganized around how operators scan an estate. Elasticsearch context sits directly under the page header, and active events appear as <strong>event ribbons</strong> below the deployments table. Each ribbon shows the latest active event in your selected time range; if the same event type is open on other deployments, a new badge lets you expand the view without opening each resource individually. Event search moved to the left for quicker filtering.</p><p>The “Events over time” chart moved off Overview to keep this page focused on fleet-level triage; open a single deployment when you need that timeline.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc8fbe2571e164c56/6a6a33cad57c1d09d8c13ee4/f73d8b413c66662f890232ecfc57383be35c3203-1999x1202.png" alt="AutoOps Overview page showing a fleet of 7 Elasticsearch deployments with ES status, priority events, node and shard counts, and a Top events list filtered by Critical, High and Medium severity" /><h3>Nodes, Shards, and Indices are designed with easier navigation and information hierarchy</h3><p><strong>Nodes view</strong> now uses updated chart components and the Elastic UI color scheme, with clear expansion indicators on accordion sections. Event and instance lists that duplicated deployment-level views were removed to reduce noise.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56b42b5dd910a347/6a6a33cb820dedb2ad12936a/dbbdca29d142a23e46acf5ed2c150cdfc18a2ba3-1999x1203.png" alt="AutoOps Nodes view for an Elasticsearch deployment showing disk usage, shards count, segments count, and documents count charts across 24 nodes over a two-day period" /><p><strong>Shards view </strong>improves node selection and groups view controls in the upper-right corner. A horizontal scrollbar supports wider layouts, and the time slider now uses native Elastic UI components. Node selection in Shards view now works across larger clusters and presents up to 100 nodes at a time.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt40d54785dc72234b/6a6a33cc776a4d7a5b51ddc5/a00dc567f48eefa06839ba294e5592a41904ba8c-1999x1202.png" alt="AutoOps Shards view for an Elasticsearch cluster showing hot and cold tier nodes with an indexing rate tooltip for a specific index on instance-181, displaying 3K/sec indexing rate and 56 million documents" /><p><strong>Index view</strong> keeps the Indices table experience you already use, including sorting, time-range brushing, and chart zoom behavior tuned for meaningful ranges.</p><h3>Template Optimizer</h3><p>The <a href="https://www.elastic.co/guide/en/cloud/current/ec-autoops-template-optimizer.html">Template Optimizer</a> now provides a searchable list of templates ordered by the most recently identified recommendations. You can open each recommendation directly or expand the JSON panel to inspect the complete template.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b0f64ce58e9783e/6a6a33cd065b160c08701ff5/22e5371e3174233cfe942094cca3dac9444a3550-1999x1202.png" alt="AutoOps Template Optimizer showing a codec compression recommendation alongside the full JSON template configuration for autoops_standard_index_settings" /><h3>Configure notifications and event settings</h3><p>Notification settings now include connector search, clearer filters, and a simpler connector editing flow. Event settings moved from a popup to a flyout, matching the pattern used across AutoOps. Notification reports retain the same 10-day history window with minor layout updates, and dismiss events use updated confirmation components aligned with Elastic UI.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8d711e0f3814e95/6a6a33cec9699ab1cef4b1c8/11a3cb4646f1d1efa2a384b8ee82b6b736aede29-1999x1203.png" alt="AutoOps Events settings page showing the Edit event settings flyout with index filter pattern, empty indices threshold, and per-deployment configuration options" /><h3>Navigation and controls</h3><p>The deployment picker now shows deployment ID and real-time cluster status, with copy actions for deployment name and ID in the dropdown sub-menu. Node selection supports select-all, select-by-tier grouping, and clear master node indication. The date picker follows the same relative-range and custom-range model used in Kibana and other Cloud Console monitoring views.</p><h2>AutoOps roadmap: API, MCP, CLI, and agentic experience</h2><p>Looking ahead, we are building toward a headless, agentic AutoOps experience. A forthcoming public <a href="https://github.com/elastic/roadmap/issues/144">AutoOps API </a>will make insights and raw metrics available outside the AutoOps interface. Administrators and agents will be able to query the API directly or store its data in Elasticsearch. The API will also provide the foundation for integrations with MCP, Elastic Agent Builder, the Elastic CLI, Kibana, and native AutoOps chat.</p><ul><li><p><strong>Hosted MCP server: </strong>Make AutoOps insights available to MCP clients such as Claude and Cursor.</p></li><li><p><strong>Native Elastic Agent Builder tool</strong>: Use AutoOps insights in Elastic Agent Builder.</p></li><li><p><strong>Elastic CLI support:</strong> Access the AutoOps API through the Elastic CLI.</p></li><li><p><strong>AutoOps in Kibana:</strong> Surface relevant insights and metrics within Kibana.</p></li><li><p><strong>Native AutoOps chat</strong>: Investigate cluster issues through an agentic chat experience within AutoOps UI in Elastic Cloud Console.</p></li></ul><p>The application redesign is the foundation; these surfaces will meet operators where automation and AI already live. Read more about what is coming on the <a href="https://github.com/orgs/elastic/projects/2066/views/2?sliceBy%5Bvalue%5D=Monitoring+and+diagnostics">Elastic public roadmap</a>.</p><h2>How to start using the redesigned AutoOps in Elastic Cloud Console</h2><p>Sign in to <a href="https://cloud.elastic.co">Elastic Cloud Console</a>, open a deployment, project, or connected cluster, and select <strong>AutoOps</strong> from the navigation. Learn more in the <a href="https://www.elastic.co/guide/en/cloud/current/ec-autoops.html">AutoOps documentation</a>.</p><p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/autoops-elasticsearch-cluster-monitoring-redesigned</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/autoops-elasticsearch-cluster-monitoring-redesigned</guid>
    <category><![CDATA[AutoOps]]></category>
    <category><![CDATA[Elastic Cloud Hosted]]></category>
    <category><![CDATA[Operations]]></category>
    <dc:creator><![CDATA[Ori Shafir,Arnon Stern]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd9be3378b7b18d00/6a6a33c6d137a512563e106b/86ddf69cfc68919fb0f708eb190c18fdc2b9479a-1999x1200.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Training LTR models in Elasticsearch with judgement lists based on user behavior data]]></title>
    <description><![CDATA[Learn how to use UBI data to create judgment lists to automate the training of your Learning to Rank (LTR) models in Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>A big challenge when using <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr"><em><strong>Learning-to-rank</strong></em></a> models is to create a high-quality <a href="https://www.elastic.co/search-labs/blog/judgment-lists"><em><strong>judgment list</strong></em></a> to train the model on. Traditionally, this process involves a <em><strong>manual</strong></em> evaluation of query-document relevance to assign a grade to each one. This is a slow process that does not scale well and is hard to maintain (imagine having to update a list with hundreds of entries by hand).</p><p>Now, what if we could use real user interactions with our search application to create this training data? Using <a href="https://www.elastic.co/search-labs/blog/elasticsearch-plugin-user-behavior-insights"><em><strong>UBI</strong></em></a> data lets us do just that. Creating an automatic system that can capture and use our searches, clicks, and other interactions to generate a judgment list. This process can scale and be repeated far more easily than a manual interaction and would tend to yield better results. In this blog, we will explore how we can query UBI data stored in Elasticsearch to calculate meaningful signals to generate a training dataset for an <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction"><em><strong>LTR</strong></em></a> model.</p><p><em><strong>You can find the full experiment </strong></em><a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git"><em><strong>here</strong></em></a><em><strong>.</strong></em></p><h2>Why UBI data can be useful to train your LTR model</h2><p>UBI data offers several advantages over a manual annotation:</p><ul><li><p><strong>Volume:</strong> Given that UBI data comes from real interactions, we can collect much more data than we can generate manually. This is assuming we have enough traffic to generate this data, of course.</p></li><li><p><strong>Real User intent:</strong> Traditionally, a manual judgment list comes from an expert evaluation of the available data. On the other hand, UBI data reflects real user behavior. This means we can generate better training data that will improve our search system's accuracy, because it's based on how users actually interact with and find value in your content rather than theoretical assumptions about what should be relevant.</p></li><li><p><strong>Continuous updates:</strong> Judgment lists need to be refreshed over time. If we create them from UBI data, we can have current data that results in updated judgment lists.</p></li><li><p><strong>Cost effectiveness:</strong> Without the overhead of manually creating a judgment list, the process can be repeated efficiently any number of times.</p></li><li><p><strong>Natural query distribution</strong>: UBI data represent real user queries, which can drive deeper changes. For example, do our users use natural language to search in our system? If so, we might want to implement a semantic search or hybrid search approach.</p></li></ul><p>It does come with some warnings, though:</p><ul><li><p><strong>Bias amplification: </strong>Popular content is more likely to receive clicks, just because it gets more exposure. So this might end up amplifying popular items and possibly drowning out better options.</p></li><li><p><strong>Incomplete coverage: </strong>New content lacks any interactions, so it might be difficult for it to be high in the results. Rare queries can also lack sufficient data points to create meaningful training data.</p></li><li><p><strong>Seasonal variations:</strong> If you expect user behaviour to change drastically over time, historical data might not tell you much about what is a good result.</p></li><li><p><strong>Task ambiguity:</strong> A click doesn’t always guarantee that the user found what they were looking for.</p></li></ul><h2>Grades calculation</h2><h3>Grades for LTR training</h3><p>To train LTR models, we need to provide some numerical representation of how relevant a document is for a query. In our implementation, this number is a continuous score going from 0.0 to 5.0+, where higher scores indicate higher relevance.</p><p>To show how this grading system works, consider this manually created example:</p><p>Query</p><p>Document content</p><p>Grade</p><p>Explanation</p><p>"best pizza recipe"</p><p>"Authentic Italian Pizza Dough Recipe with Step-by-Step Photos"</p><p>4.0</p><p>Highly relevant, exactly what the user is looking for </p><p>"best pizza recipe"</p><p>"History of Pizza in Italy"</p><p>1.0</p><p>Somewhat in topic, it is about pizza but is not a recipe</p><p>"best pizza recipe"</p><p>"Quick 15-Minute Pizza Recipe for Beginners"</p><p>3.0</p><p>Relevant, a good result but it maybe misses the mark on being the “best” recipe. </p><p>"best pizza recipe"</p><p>"Car Maintenance Guide"</p><p>0.0</p><p>Not relevant at all, completely unrelated to the query</p><p>As we can see here, the grade is a numerical representation of how relevant a document is to our sample query of “best pizza recipe”. With these scores, our LTR model can learn which documents should be presented higher in the results.</p><p>How to calculate the grades is the core of our training dataset. There are <a href="https://www.elastic.co/search-labs/blog/judgment-lists">multiple approaches</a> to do this, each with its own strengths and weaknesses. For example, we could assign a binary score of 1 for relevant 0 for not relevant or we could just count the number of clicks in a resulting document for each query.</p><p>In this blog post, we will be using a different approach, <em><strong>taking into account the user behavior as our input and calculating a grade number as the output</strong></em>. We will also be correcting bias that could occur from the fact that higher results tend to be more clicked, regardless of the relevancy of the document.</p><h2>Calculating the grades - COEC algorithm</h2><p>The COEC (<a href="https://www.wsdm-conference.org/2010/proceedings/docs/p351.pdf">Clicks over Expected Clicks</a>) algorithm is a methodology for calculating judgment grades from user clicks.
As we stated earlier, users tend to click on higher-positioned results even if the document is not the most relevant to the query; this is called <a href="https://eugeneyan.com/writing/position-bias/">Position Bias</a>. The core idea for using the COEC algorithm is that not all clicks are equally significant; a click on a document at position 10 indicates that the document is much more relevant to the query than a click on a document at position 1. To quote the research paper about the COEC algorithm (linked above):</p><p><em>“It is well known that the click-through rate (CTR) of search results or advertisements decreases significantly depending on the position of the results.”</em></p><p>You can further read about position bias <a href="https://www.researchgate.net/publication/200110550_An_experimental_comparison_of_click_position-bias_models">here</a>.</p><p>To address this with the COEC algorithm, we follow these steps:</p><p><strong>1. Establish position baselines:</strong> We calculate the click-through rate (CTR) for each search position from 1 to 10. This means we determine what percentage of users typically click on position 1, position 2, and so on. This step captures the users’ natural position bias.

We calculate the CTR using:Where:</p><p> = Position. From 1 to 10</p><p>
= Total clicks (on any document) at position p across all queries</p><p>
 = Total impressions: How many times any document appeared at the position p across all queries</p><p>Here, we expect higher positions to get more clicks.</p><p></p><p><strong>2.</strong> <strong>Calculate Expected Clicks (EC)</strong>:</p><p>This metric establishes how many clicks a document “should” have received based on the positions it appeared in and the CTR for those positions We calculate EC using:Where:</p><p> = All queries where the document d appeared</p><p>
= Position of the document d in the query q results</p><p></p><p>3. <strong>Count actual clicks: </strong>We count the actual total clicks a document received across all queries where it appeared, hereafter called <strong>A(d).</strong></p><p></p><p>4. <strong>Compute the COEC score:</strong> This is the ratio of Actual clicks (A(d)) over the Expected clicks (EC(d)):This metric normalizes for position bias like this:</p><ul><li><p>A score of 1.0 means the document performed exactly as expected given the positions it appeared in.</p></li><li><p>A score above 1.0 means the document performed better than expected by looking at its positions. So this document is more relevant for the query.</p></li><li><p>A score under 1.0 means the document performed worse than expected by looking at its positions. So this document is less relevant for the query.</p></li></ul><p><em><strong>The end result is a grade number that captures what users are looking for, taking into account position-based expectations extracted from real interactions with our search system.</strong></em></p><h2>Technical implementation</h2><p>We will be creating a script to create a judgment list to train an LTR model.</p><p>The input for this script is the UBI data indexed in Elastic (queries and events).</p><p>The output is a judgment list in a CSV file generated from these UBI documents using the COEC algorithm. This judgment list can be used with <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction">Eland</a> to extract relevant features and train an LTR model.</p><h3>Quick start</h3><p>To generate a judgment list from the sample data in this blog, you can follow these steps:</p><p>1. Clone the repository:</p>git clone https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git  
cd elastic-ltr-judgement_list-blog<p>2. Install required libraries</p><p>For this script, we need the following libraries:</p><ul><li><p><em>pandas</em>: to save the judgment list</p></li><li><p><em>elasticsearch</em>: To get the UBI data from our Elastic deployment</p></li></ul><p>We also need Python 3.11</p>pip install -r requirements.txt<p>3. Update the environment variables for your Elastic deployment in a <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/.env-example">.env file</a></p><ul><li><p>ES_HOST</p></li><li><p>API_KEY</p></li></ul><p>To add the environment variables, use:</p>source .env<p>4. Create the ubi_queries, ubi_events indices, and upload the sample data. Run the setup.py file:</p>python setup.py<p>5. Run the Python script:</p>python judgement_list-generator.py<p>If you follow these steps, you should see a new file called judgment_list.csv that looks like this:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94317eda8f7af194/6a170aa46f7f04542f914821/2531090131ac9fe3e4e1d79de9d156fc47a7825a-782x531.png" alt="" /><p>This script calculates the grades applying the COEC algorithm discussed before using the <strong>calculate_relevance_grade()</strong> function that is shown below.</p><h2>Data architecture</h2><h3>Ubi queries</h3><p>Our UBI queries index has information about the queries executed in our search system. This is a sample document:</p>{
          "client_id": "client_002",
          "query": "italian pasta recipes",
          "query_attributes": {
            "search_type": "recipe",
            "category": "food",
            "cuisine": "italian"
          },
          "query_id": "q002",
          "query_response_id": "qr002",
          "query_response_object_ids": [
            "doc_011",
            "doc_012",
            "doc_013",
            "doc_014",
            "doc_015",
            "doc_016",
            "doc_017",
            "doc_018",
            "doc_019",
            "doc_020"
          ],
          "timestamp": "2024-08-14T11:15:00Z",
          "user_query": "italian pasta recipes"
        }<p>Here we can see data from the user (client_id), from the results of the query (query_response_object_ids), and the query itself (timestamp, user_query)</p><h3>Ubi click events</h3><p>Our ubi_events index has data from each time a user clicked a document in the results. This is a sample document:</p>{
          "action_name": "click",
          "application": "recipe_search",
          "client_id": "client_001",
          "event_attributes": {
            "object": {
              "description": "Authentic Italian Pizza Dough Recipe with Step-by-Step Photos",
              "device": "desktop",
              "object_id": "doc_001",
              "position": {
                "ordinal": 1,
                "page_depth": 1
              },
              "user": {
                "city": "New York",
                "country": "USA",
                "ip": "192.168.1.100",
                "location": {
                  "lat": 40.7128,
                  "lon": -74.006
                },
                "region": "NY"
              }
            }
          },
          "message": "User clicked on document doc_001",
          "message_type": "click",
          "query_id": "q001",
          "timestamp": "2024-08-14T10:31:00Z",
          "user_query": "best pizza recipe"
        }<h2>Judgment list generation script</h2><h3>General script overview</h3><p>This script automates the generation of the judgment list using UBI data from Queries and Click events stored in Elasticsearch. It executes these tasks:</p><ul><li><p>Fetches and processes the UBI data in Elasticsearch.</p></li><li><p>Correlates UBI events with its queries.</p></li><li><p>Calculates the CTR for each position.</p></li><li><p>Calculates the expected clicks (EC) for each document.</p></li><li><p>Counts the actual clicks for each document.</p></li><li><p>Calculates the COEC score for each query-document pair.</p></li><li><p>Generates a judgment list and writes it in a CSV file.</p></li></ul><p>Let’s go over each function:</p><h3>connect_to_elasticsearch()</h3>def connect_to_elasticsearch(host, api_key):
    """Create and return Elasticsearch client"""
    try:
        es = Elasticsearch(
            hosts=[host],
            api_key=api_key,
            request_timeout=60
        )
        # Test the connection
        if es.ping():
            print(f"✓ Successfully connected to Elasticsearch at {host}")
            return es
        else:
            print("✗ Failed to connect to Elasticsearch")
            return None
    except Exception as e:
        print(f"✗ Error connecting to Elasticsearch: {e}")
        return None<p>This function returns an Elasticsearch client object using the host and api key.</p><h3>fetch_ubi_data()</h3>def fetch_ubi_data(es_client: Elasticsearch, queries_index: str, events_index: str,
                   size: int = 10000) -&gt; Tuple[List[Dict], List[Dict]]:
    """
    Fetch UBI queries and events data from Elasticsearch indices.

    Args:
        es_client: Elasticsearch client
        queries_index: Name of the UBI queries index
        events_index: Name of the UBI events index
        size: Maximum number of documents to fetch

    Returns:
        Tuple of (queries_data, events_data)
    """
    logger.info(f"Fetching data from {queries_index} and {events_index}")

    # Fetch queries with error handling
    try:
        queries_response = es_client.search(
            index=queries_index,
            body={
                "query": {"match_all": {}},
                "size": size
            }
        )
        queries_data = [hit['_source'] for hit in queries_response['hits']['hits']]
        logger.info(f"Fetched {len(queries_data)} queries")

    except Exception as e:
        logger.error(f"Error fetching queries from {queries_index}: {e}")
        raise

    # Fetch events (only click events for now) with error handling
    try:
        events_response = es_client.search(
            index=events_index,
            body={
                "query": {
                    "term": {"message_type.keyword": "CLICK_THROUGH"}
                },
                "size": size
            }
        )
        events_data = [hit['_source'] for hit in events_response['hits']['hits']]
        logger.info(f"Fetched {len(events_data)} click events")

    except Exception as e:
        logger.error(f"Error fetching events from {events_index}: {e}")
        raise

    logger.info(f"Data fetch completed successfully - Queries: {len(queries_data)}, Events: {len(events_data)}")

    return queries_data, events_data<p>This function is the data extraction layer; it connects with Elasticsearch to fetch UBI queries using a match_all query and filters UBI events to get ‘CLICK_THROUGH’ events only.</p><h3>process_ubi_data()</h3>def process_ubi_data(queries_data: List[Dict], events_data: List[Dict]) -&gt; pd.DataFrame:
    """
    Process UBI data and generate judgment list.

    Args:
        queries_data: List of query documents from UBI queries index
        events_data: List of event documents from UBI events index

    Returns:
        DataFrame with judgment list (qid, docid, grade, keywords)
    """
    logger.info("Processing UBI data to generate judgment list")

    # Group events by query_id
    clicks_by_query = {}
    for event in events_data:
        query_id = event['query_id']
        if query_id not in clicks_by_query:
            clicks_by_query[query_id] = {}

        # Extract clicked document info
        object_id = event['event_attributes']['object']['object_id']
        position = event['event_attributes']['object']['position']['ordinal']

        clicks_by_query[query_id][object_id] = {
            'position': position,
            'timestamp': event['timestamp']
        }

    judgment_list = []

    # Process each query
    for query in queries_data:
        query_id = query['query_id']
        user_query = query['user_query']
        document_ids = query['query_response_object_ids']

        # Get clicks for this query
        query_clicks = clicks_by_query.get(query_id, {})

        # Generate judgment for each document shown
        for doc_id in document_ids:
            grade = calculate_relevance_grade(doc_id, query_clicks, document_ids, queries_data, events_data)

            judgment_list.append({
                'qid': query_id,
                'docid': doc_id,
                'grade': grade,
                'query': user_query
            })

    df = pd.DataFrame(judgment_list)
    logger.info(f"Generated {len(df)} judgment entries for {df['qid'].nunique()} unique queries")

    return df<p>This function handles the judgment list generation. It starts processing the UBI data by associating UBI events and queries. Then it calls the calculate_relevance_grade() function for each document-query pair to obtain the entries for the judgment list. Finally, it returns the resulting list as a pandas dataframe.</p><h3>calculate_relevance_grade()</h3>def calculate_relevance_grade(document_id: str, clicks_data: Dict,
                              query_response_ids: List[str], all_queries_data: List[Dict] = None,
                              all_events_data: List[Dict] = None) -&gt; float:
    """
    Calculate COEC (Click Over Expected Clicks) relevance score for a document.

    Args:
        document_id: ID of the document
        clicks_data: Dictionary of clicked documents with their positions for current query
        query_response_ids: List of document IDs shown in search results (ordered by position)
        all_queries_data: All queries data for calculating position CTR averages
        all_events_data: All events data for calculating position CTR averages

    Returns:
        COEC relevance score (continuous value, typically 0.0 to 5.0+)
    """

    # If no global data provided, fall back to simple position-based grading
    if all_queries_data is None or all_events_data is None:
        logger.warning("No global data provided, falling back to position-based grading")
        # Simple fallback logic
        if document_id in clicks_data:
            position = clicks_data[document_id]['position']
            if position &gt; 3:
                return 4.0
            elif position &gt;= 1 and position &lt;= 3:
                return 3.0
        if document_id in query_response_ids:
            position = query_response_ids.index(document_id) + 1
            if position &lt;= 5:
                return 2.0
            elif position &gt;= 6 and position &lt;= 10:
                return 1.0
        return 0.0

    # Calculate rank-aggregated click-through rates
    position_ctr_averages = {}
    position_impression_counts = {}
    position_click_counts = {}

    # Initialize counters
    for pos in range(1, 11):  # Positions 1-10
        position_impression_counts[pos] = 0
        position_click_counts[pos] = 0

    # Count impressions (every document shown contributes)
    for query in all_queries_data:
        for i, doc_id in enumerate(query['query_response_object_ids'][:10]):  # Top 10 positions
            position = i + 1
            position_impression_counts[position] += 1

    # Count clicks by position
    for event in all_events_data:
        if event.get('action_name') == 'click':
            position = event['event_attributes']['object']['position']['ordinal']
            if position &lt;= 10:
                position_click_counts[position] += 1

    # Calculate average CTR per position
    for pos in range(1, 11):
        if position_impression_counts[pos] &gt; 0:
            position_ctr_averages[pos] = position_click_counts[pos] / position_impression_counts[pos]
        else:
            position_ctr_averages[pos] = 0.0

    # Calculate expected clicks for this specific document
    expected_clicks = 0.0

    # Count how many times this document appeared at each position for any query
    for query in all_queries_data:
        if document_id in query['query_response_object_ids']:
            position = query['query_response_object_ids'].index(document_id) + 1
            if position &lt;= 10:
                expected_clicks += position_ctr_averages[position]

    # Count total actual clicks for this document across all queries
    actual_clicks = 0
    for event in all_events_data:
        if (event.get('action_name') == 'click' and
                event['event_attributes']['object']['object_id'] == document_id):
            actual_clicks += 1

    # Calculate COEC score
    if expected_clicks &gt; 0:
        coec_score = actual_clicks / expected_clicks
    else:
        coec_score = 0.0

    logger.debug(
        f"Document {document_id}: {actual_clicks} clicks / {expected_clicks:.3f} expected = {coec_score:.3f} COEC")

    return coec_score<p>This is the function that implements the COEC algorithm. It calculates the CTR for each position, then it compares the actual clicks for a document-query pair, and finally calculates the actual COEC score for each one.</p><h3>generate_judgment_statistics()</h3>def generate_judgment_statistics(df: pd.DataFrame) -&gt; Dict:
    """Generate statistics about the judgment list."""
    stats = {
        'total_judgments': len(df),
        'unique_queries': df['qid'].nunique(),
        'unique_documents': df['docid'].nunique(),
        'grade_distribution': df['grade'].value_counts().to_dict(),
        'avg_judgments_per_query': len(df) / df['qid'].nunique() if df['qid'].nunique() &gt; 0 else 0,
        'queries_with_clicks': len(df[df['grade'] &gt; 1]['qid'].unique()),
        'click_through_rate': len(df[df['grade'] &gt; 1]) / len(df) if len(df) &gt; 0 else 0
    }
    return stats<p>It generates useful statistics from the judgment list, such as total queries, total unique documents, or the grade distribution. This is purely informational and does not change the resulting judgment list.</p><h2>Results and impact</h2><p>If you follow the instructions in the Quick start section, you should see a resulting CSV file containing a judgment list with 320 entries (you can see a <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/judgment_list.csv">sample output</a> in the repo). With these fields:</p><ul><li><p>qid: unique ID of the query</p></li><li><p>docid: unique identifier for a resulting document</p></li><li><p>grade: the calculated grade for the query-document pair</p></li><li><p>query: The user query</p></li></ul><p> Let’s look at the results for the query “Italian recipes”:</p><p>qid</p><p>docid</p><p>grade</p><p>query</p><p>q1-italian-recipes</p><p>recipe_pasta_basics</p><p>0.0</p><p>Italian recipes</p><p>q1-italian-recipes</p><p>recipe_pizza_margherita</p><p>3.333333</p><p>Italian recipes</p><p>q1-italian-recipes</p><p>recipe_risotto_guide</p><p>10.0</p><p>Italian recipes</p><p>q1-italian-recipes</p><p>recipe_french_croissant</p><p>0.0</p><p>Italian recipes</p><p>q1-italian-recipes</p><p>recipe_spanish_paella</p><p>0.0</p><p>Italian recipes</p><p>q1-italian-recipes</p><p>recipe_greek_moussaka</p><p>1.875</p><p>Italian recipes</p><p>We can see from the results that for the query “Italian recipes”:</p><ul><li><p>The risotto recipe is definitely the best result for the query, receiving 10 times more clicks than expected</p></li><li><p>Pizza Margherita is a great result too.</p></li><li><p>The Greek mousaka (surprisingly) is a good result as well and performs better than its position on the results would suggest. This means a few users looking for Italian recipes got interested in this recipe instead. Maybe these users are interested in Mediterranean dishes in general. At the end, what this tells us is that this could be a good result to be shown under the other two ‘better’ matches we discussed above.</p></li></ul><h2>Conclusion</h2><p>Using UBI data lets us automate the training of LTR models, creating high-quality judgment lists from our own users. UBI data provides a big dataset that reflects how our search system is being used.By using the COEC algorithm to generate the grades, we account for inherent bias while at the same time, it reflects what a user considers a better result. The method outlined here can be applied to real use cases to provide a better search experience that evolves with real usage trends.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/training-learning-to-rank-models-elasticsearch-ubi-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/training-learning-to-rank-models-elasticsearch-ubi-data</guid>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[Elastic Cloud Hosted]]></category>
    <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt037eb2f4d380fe65/6a170aa67d8d67397170e6e6/762bf09c28829d626d42c2cfadc719e1dd618d1b-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Cloud adds Elasticsearch Vector Database optimized instance to Google Cloud]]></title>
    <description><![CDATA[Elasticsearch's vector search optimized profile for GCP is available. Learn more about it and how to use it in this blog.]]></description>
    <content:encoded><![CDATA[<p>Elastic Cloud Vector Search optimized hardware profile is available for Google Elastic Cloud users. This hardware profile is optimized for applications that require the storage of dense or sparse embeddings for search and Generative AI use cases powered by RAG (retrieval augmented generation). This release follows the previous release of a Vector Search optimized hardware profile for AWS Elastic Cloud users in Nov 2023.</p><h2>GCP Vector Search optimized instances: what you need to know</h2><p>Elastic Cloud users benefit from having Elastic managed infrastructure across all major cloud providers (GCP, AWS and Azure) along with <a href="https://www.elastic.co/guide/en/cloud/current/ec-regions-templates-instances.html">wide region support</a> for GCP users. For more specific details on the instance configuration for this hardware profile, refer to our documentation for instance type: <a href="https://www.elastic.co/guide/en/cloud/current/ec-default-gcp-configurations.html">gcp.es.datahot.n2d.64x8x11</a></p><h2>Vector Search, HNSW, and memory</h2><p>Elasticsearch uses the <a href="https://www.elastic.co/search-labs/blog/vector-search-elasticsearch-rationale">Hierarchical Navigable Small World</a> graph (HNSW) data structure to implement its Approximate Nearest Neighbor search (ANN). Because of its layered approach, HNSW's hierarchical aspect offers excellent query latency. To be most performant, HNSW requires the vectors to be cached in the node's memory. This caching is done automatically and uses the available RAM not taken up by the Elasticsearch JVM. Because of this, memory optimizations are important steps for scalability.</p><p>Consult our vector search <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-knn-search.html#_ensure_data_nodes_have_enough_memory">tuning guide</a> to determine the right setup for your vector search embeddings and whether you have adequate memory for your deployment.</p><p>With this in mind, the Vector Search optimized hardware profile is configured with a smaller than standard Elasticsearch JVM heap setting. This provides more RAM for caching vectors on a node, allowing users to provision fewer nodes for their vector search use cases.</p><p>If you’re using compression techniques like <a href="https://www.elastic.co/search-labs/blog/scalar-quantization-in-lucene">scalar quantization</a>, the memory requirement is lowered by a factor of 4. To store quantized embeddings (available in versions Elasticsearch 8.12 and later) simply ensure that you’re storing in the correct <code>element_type: byte</code>. To utilize our <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">automatic quantization</a> of <code>float</code> vectors update your embeddings to use index type: <code>int8_hnsw</code> like in the following mapping example.</p>PUT my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 512,
        "index_options": {
          "type": "int8_hnsw"
        }
      }
    }
  }
}
<p>In upcoming versions, Elasticsearch will provide this as the default mapping, removing the need for users to adjust their mapping.</p><p>Combining this optimized hardware profile with Elasticsearch’s <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">automatic quantization</a> are two examples where Elastic is focused on vector search to be cost-effective while still being extremely performant.</p><h2>Getting Started with Elastic Cloud vector search optimized profile for GCP</h2><p>Start a <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">free trial</a> on Elastic Cloud and simply select the new Vector Search optimized profile to get started.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a33219437748674/6a17d7823e9e458302ba12de/c0434f399ee75c99b290060d7b0e613cbcd0829b-1440x1390.png" alt="cloud UI view for new deployments" /><h2>Migrating existing Elastic Cloud deployments</h2><p>Migrating to this new Vector Search optimized hardware profile is a few clicks away. Simply navigate to your Elastic Cloud management UI, click to manage the specific deployment, and edit the hardware profile. In this example, we are migrating from a ‘Storage optimized’ profile to the new ‘Vector Search’ optimized profile. When choosing to do so, while there is a reduction to available storage and vCPU, what is gained is the ability to store more vectors per memory with vector search.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18139bf4f13d62da/6a17d7843e9e4537e0ba12e2/f13962f914d5d9a3be765bde2ac95a9e2d797d3f-1440x561.png" alt="cloud UI view for migrating deployments" /><p>Migrating to a new hardware profile uses the grow and shrink approach for deployment changes. This approach adds new instances, migrates data from old instances to the new ones, and then shrinks the deployment by removing the old instances. This approach allows for high availability during configuration changes even for single availability zones.</p><p>The following image shows a typical architecture for a deployment running in Elastic Cloud, where vector search will be the primary use case.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaac029d716db095/6a17d785e9ea874ffca9c421/58e00f32bef1411dbc11849a78b5ecd3c334528a-1440x570.png" alt="deployment view" /><p>This example deployment uses our new Vector Search optimized hardware profile, now available in GCP. This setup includes:</p><ul><li><p>Two data nodes in our hot tier with our vector search profile</p></li><li><p>One Kibana node</p></li><li><p>One Machine Learning node</p></li><li><p>One integration server</p></li><li><p>One master tiebreaker</p></li></ul><p>By deploying these two “full-sized” data nodes with the Vector Search optimized hardware profile and while taking advantage of Elastic’s automatic dense vector <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-quantization">scalar quantization</a>, you can index roughly 60 million vectors, including one replica (with 768 dimensions).</p><h2>Conclusion</h2><p>Vector search is a powerful tool when building modern search applications, be it for semantic document retrieval on its own or integrating with an LLM service provider in a <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">RAG setup</a>. Elasticsearch provides a full-featured vector database natively integrated with a full-featured search platform. Along with improving vector search feature set and usability, Elastic continues to improve scalability. The vector search node type is the latest example, allowing users to scale their search application.</p><p>Elastic is committed to providing scalable, price effective infrastructure to support enterprise grade search experiences. Customers can depend on us for reliable and easy to maintain infrastructure and cost levers like vector compression, so you benefit from the lowest possible total cost of ownership for building search experiences powered by AI.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-vector-profile-gcp</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-vector-profile-gcp</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Elastic Cloud Hosted]]></category>
    <dc:creator><![CDATA[Serena Chou,Jeff Vestal,Yuvraj Gupta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaac029d716db095/6a17d785e9ea874ffca9c421/58e00f32bef1411dbc11849a78b5ecd3c334528a-1440x570.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>