# Exact Matches in Atlas Search: Beginners Guide

## Contributors
Much of this article was contributed by a MongoDB Intern, Humayara Karim. Thanks for spending your summer with us!

## Introduction

Search engines are powerful tools that users rely on when they're looking for information. They oftentimes rely on them to handle the misspelling of words through a feature called fuzzy matching. Fuzzy matching  identifies text, string, and even queries that are very similar but not the same. This is very useful.

But a lot of the time, the search that is most useful is an exact match. I'm looking for a word, `foobar`, and I want `foobar`, not `foobarr` and not `greenfoobart`. 

Luckily, Atlas Search has solutions for both fuzzy searches as well as exact matches. This tutorial will focus on the different ways users can achieve exact matches as well as the pros and cons of each. In fact, there are quite a few ways to achieve exact matches with Atlas Search. 

## (Let us count the) Ways to Exact Match in MongoDB
Just like the NYC subway system, there are many ways to get to the same destination, and not all of them are good. So let's talk about the various methods of doing exact match searches, and the pros and cons. 

## Atlas Search Index Analyzers
These are policies that allow users to define filters for the text matches they are looking for. For example, if you wanted to find an exact match for a string of text, the best analyzer to use would be the **Keyword Analyzer** as this analyzer indexes text fields as single terms by accepting a string or array of strings as a parameter. 

If you wanted to return exact matches that contain a specific word, the **Standard Analyzer** would be your go-to as it divides texts based on word-boundaries. It's crucial to first identify and understand the appropriate analyzer you will need based on your use case. This is where MongoDB makes our life easier because you can find all the built-in analyzers Atlas Search supports and their purposes all in one place, as shown below: 

**Pros**: Users can also make custom and multi analyzers to cater to specific application needs. There are examples on the MongoDB Developer Community Forums demonstrating folks doing this in the wild. 

Here's some code for case insensitive search using a custom analyzer and with the keyword tokenizer and a lowercase token filter:

```"analyzers": 
    {
      "charFilters": [],
      "name": "search_keyword_lowercaser",
      "tokenFilters": [
        {
          "type": "lowercase"
        }
      ],
      "tokenizer": {
        "type": "keyword"
      }
    }
  ]
```

Or, a lucene.keyword analyzer for single-word exact match queries and phrase query for multi-word exact match queries [here:
```
{
  $search: {
     "index": "movies_search_index"
     "phrase": {
       "query": "Red Robin",
       "path": "title"
     }
  }
}
```

**Cons**: Dealing with case insensitivity search isn’t super straightforward. It's not impossible, of course, but it requires a few extra steps where you would have to define a custom analyzer and run a diacritic-insensitive query.

There's a step by step guide on how to do this here. 

## The Phrase Operator
AKA a "multi-word exact match thing." The Phrase Operator can get exact match queries on multiple words (tokens) in a field. But why use a phrase operator instead of only relying on an analyzer? It’s because the phrase operator searches for an *ordered sequence* of terms with the help of an analyzer defined in the index configuration. Take a look at this example, where we want to search the phrases “the man” and “the moon” in a movie titles collection:

```
db.movies.aggregate(
{
  "$search": {
    "phrase": {
      "path": "title",
      "query": ["the man", "the moon"]
    }
  }
},
{ $limit: 10 },
{
  $project: {
    "_id": 0,
    "title": 1,
    score: { $meta: "searchScore" }
  }
}
])
```
          
 As you can see, the query returns all the results the contain ordered sequence terms “the man” and “the moon.”
```
{ "title" : "The Man in the Moon", "score" : 4.500046730041504 }
{ "title" : "Shoot the Moon", "score" : 3.278003215789795 }
{ "title" : "Kick the Moon", "score" : 3.278003215789795 }
{ "title" : "The Man", "score" : 2.8860299587249756 }
{ "title" : "The Moon and Sixpence", "score" : 2.8754563331604004 }
{ "title" : "The Moon Is Blue", "score" : 2.8754563331604004 }
{ "title" : "Racing with the Moon", "score" : 2.8754563331604004 }
{ "title" : "Mountains of the Moon", "score" : 2.8754563331604004 }
{ "title" : "Man on the Moon", "score" : 2.8754563331604004 }
{ "title" : "Castaway on the Moon", "score" : 2.8754563331604004 }
```

**Pros:** There are quite a few [field type options you can use with phrase that gives users the flexibility to customize the exact phrases they want to return. 

**Cons:** The phrase operator isn’t compatible with synonym search. What this means is that even if you have synonyms enabled, there can be a chance where your search results are whole phrases instead of an individual word. However, you can use the compound operator with two should clauses, one with the text query that uses synonyms and another that doesn't, to help go about this issue. Here is a sample code snippet of how to achieve this:

```
 compound: {
    should: 
      {
        text: {
          query: "radio tower",
          path: {
            "wildcard": "*"
          },
          synonyms: "synonymCollection"
        }
      },
      {
        text: {
          query: "radio tower",
          path: {
            "wildcard": "*"
          }
        }
      }
    ]
  }
}
```

## Autocomplete Operator
There are few things in life that thrill me as much as the [autocomplete. Remember the sheer, wild joy of using that for the first time with Google search? It was just brilliant. It was one of things that made me want to work in technology in the first place. You type, and the machines *know what you're thinking!*

And oh yea, it helps me from getting "no search results" repeatedly by guiding me to the correct terminology.

Tutorial on how to implement this for yourself is here. 

**Pros:** Autocomplete is awesome. Faster and more responsive search!
**Cons:** There are some limitations with auto-complete. You essentially have to weigh the tradeoffs between *faster* results vs *more relevant* results. There are potential workarounds, of course. You can get your exact match score higher by making your autocompleted fields indexed as a string, querying using compound operators, etc... but yea, those tradeoffs are real. I still think it's preferable over plain search, though. 

## Text Operator
As the name suggests, this operator allows users to search text.
Here is how the syntax for the text operator looks:
```
{
  $search: {
    "index": , // optional, defaults to "default"
    "text": {
      "query": "",
      "path": "",
      "fuzzy": ,
      "score": ,
      "synonyms": ""
    }
  }
}
```
 
If you're searching for a *single term* and want to use full text search to do it, this is the operator for you. Simple, effective, no frills.  It's simplicity means it's hard to mess up, and you can use it in complex use cases without worrying. You can also layer the text operator with other items.
 
 The `text` operator also supports synonyms and score matching as shown here:
 
```
db.movies.aggregate(
  {
    $search: {
      "text": {
        "path": "title",
        "query": "automobile",
        "synonyms": "transportSynonyms"
      }
    }
  },
  {
    $limit: 10
  },
  {
    $project: {
      "_id": 0,
      "title": 1,
      "score": { $meta: "searchScore" }
    }
  }
])
```
 
```
db.movies.aggregate([
  {
    $search: {
      "text": {
        "query": "Helsinki",
        "path": "plot"
      }
    }
  },
  {
    $project: {
      plot: 1,
      title: 1,
      score: { $meta: "searchScore" }
    }
  }
])
```

**Pros:** Straightforward, easy to use. 
**Cons:** The terms in your query are considered individually, so if you want to return a result that contains more than a single word, you have to nest your operators. Not a huge deal, but as a downside, you'll probably have to conduct a little research on the [other operators that fit with your use case. 

## Highlighting
Although this feature doesn’t necessarily return exact matches like the other features, it's worth *highlighting. (See what I did there?!)*

I love this feature. It's super useful.  Highlight allows users to visually see exact matches. This option also allows users to visually return search terms in their original context. In your application UI, the highlight feature looks like so:

If you’re interested in learning how to build an application like this, here is a step by step tutorial visually showing Atlas Search highlights with JavaScript and HTML.

**Pros**: Aesthetically, this feature enhances user search experience because users can easily see what they are searching for in a given text.

**Cons**:  It can be costly if passages are long because a lot more RAM will be needed to hold the data. In addition, this feature does not work with autocomplete. 

## Conclusion

Ultimately, there are many ways to achieve exact matches with Atlas Search. Your best approach is to skim through a few of the tutorials in the documentation and take a look at the Atlas search section here in the DevCenter and then tinker with it.