<?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>Forem: Erik Hatcher</title>
    <description>The latest articles on Forem by Erik Hatcher (@erikhatcher).</description>
    <link>https://forem.com/erikhatcher</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%2F1252881%2F0a95c74a-a4b3-44b9-8e18-6ac0fcf1df52.jpeg</url>
      <title>Forem: Erik Hatcher</title>
      <link>https://forem.com/erikhatcher</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/erikhatcher"/>
    <language>en</language>
    <item>
      <title>Enriching the Search Experience</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Tue, 11 Nov 2025 13:56:51 +0000</pubDate>
      <link>https://forem.com/mongodb/enriching-the-search-experience-2gg2</link>
      <guid>https://forem.com/mongodb/enriching-the-search-experience-2gg2</guid>
      <description>&lt;p&gt;Welcome to a series on enriching the search experience by using &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/view-support/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=enrichment_recipes&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Views with MongoDB Search&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;MongoDB lexical and vector indexes are built directly from the data in the associated collection. Every document is mapped through an index configuration into one, or more if using &lt;code&gt;embeddedDocuments&lt;/code&gt;, Lucene documents. A mapping determines what fields are indexed and, primarily for string fields, how they are indexed. A mapping only can map what it sees: the fields on each available document. &lt;/p&gt;

&lt;p&gt;There are situations where filtering what documents are indexed is necessary, perhaps when &lt;code&gt;archived&lt;/code&gt; is true. Rather than indexing all documents and filtering them out at $search time, we can simply avoid indexing them altogether. In this case, our index size will only be based on non-archived documents.&lt;/p&gt;

&lt;p&gt;And there's situations where enriching a document before it is indexed can enhance searches, such as indexing the size of an array rather than using a full collection scanning query-time expression, or transforming a boolean into a string to support &lt;a href="https://deploy-preview-15183--mongodb-cloud-docs.netlify.app/docs/atlas/atlas-search/facet/#comparing-updated-and-deprecated-facet-types" rel="noopener noreferrer"&gt;faceting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Index it like you want to &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=enrichment_recipes&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;search&lt;/a&gt; it—check out the recipes in this series to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  First view: Indexing array size
&lt;/h2&gt;

&lt;p&gt;Our first enrichment recipe fits when your application needs to query over the size of an array. Here's a couple of documents:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;_id:&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="err"&gt;values:&lt;/span&gt;&lt;span class="w"&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&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="err"&gt;_id:&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="err"&gt;values:&lt;/span&gt;&lt;span class="w"&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&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;We would like to find all documents that have exactly four &lt;code&gt;values&lt;/code&gt;. We could use a brute force $match aggregation pipeline in this way:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;$match:&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="err"&gt;values:&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="err"&gt;$size:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;That aggregation returns the right results—however, at the expense of a COLLSCAN (collection scan) to evaluate that expression for &lt;em&gt;every&lt;/em&gt; document in the collection. Scale matters—this will be fine to a point, but then it'll be way too slow.&lt;/p&gt;

&lt;p&gt;Rather than compute repeatedly at query time visiting documents that do not match the criteria, it would be way more efficient to have the size of the array as a separate field and index that instead. Sure, we could use the &lt;a href="https://www.mongodb.com/company/blog/building-with-patterns-the-computed-pattern?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=enrichment_recipes&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Computed Pattern&lt;/a&gt;, though that would require our application to compute the size, store it alongside the array, and ensure it is kept in sync. There's an alternative: to compute the size during indexing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an enriched view
&lt;/h2&gt;

&lt;p&gt;First, create a standard view on the &lt;code&gt;docs&lt;/code&gt; collection, called &lt;code&gt;docs_with_sizes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docs_with_sizes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docs&lt;/span&gt;&lt;span class="dl"&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="na"&gt;$addFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;num_values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$values&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is using JavaScript in &lt;code&gt;mongosh&lt;/code&gt;, though you can create a view from other environments as well.&lt;/p&gt;

&lt;p&gt;This view acts as a collection such that &lt;code&gt;db.docs_with_sizes.find({})&lt;/code&gt; returns:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_id:&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="err"&gt;values:&lt;/span&gt;&lt;span class="w"&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="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="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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;num_values:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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="err"&gt;_id:&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="err"&gt;values:&lt;/span&gt;&lt;span class="w"&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="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="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="mi"&gt;4&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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;num_values:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;num_values&lt;/code&gt; field isn't stored in the database—it's computed on the fly.  &lt;/p&gt;

&lt;p&gt;Here's where the powerful part comes in—creating a search index on a view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs_with_sizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSearchIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view_index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mappings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&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;Our &lt;code&gt;view_index&lt;/code&gt; has indexed the view, incorporating the added &lt;code&gt;num_values&lt;/code&gt; field as an indexed, and thus queryable, value.&lt;/p&gt;

&lt;h2&gt;
  
  
  The final result
&lt;/h2&gt;

&lt;p&gt;With this enriched index, we can straightforwardly query on &lt;code&gt;num_values&lt;/code&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="err"&gt;$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="err"&gt;index:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view_index"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
      &lt;/span&gt;&lt;span class="err"&gt;equals:&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="err"&gt;path:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"num_values"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
        &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;...yielding these results:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;_id:&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="err"&gt;values:&lt;/span&gt;&lt;span class="w"&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="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="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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;num_values:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Index what you want to search.  &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/view-support/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=enrichment_recipes&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Using Views with MongoDB Search&lt;/a&gt; enhances and enables a number of interesting use cases that would otherwise be difficult or burdensome to tackle. Add these techniques to your search toolkit. Stay tuned for additional recipes in this series!&lt;/p&gt;

</description>
      <category>search</category>
      <category>indexing</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Playing With Search: Which Clause(s) Matched?</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Mon, 21 Jul 2025 15:33:34 +0000</pubDate>
      <link>https://forem.com/mongodb/which-clause-matched-17oi</link>
      <guid>https://forem.com/mongodb/which-clause-matched-17oi</guid>
      <description>&lt;p&gt;This article is part of a series. Please see the complete series above.&lt;/p&gt;

&lt;p&gt;Here's our &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/669e82e12ce7658e786edc03?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_4&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;playground of the post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this custom scoring trick, clauses that match a query can be determined with a little binary math.&lt;/p&gt;

&lt;p&gt;Given these documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;_id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;_id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red Green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red Green Blue&lt;/span&gt;&lt;span class="dl"&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;Let's search for documents that match any of the terms "red", "green", or "blue" in a way we can determine which ones matched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;compound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;should&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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&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="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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&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="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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&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;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="na"&gt;$addFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;searchScore&lt;/span&gt;&lt;span class="dl"&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;p&gt;This query overrides the score for each clause, setting the score for "red" matches to 1.0, matches of "green" are assigned the score 2.0, and matches for "blue" are scored 4.0. These values are the first values of the sequence of decimal values of binary digits. In binary, 1.0 is represented as &lt;code&gt;001&lt;/code&gt;, 2.0 is &lt;code&gt;010&lt;/code&gt;, and 4.0 is &lt;code&gt;100&lt;/code&gt;. Because each match has a unique digit position, we can reverse engineer the resulting relevancy &lt;code&gt;searchScore&lt;/code&gt; to determine which clauses matched.&lt;/p&gt;

&lt;p&gt;Let's analyze the search results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red Green Blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;score&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red Green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;score&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;score&lt;/span&gt;&lt;span class="dl"&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="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A score of 7.0 is binary &lt;code&gt;111&lt;/code&gt;, meaning every clause matched. The score of 3.0 is binary &lt;code&gt;011&lt;/code&gt;, meaning that the first two ("red" and "green") clauses matched, but not the third ("blue").&lt;/p&gt;

&lt;p&gt;See more about &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/scoring/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_4&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;adjusting the scores&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>search</category>
      <category>index</category>
      <category>programming</category>
    </item>
    <item>
      <title>Playing With Search: Query Term Highlighting</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Tue, 08 Jul 2025 18:05:03 +0000</pubDate>
      <link>https://forem.com/mongodb/highlighting-search-terms-14jh</link>
      <guid>https://forem.com/mongodb/highlighting-search-terms-14jh</guid>
      <description>&lt;p&gt;This article is part of a series. Please see the complete series above.&lt;/p&gt;

&lt;p&gt;Visit the &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/669e6d0cd49ef6fad98118bc?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_3&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;query term highlighting Playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The collection contains this document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Introduction to Atlas Search&lt;/span&gt;&lt;span class="dl"&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;&lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/highlighting/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_3&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Enable highlighting and project the &lt;code&gt;searchHighlights&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;atlas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&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;span class="na"&gt;$project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;highlights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;$meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;searchHighlights&lt;/span&gt;&lt;span class="dl"&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;p&gt;The query searched for the word "atlas", and is shown in context of the full field value in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;highlights&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;score&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.3466932773590088&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;texts&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Introduction to &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Atlas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; Search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&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;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;The application code, when presenting the search results, can concatenate all the &lt;code&gt;value&lt;/code&gt;s, accentuating the &lt;code&gt;hit&lt;/code&gt;s.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Introduction to &lt;strong&gt;Atlas&lt;/strong&gt; Search&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Learn more &lt;a href="https://learn.mongodb.com/learn/course/search-fundamentals-on-demand-devrel-content/search-fundamentals/introduction-to-atlas-search" rel="noopener noreferrer"&gt;search fundamentals&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>mongodb</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Playing With Search: geoWithin</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Thu, 03 Jul 2025 11:30:06 +0000</pubDate>
      <link>https://forem.com/mongodb/playing-with-geowithin-ngo</link>
      <guid>https://forem.com/mongodb/playing-with-geowithin-ngo</guid>
      <description>&lt;p&gt;This article is part of a series. Please see the complete series above.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/669e6b762ce7658e786edbfa?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_2&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;geoWithin playground&lt;/a&gt; for this post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/docs/manual/reference/geojson/#point?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_2&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Geographic points&lt;/a&gt; are annotated on documents (longitude first, then latitude):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;_id&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="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Charlottesville&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;78.47668&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;38.02931&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;GeoJSON objects are not automatically indexed with &lt;code&gt;"dynamic": true&lt;/code&gt; mappings and must be explicitly mapped, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mappings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;location&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geo&lt;/span&gt;&lt;span class="dl"&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;p&gt;Now, the &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/geoWithin/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_2&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;geoWithin&lt;/a&gt; operator can be used to locate points within a shape—in this case, a 100m radius circle centered near the location of our one document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geoWithin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;circle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coordinates&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;78.476&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;38.029&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;radius&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;location&lt;/span&gt;&lt;span class="dl"&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;p&gt;Press &lt;code&gt;Run&lt;/code&gt; on the Playground (linked near the top of this post) and you'll see instead of an empty array &lt;code&gt;[]&lt;/code&gt; that it returns the, one, matching document.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://learn.mongodb.com/learn/course/search-fundamentals-on-demand-devrel-content/search-fundamentals/introduction-to-atlas-search" rel="noopener noreferrer"&gt;search fundamentals&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Playing With Search: Stemming</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Tue, 01 Jul 2025 21:44:57 +0000</pubDate>
      <link>https://forem.com/mongodb/playing-with-stemming-5cad</link>
      <guid>https://forem.com/mongodb/playing-with-stemming-5cad</guid>
      <description>&lt;p&gt;This article is part of a series. Please see the complete series above.&lt;/p&gt;

&lt;p&gt;Let's start with this &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/664739964e0a3f240a5de9db?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_1&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;"text": {"path": "name", "query": "searches"} playground&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Playground "unit test" technique
&lt;/h2&gt;

&lt;p&gt;Before we get into the textual analysis stemming topic, I want to share a different way of displaying search results in a playground. When playing with textual analysis, only a single document with a string field is needed. Rather than returning an empty array or an array containing only that one document from &lt;code&gt;$search&lt;/code&gt;, I'd rather see literally &lt;code&gt;0&lt;/code&gt; (false) or &lt;code&gt;1&lt;/code&gt; (true) indicating whether the document matched the query or not. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.mongodb.com/docs/manual/reference/operator/aggregation/searchMeta/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_1&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;&lt;code&gt;$searchMeta&lt;/code&gt; stage&lt;/a&gt; performs the query the same as &lt;code&gt;$search&lt;/code&gt;, but instead of returning an array of matching documents, it returns a single document of search result metadata. In this case, only the &lt;code&gt;total&lt;/code&gt; count is included. No actual collection documents are returned from &lt;code&gt;$searchMeta&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$searchMeta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;searches&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;total&lt;/span&gt;&lt;span class="dl"&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;h2&gt;
  
  
  Stemming example
&lt;/h2&gt;

&lt;p&gt;Will the query above match this document (given the default MongoDB Atlas Search configuration)?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Atlas Search Playground&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No, it doesn't (count of &lt;code&gt;0&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;total&lt;/span&gt;&lt;span class="dl"&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="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;The default mapping for string fields uses the &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/analyzers/standard/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_1&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;&lt;code&gt;lucene.standard&lt;/code&gt; analyzer&lt;/a&gt;. The standard analyzer is language-agnostic, but Unicode "word" aware, and tokenizes the &lt;code&gt;name&lt;/code&gt; field into these lowercased terms: "the", "atlas", "search", "playground". The &lt;code&gt;query&lt;/code&gt; of "searches" does not exactly match the indexed term "search", so the document does not match the query.&lt;/p&gt;

&lt;p&gt;It would be better if this query would match that document, given the words "search" and "searches" are the same root word in English. Setting the analyzer to &lt;code&gt;lucene.english&lt;/code&gt; brings in English-specific handling including stemming words to their "word stem". In this case, both "search" and "searches" (the content and the query) are analyzed to "search":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;analyzer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lucene.english&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mappings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the document matches (count of &lt;code&gt;1&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;total&lt;/span&gt;&lt;span class="dl"&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="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 the successful &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/66473aa64e0a3f240a5de9dd?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search_1&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;&lt;code&gt;lucene.english&lt;/code&gt; playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Head to the next article in this series.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://learn.mongodb.com/learn/course/search-fundamentals-on-demand-devrel-content/search-fundamentals/introduction-to-atlas-search" rel="noopener noreferrer"&gt;search fundamentals&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>search</category>
      <category>stemming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Introducing the "Playing With Search" Series</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Mon, 30 Jun 2025 17:50:23 +0000</pubDate>
      <link>https://forem.com/mongodb/introducing-playing-around-with-search-series-5eep</link>
      <guid>https://forem.com/mongodb/introducing-playing-around-with-search-series-5eep</guid>
      <description>&lt;p&gt;The inherent underlying complexity in building software necessitates techniques to peel away layers so that we can focus on a specific capability. Search-based applications, in particular, demand a special kind of understanding and experience, with nuanced challenges such as fuzzy and sub-string matching, synonym handling, custom text tokenization, and lexical relevancy scoring computations. Let's ignore, for now, how and where things will be deployed. Let's not, yet, write application code. Let's peel away all of surrounding stuff above and below "search" and we'll &lt;strong&gt;just&lt;/strong&gt; &lt;em&gt;search&lt;/em&gt; and that's it. &lt;/p&gt;

&lt;p&gt;And while we're peeling away the software code and infrastructure layers, we'll pare down the quantity and structure of the data to just the smallest we need to experiment with a configuration change, or query adjustment.&lt;/p&gt;

&lt;p&gt;This series of concise, focused posts will demonstrate a wide variety of learning about, configuring, diagnosing, and tuning MongoDB Atlas Search.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: My job is to advocate for developers leveraging MongoDB, but search is search, so if you're not using MongoDB currently, that's cool. The data, configuration, and queries will look familiar to those who have experience with other search systems. The concepts from all search systems apply, especially anything built on Lucene (documents, fields, analyzers, tokenizers, etc).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most of the examples in this series will be publicly accessible without requiring any sign-up, and easily editable by you once you click into them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our first Playground
&lt;/h2&gt;

&lt;p&gt;Borrowing from the article I wrote last year introducing the Atlas Search Playground—&lt;a href="https://www.mongodb.com/developer/products/atlas/search-playground-intro/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Atlas Search Playground: Easy Experimentation&lt;/a&gt;—here's a case-insensitive search example using the default configuration and a simple lowercase query.&lt;/p&gt;

&lt;p&gt;First a screenshot:&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%2Fhkwls4keguoriijjzydg.webp" 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%2Fhkwls4keguoriijjzydg.webp" alt="Atlas Search Playground" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ought to look the same for you clicking into &lt;a href="https://search-playground.mongodb.com/tools/code-sandbox/snapshots/664738af4e0a3f240a5de9d9?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=playing_with_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;the Playground&lt;/a&gt; and pressing Run.&lt;/p&gt;

&lt;p&gt;When we need to add case-insensitive, language-agnostic, word-level search to an application, we only need one document with one effective field to figure out how to make it work. Here's the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_id&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Atlas Search Playground&lt;/span&gt;&lt;span class="dl"&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;A user should find this document when searching for the silly-cased query of &lt;code&gt;pLaYgRoUnD&lt;/code&gt;. In MongoDB Atlas Search, that sort of simple "text" query is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pLaYgRoUnD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&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;p&gt;Welcome to this new series! Stay tuned for more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more about &lt;a href="https://learn.mongodb.com/learn/course/search-fundamentals-on-demand-devrel-content/search-fundamentals/introduction-to-atlas-search" rel="noopener noreferrer"&gt;search fundamentals&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>search</category>
      <category>regex</category>
      <category>webdev</category>
    </item>
    <item>
      <title>When NOT to use Atlas Search</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Fri, 06 Dec 2024 22:07:19 +0000</pubDate>
      <link>https://forem.com/erikhatcher/when-not-to-use-atlas-search-kh9</link>
      <guid>https://forem.com/erikhatcher/when-not-to-use-atlas-search-kh9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Design reviews are one-on-one meetings where MongoDB experts deliver advice on data modeling best practices and application design challenges. In this series, we are going to explore common real-life scenarios where design reviews helped developers achieve meaningful success with MongoDB. - &lt;a href="https://medium.com/mongodb/how-to-align-your-data-model-with-your-application-needs-when-migrating-from-rdbms-to-mongodb-abde87f2d96f" rel="noopener noreferrer"&gt;How to Align Your Data Model With Your Application Needs When Migrating From RDBMS to MongoDB | by Néstor Daza&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: I’m taking the opportunity to link liberally, sometimes loony-ily. I love the &lt;a href="https://en.wikipedia.org/wiki/Applied_Research_in_Patacriticism" rel="noopener noreferrer"&gt;serendipity&lt;/a&gt; of following &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/#behavior?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;interesting&lt;/a&gt; &lt;a href="https://www.wikihow.com/Open-a-Page-in-a-New-Window" rel="noopener noreferrer"&gt;links&lt;/a&gt;. I had fun researching and reminding myself of oldies but goodies. Here’s to at least some of the shiny &lt;a href="https://www.reddit.com/r/Damnthatsinteresting/comments/15jnlk3/a_termite_path_and_an_ant_path_intersect_with/" rel="noopener noreferrer"&gt;paths followed&lt;/a&gt; being entertaining and educational to you too.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ve got to start with a couple of assumptions for this article to best fit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’ve got documents in MongoDB Atlas.
&lt;/li&gt;
&lt;li&gt;The documents need to be findable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your documents aren’t in Atlas, then Atlas Search doesn’t (yet) apply, and thus the rest of this write-up is &lt;a href="https://en.wikipedia.org/wiki/Mu_(negative)" rel="noopener noreferrer"&gt;moot&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If anything, I’m &lt;a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/" rel="noopener noreferrer"&gt;pragmatic&lt;/a&gt; and &lt;a href="https://agilemanifesto.org/" rel="noopener noreferrer"&gt;agile&lt;/a&gt;. Duct tape, twine, or a &lt;a href="https://www.youtube.com/watch?v=nZ_5ElzesNM" rel="noopener noreferrer"&gt;card catalog&lt;/a&gt;—use what works for the job. Whether you use &lt;a href="https://en.wikipedia.org/wiki/Document-oriented_database" rel="noopener noreferrer"&gt;MongoDB or not&lt;/a&gt;, the document model is a good way to think about data challenges and worth having handy when the time is right. Do consider Atlas for your future data needs, as it’s a &lt;a href="https://www.mongodb.com/solutions/developer-data-platform?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;platform&lt;/a&gt; that provides a lot of necessary and powerful capabilities. &lt;a href="https://www.mongodb.com/developer/author/erik-hatcher/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Just sayin’.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Findability is one such necessary database capability. If you can’t find your content, it may as well not exist.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/products/platform/atlas-search?utm_campaign=devrel&amp;amp;utm_source=insights_article?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Atlas Search&lt;/a&gt; enables powerful, scalable, and relevant search features. Its strength primarily stems from &lt;a href="https://en.wikipedia.org/wiki/Apache_Lucene#History" rel="noopener noreferrer"&gt;one little, old Java library&lt;/a&gt;. There’s a potent elixir in that .jar!  And it has been &lt;a href="https://dev.to/erikhatcher/lucene-and-i-2cgo"&gt;The Solution to All The Challenges&lt;/a&gt; for the bulk of my career. One rather fun aspect of my life at MongoDB is tackling Design Reviews that involve some aspect of search. I excel at, and enjoy, solving concrete search problems. These reviews typically are with folks using Atlas Search and wanting to dig in deeper to get a bit more nuance to relevancy tuning, or folks using MongoDB &lt;code&gt;$match&lt;/code&gt; and &lt;code&gt;$regex&lt;/code&gt; and exploring if and how to leverage Atlas Search instead. Here’s a story about a recent Design Review with a customer already well versed in Atlas Search and using it effectively… to a point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atlas Search matching as expected
&lt;/h2&gt;

&lt;p&gt;Here’s the use case presented to me by a customer during a design review session:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have a service built on MongoDB Atlas that needs to rapidly match identity requests using only a few fields of exact (though case-insensitive) values, such as an ID, e-mail address, and phone number. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Case-insensitive matching over a few fields? Definitely a problem that Atlas Search can solve handily! Take a few &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/compound/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;&lt;code&gt;compound.should&lt;/code&gt;&lt;/a&gt; clauses and call us in the morning.&lt;/p&gt;

&lt;p&gt;And unsurprisingly, the customer reports that: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Atlas Search matching works as expected...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  But not so fast
&lt;/h2&gt;

&lt;p&gt;Literally, and unfortunately,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;… However, the time to “eventual consistency” in order to match recently updated documents is too long for the required &lt;a href="https://en.wikipedia.org/wiki/Service-level_agreement" rel="noopener noreferrer"&gt;SLA&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, to work around that, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A [third-party key-value] caching mechanism was implemented for a first pass lookup. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both of these topics warrant a bit of a deeper dive, so that we can understand how best to help this customer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eventual consistency
&lt;/li&gt;
&lt;li&gt;Key lookup using indexes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Eventual consistency
&lt;/h2&gt;

&lt;p&gt;Yes, Atlas Search is awesome! It can &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/analyzers/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;slice&lt;/a&gt;, &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/facet/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;dice&lt;/a&gt;, and do all sorts of &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/highlighting/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;groovy things&lt;/a&gt;, yet it obediently stays within the laws of physics. Data, being what it is, always will be adding, updating, and deleting from the database and its replica set. The &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/atlas-search-overview/#about-the-mongot-process?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Atlas Search process (&lt;code&gt;mongot&lt;/code&gt;)&lt;/a&gt; handles the database change stream and updates the underlying Lucene index. This process, by default, runs co-located with the database processes themselves on the same hardware, though ideally should run on &lt;a href="https://www.mongodb.com/blog/post/search-nodes-now-public-preview-performance-scale-dedicated-infrastructure?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;its own hardware&lt;/a&gt; nearby. &lt;/p&gt;

&lt;h2&gt;
  
  
  Coupled Architecture
&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%2Fukuqso7z296duxlm4ijc.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%2Fukuqso7z296duxlm4ijc.png" alt="Coupled architecture" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dedicated Search Nodes
&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%2F571m6m9mddkh650fn62z.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%2F571m6m9mddkh650fn62z.png" alt="Dedicated Search Nodes" width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Atlas Search is &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/performance/index-performance/#eventual-consistency-and-indexing-latency?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;eventually consistent&lt;/a&gt;. These machinations involve shards, replicas, CPU, disk, memory, network, and a bit of time. Changes to the database will, eventually, be reflected in associated search indexes. But it isn’t instantaneous, and there are many variables that affect the lag between a database change and search requests finding documents by the modified criteria: rate of data changes, complexity of index mapping configuration, deployment architecture/capabilities, resource contentions, size of the index, query load, and maybe even &lt;a href="https://en.wikipedia.org/wiki/Carrington_Event#Associated_solar_flare" rel="noopener noreferrer"&gt;solar flares&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the nature of the application, the eventually consistent lag time may be irrelevant or a critical aspect of consideration. An update to a book record in a library can get reindexed overnight without affecting operations. However, this identity request for a record that just got updated failing to match the latest value in the database is unacceptable.&lt;/p&gt;

&lt;p&gt;The trade-off of a search index being eventually consistent is to not delay, or interfere, with database-level updates and transactions. A search index update has so many variables involved and can change over time in complexity; a change to the index configuration could cause vastly more terms or documents to be indexed. An Atlas Search index is an index configuration and its corresponding Lucene index. This word “index” is a great one, but actually a Lucene index is really a collection of special purpose data structures, one for each field (and &lt;a href="https://www.mongodb.com/docs/atlas/atlas-search/analyzers/multi/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;multi&lt;/a&gt;) defined. Each field “type” has its own optimized index data structure. Lexicographically ordered inverted indexes with posting lists complete with term, document, and corpus-level statistics power &lt;code&gt;string&lt;/code&gt; mapped fields and queries. This is the heart of relevancy computations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key value lookup using indexes
&lt;/h2&gt;

&lt;p&gt;Finding by &lt;code&gt;_id&lt;/code&gt; (every document's unique key) is a given (so &lt;a href="https://www.mongodb.com/docs/manual/core/indexes/index-types/index-single/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;we throw that one in for free!&lt;/a&gt;). What about finding your data by other exact match types of criteria, such as all products in a specific category? Or all documents modified by a particular username? No doubt this type of findability is crucial too. MongoDB is really good at looking up documents by a value, provided the value is indexed. &lt;/p&gt;

&lt;p&gt;This particular application needs exact, case-insensitive value lookup over a few fields. Let’s push the case-insensitivity issue to the application, and simply have it lowercase the field value any time it is being written or queried, so now it’s fully an exact match situation on the MongoDB side of things. Following &lt;a href="https://www.mongodb.com/blog/post/performance-best-practices-indexing?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;indexing best practices&lt;/a&gt; such as the &lt;a href="https://www.mongodb.com/docs/manual/tutorial/equality-sort-range-rule/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;ESR rule&lt;/a&gt;, a few single-field B-Tree index definitions are all the customer needs to satisfy their performance SLA. These indexes &lt;a href="https://www.mongodb.com/docs/manual/core/write-performance/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;don’t come for free&lt;/a&gt;, either, but are managed in the database process quickly and handled synchronously with every document update, so consistency is guaranteed.  &lt;/p&gt;

&lt;p&gt;And to be sure, key/value lookup in Lucene (via Atlas Search) is very fast. It’s the eventual consistency lag that drives the design recommendation here. If the use case had been querying across dozens of fields in any combination for exact values and eventual consistency was an acceptable trade-off, Atlas Search would be the better approach here. With a lot of fields to intersect, B-Tree index configuration would be arduous and resource-intensive, whereas an Atlas Search index configured for multiple-field intersection would be quite efficient and performant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design recommendation: B-Tree pragmatism
&lt;/h2&gt;

&lt;p&gt;For this case of a few fields of exact value matching, with no full-text fuzzy search needed, the clear winner is leveraging the in-process, consistent, and quick B-Tree index capabilities. &lt;/p&gt;

&lt;p&gt;When queries are exact field matches and the eventual consistency time lag is a critical blocker, consider using &lt;a href="https://www.mongodb.com/docs/manual/indexes/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;classic MongoDB B-Tree indexes&lt;/a&gt; rather than Atlas Search. Atlas Search indexes are updated in a separate process, maybe even on separate hardware via a network hop, whereas B-Tree index updates happen within the scope of database update transactions and are immediately usable after an update completes. Note that &lt;code&gt;_id&lt;/code&gt; is implicitly indexed in this fashion and can be used for domain values &lt;a href="https://medium.com/mongodb/how-to-efficiently-get-json-from-mongodb-using-spring-data-and-java-1e083f620a44" rel="noopener noreferrer"&gt;if appropriate&lt;/a&gt;. With B-Tree-based lookups, a front-end cache is not needed as this is already a fast key/value lookup from a RAM-based index.&lt;/p&gt;

&lt;p&gt;Be sure to learn about &lt;a href="https://www.mongodb.com/developer/products/atlas/data-modeling-for-search/?utm_campaign=devrel&amp;amp;utm_source=insights_articles&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;data modeling and schema design for Atlas Search&lt;/a&gt; so that you’re ready for the problems for which it shines!  &lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>lucene</category>
      <category>atlas</category>
      <category>search</category>
    </item>
    <item>
      <title>Lucene and I</title>
      <dc:creator>Erik Hatcher</dc:creator>
      <pubDate>Thu, 05 Dec 2024 19:55:43 +0000</pubDate>
      <link>https://forem.com/erikhatcher/lucene-and-i-2cgo</link>
      <guid>https://forem.com/erikhatcher/lucene-and-i-2cgo</guid>
      <description>&lt;p&gt;There is one tool that has found its way into the toolbox (or let’s say “stack”) of, &lt;a href="https://lucidworks.com/post/learn-lucene/" rel="noopener noreferrer"&gt;dare I said&lt;/a&gt;, a large part of the world's computer systems in one way or another. It’s a tool that has intrigued, educated, and (give thanks) employed me for, now, nearly 25 years. You know what I’m talking about…&lt;/p&gt;

&lt;h3&gt;
  
  
  Lucene and I
&lt;/h3&gt;

&lt;p&gt;I’ve been a Lucene-ite since Doug Cutting’s creation &lt;a href="https://en.wikipedia.org/wiki/Apache_Lucene#History" rel="noopener noreferrer"&gt;migrated from Sourceforge to Jakarta&lt;/a&gt;.&lt;a href="https://almaer.com/blog/i-love-luc-ene" rel="noopener noreferrer"&gt;”I Love[d] Lucene”&lt;/a&gt; so much.that I volunteered, a large part of time over 14 months of &lt;a href="https://developeronfire.com/podcast/episode-082-erik-hatcher-principled-priorities" rel="noopener noreferrer"&gt;my life&lt;/a&gt; (and shout out to &lt;a href="https://www.linkedin.com/in/otisg/" rel="noopener noreferrer"&gt;Otis&lt;/a&gt; and &lt;a href="https://benchmarks.mikemccandless.com/" rel="noopener noreferrer"&gt;Mike&lt;/a&gt; for sharing the arduous grinds) to co-author the first edition of &lt;a href="https://www.manning.com/books/lucene-in-action-second-edition" rel="noopener noreferrer"&gt;Lucene in Action&lt;/a&gt;. The process of discovering a really cool, super powerful, and easy to use full text search library, realizing the word about it needed to get out widely, had me dig deep into the community and &lt;a href="https://github.com/apache/lucene" rel="noopener noreferrer"&gt;codebase&lt;/a&gt; with &lt;a href="https://people.apache.org/committer-index.html#ehatcher" rel="noopener noreferrer"&gt;my&lt;/a&gt; &lt;a href="https://news.apache.org/foundation/entry/success-at-apache-wearing-small" rel="noopener noreferrer"&gt;Apache&lt;/a&gt; &lt;a href="https://www.apache.org/foundation/how-it-works/#hats" rel="noopener noreferrer"&gt;hat&lt;/a&gt; on, tinkering, contributing, committing, and generally loitering on the shoulders of folks way smarter than me. &lt;/p&gt;

&lt;p&gt;I thank the &lt;a href="https://lucidworks.com/post/author/erik/" rel="noopener noreferrer"&gt;bulk of&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/erikhatcher/" rel="noopener noreferrer"&gt;my professional life&lt;/a&gt; to Lucene. I’d be kinda &lt;a href="https://www.linkedin.com/posts/erikhatcher_lucene-solr-activity-6967896584399286272-3Dzb/" rel="noopener noreferrer"&gt;lost without it&lt;/a&gt;. A lot of us would.&lt;/p&gt;

&lt;p&gt;It just be this easy with a &lt;a href="https://datawarrior.medium.com/building-a-search-engine-lucene-tutorial-a515e3bfb44b" rel="noopener noreferrer"&gt;little Java elbow grease&lt;/a&gt;. And because it’s fairly straightforward to send data into Lucene and then query it powerfully, and because Mr. Cutting nurtured such a benevolent, inviting yet demanding, &lt;a href="https://lucene.apache.org/" rel="noopener noreferrer"&gt;open source&lt;/a&gt; environment, an entire ecosystem of add-ons, forks, ports, wrappers, and companies, and ... And ... AND!&lt;/p&gt;

&lt;p&gt;When my homie Marcus “&lt;a href="https://www.youtube.com/watch?v=DjJbBj71qdM" rel="noopener noreferrer"&gt;so&lt;/a&gt;-&lt;a href="https://github.com/MarcusSorealheis/Atlas-Search-Python" rel="noopener noreferrer"&gt;real&lt;/a&gt;-&lt;a href="https://www.linkedin.com/in/marcuseagan/" rel="noopener noreferrer"&gt;he&lt;/a&gt;-&lt;a href="https://tracemachina.com/" rel="noopener noreferrer"&gt;is&lt;/a&gt;” Eagan implored me to join him at MongoDB to work on Lucene integrated capabilities, called &lt;a href="https://www.mongodb.com/products/platform/atlas-search?utm_campaign=devrel&amp;amp;utm_source=insights_article&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;Atlas Search&lt;/a&gt;, I was intrigued and jumped at the opportunity to start a fresh challenge &lt;a href="https://www.mongodb.com/developer/products/atlas/atlas-search-cene-1/?utm_campaign=devrel&amp;amp;utm_source=insights_article&amp;amp;utm_medium=dev_to&amp;amp;utm_content=not_atlas_search&amp;amp;utm_term=erik.hatcher" rel="noopener noreferrer"&gt;advocating&lt;/a&gt; for &lt;a href="https://www.youtube.com/watch?v=Vhh_GeBPOhs" rel="noopener noreferrer"&gt;developers&lt;/a&gt; baking search into “document data”-based applications.&lt;/p&gt;

&lt;p&gt;All of this said is to set the stage here - Lucene is the answer to All of The Problems! I’m an obeyer of the &lt;a href="https://en.wikipedia.org/wiki/Law_of_the_instrument#Computer_programming" rel="noopener noreferrer"&gt;Law of the instrument&lt;/a&gt;, and &lt;a href="https://spanish.stackexchange.com/questions/25979/spanish-version-for-when-life-gives-you-lemons#:~:text=Si%20la%20vida%20te%20da%20limones%20aprende%20hacer%20limonada.,you%20lemons%2C%20learn%20make%20lemonade." rel="noopener noreferrer"&gt;when life gives me data&lt;/a&gt;, I make Lucene-ade.  &lt;/p&gt;

</description>
      <category>lucene</category>
    </item>
  </channel>
</rss>
