DEV Community

Utsav Upadhyay
Utsav Upadhyay

Posted on

Enhance Your Search Capabilities with MongoDB Atlas: Advanced Querying Techniques

Welcome back to the second part of our blog series on MongoDB Atlas Full-Text Search. In the first part, "From Indexing to Querying: How MongoDB's Full-Text Search Stacks Up Against Elasticsearch," we explored the fundamentals of MongoDB's full-text search and compared it to Elasticsearch. If you haven't read it yet, we highly recommend starting there to get a comprehensive understanding of MongoDB's full-text search capabilities. In this blog post, we will dive deeper into MongoDB Atlas Full-Text Search and explore advanced querying techniques that will enable you to take full advantage of its powerful features.

Understanding Compound Queries.

Compound queries are very important and powerful when it comes to real-world use cases.
A compound operator in MongoDB combines multiple operators to form a single query. Each part of the compound query is referred to as a clause, and each clause contains one or more sub-queries.

A compound query has the following syntax:

{
  $search: {
    "index": <index name>, // optional, defaults to "default"
    "compound": {
      <must | mustNot | should | filter>: [ { <clauses> } ],
      "score": <options>
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

Each must, mustNot, should, and filter clause contains an array of subclauses. Use array syntax even if the array contains only one subclause.

We required the following terms to construct the query, which we also explained above -

Document for explaining all the queries -

{
  "_id" : 1,
  "type" : "apple",
  "description" : "Apples come in several varieties, including Fuji, Granny Smith, and Honeycrisp.",
  "category" : "nonorganic",
  "in_stock" : false
},
{
  "_id" : 2,
  "type" : "banana",
  "description" : "Bananas are usually sold in bunches of five or six.",
  "category" : "nonorganic",
  "in_stock" : true
},
{
  "_id" : 3,
  "type" : "pear",
  "description" : "Bosc and Bartlett are the most common varieties of pears.",
  "category" : "organic",
  "in_stock" : true
}
Enter fullscreen mode Exit fullscreen mode

Operators -

  • must Clauses that must match for a document to be included in the results. The returned score is the sum of the scores of all the subqueries in the clause.

Maps to the AND boolean operator.

Example:

db.col.aggregate([
  {
    $search: {
      "compound": {
        "must": [{
          "text": {
            "query": "varieties",
            "path": "description"
          }
        }]
      }
    }
  }
])
Enter fullscreen mode Exit fullscreen mode

Explanation :

In the above query we are searching for the term Varieties in the field description which is called path in the query, and this query will return only those document which contains this - Varieties term in the field - description.

  • mustNot Clauses that must not match for a document to be included in the results. mustNot clauses don't contribute to a returned document's score.

Maps to the AND NOT boolean operator.

Example:

db.col.aggregate([
  {
    $search: {
      "compound": {
        "mustNot": [{
          "text": {
            "query": "apples",
            "path": "description"
          }
        }]
      }
    }
  }
])
Enter fullscreen mode Exit fullscreen mode

Explanation :

In the above query we are searching for the data which do not contains apples in the field description, so this query will return only those documents which do not have apples in the field description.

  • should Clauses that you prefer to match in documents that are included in the results. Documents that contain a match for a should clause have higher scores than documents that don't contain a should clause. The returned score is the sum of the scores of all the subqueries in the clause.

If you use more than one should clause, you can use the minimumShouldMatch option to specify a minimum number of should clauses that must match to include a document in the results. If omitted, the minimumShouldMatch option defaults to 0.

Maps to the OR boolean operator.

Example:

db.col.aggregate([
  {
    $search: {
      "compound": {
        "Should": [{
          "text": {
            "query": "apples",
            "path": "description"
          }
        }]
      }
    }
  }
])
Enter fullscreen mode Exit fullscreen mode

Explanation :

In the above query we are searching for the data which contains apples in the field description, so this query will return those documents which have apples in the field description.

for a better understanding of this Should operator which is the OR operator also, here is a second example -

db.col.aggregate([
  {
    $search: {
      "compound": {
        "Should": [{
          "text": {
            "query": "apples",
            "path": "description"
          },
          "text": {
            "query": "Banana",
            "path": "description"
          }
        }],
          "minimumShouldMatch": 1
      }
    }
  }
])
Enter fullscreen mode Exit fullscreen mode

Explanation :

In the above query we are searching for the data which contains apples OR Banana in the field description, so this query will return those documents which have either apples or Banana in the field description, also if you noticed we added "minimumShouldMatch": 1 in this query, minimumShouldMatch means specify a minimum number of should clauses that must match to include a document in the results(the given term has to occur specific times whatever we pass as a parameter, default it is 0)

  • filter Clauses must all match for a document to be included in the results. filter clauses do not contribute to a returned document's score.

in other words, it is a replacement of $in operator in full-text search.

filter behaves the same as must, except that the filter clause is not considered in a returned document's score, and therefore does not affect the order of the returned documents.

Example:

$search: {
  "compound": {
    "filter": [{
      "text": {
        "query": ["CLIENT", "PROFESSIONAL"],
        "path": "role"
      }
    }]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, as we understand all the important operators of full-text search, here a an example of a query which combines all these operators to get the result.

Query -

db.col.aggregate([
  {
    $search: {
      "compound": {
        "must": [{
          "text": {
             "query": "varieties",
             "path": "description"
          }
        }], 
        "mustNot": [{
          "text": {
            "query": "apples",
            "path": "description"
          }
        }],
        "should": [
          {
            "text": {
              "query": "Fuji",
              "path": "description"
            }
          },
          {
            "text": {
              "query": "Golden Delicious",
              "path": "description"
            }
          }],
          "minimumShouldMatch": 2
        }
      }
    }
])
Enter fullscreen mode Exit fullscreen mode

Explanation:

The following example uses a combination of must, mustNot, Shouldclauses to construct a query. The mustclause uses the text operator to searchfor the term varietiesin the description field. For a document to match, it must fulfil the mustclause. The mustNotclause performs a searchoperation for the term applesin the descriptionfield. For a document to match, it must not fulfil the mustNotclause. The Should clause uses the text operator to searchfor the term Fuji or Golden Delicious in the description field. For a document to match either of them at least 2 times i.e. minimumShouldMatch we used, it will return any terms or both terms given if it occurs more than 1.

I hope you find this blog post informative and helpful as you dive deeper into MongoDB Atlas Full-Text Search. Happy querying!

Top comments (0)