> For the complete MongoDB documentation index, see www.mongodb.com/docs/llms.txt

<!--
Tab options on this page. Append to the .md URL to filter:
  ?tabs=<id,...>   select specific tabs (e.g. ?tabs=nodejs,shell)
  ?allTabs=true    include every tab
  (no param)       default: one tab per tabset

Available tabs:
  other tabs: basic, facet, fts-vs, data-explorer, vib, json-editor, cloud, local, maven bom, gradle bom, maven, gradle
-->

# How to Run MongoDB Search Queries Across Multiple Collections

Run MongoDB Search queries across multiple collections using `$lookup`, `$unionWith`, or materialized views. Select your deployment type, interface, and language below to view the appropriate tutorial.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify a name for the index.

   Your MongoDB Search index is named `default` by default. If you keep this name, then your index will be the default Search index for any MongoDB Search query that does not specify a different `index` option in its [operators](https://www.mongodb.com/docs/search/query/operators-collectors/overview.md#std-label-fts-operators). If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_mflix.movies` collection.

   On the Database screen, click the `sample_analytics` database and then click the `customers` collection.

3. Run a MongoDB Search query against the collection.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$lookup` | `{
     from: "accounts",
     localField: "accounts",
     foreignField: "account_id",
     as: "purchases",
     pipeline: [
       {
         $search: {
           index: "lookup-with-search-tutorial",
           compound: {
             must: [
               {
                 queryString: {
                 defaultPath: "products",
                 query:
                   "products: (CurrencyService AND InvestmentStock)"
                 }
               }
             ],
             should: [
               {
                 range: {
                   path: "limit",
                   gte: 5000,
                   lte: 10000,
                 }
               }
             ]
           }
         }
       },
       {
         $project: {
           _id: 0,
         }
       }
     ]
   }` |
   | `$limit` | `5` |
   | `$project` | `{
      _id: 0,
      address: 0,
      birthdate: 0,
      username: 0,
      tier_and_details: 0,
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following documents next to the `$project` pipeline stage:

   ```json
   name: Elizabeth Ray
   email: arroyocolton@gmail.com
   active: True
   accounts: Array (6)
   purchases: Array (3)

   name: "Lindsay Cowan"
   email: "cooperalexis@hotmail.com"
   accounts: Array (1)
   purchases: Array (empty)

   name: "Katherine David"
   email: "timothy78@hotmail.com"
   accounts: Array (5)
   urchases: Array (2)

   name: "Leslie Martinez"
   email: "tcrawford@gmail.com"
   accounts: Array (2)
   purchases: Array (empty)

   name: "Brad Cardenas"
   email: "dustin37@yahoo.com"
   accounts: Array (5)
   purchases: Array (2)
   ```

4. Expand your query results.

   MongoDB Compass might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database. You must perform the following steps for each collection.

#### Create the MongoDB Search Index

In this section, you will create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database. You must perform the following steps for each collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

#### Run `$unionWith` with `$search` to Search the Collections

In this section, you will connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_training.companies` collection.

   On the Database screen, click the `sample_training` database and then click the `companies` collection.

3. Run the MongoDB Search query against the collection.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$search` | `{
     "text": {
       "query": "Mobile",
       "path": "name"
     }
   }` |
   | `$project` | `{
     "score": {
       "$meta": "searchScore",
     },
     "_id": 0,
     "number_of_employees": 1,
     "founded_year": 1,
     "name": 1
   }` |
   | `$set` | `{
     "source": "companies"
   }` |
   | `$limit` | `3` |
   | `$unionWith` | `{
     "coll": "inspections",
     "pipeline": [
       {
         "$search": {
           "text": {
             "query": "Mobile",
             "path": "business_name",
           }
         }
       },
       {
         "$set": {
           "source": "inspections",
         }
       },
       {
         "$project": {
           "score": {
             "$meta": "searchScore"
           },
           "source": 1,
           "_id": 0,
           "business_name": 1,
           "address": 1
         }
       },
       {
         "$limit": 3
       },
       {
         "$sort": {
           "score": -1
         }
       }
     ]
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following documents next to the `$project` pipeline stage:

   ```json
   name: "XLR8 Mobile"
   number_of_employees: 21
   founded_year: 2006
   score: 2.0815043449401855
   source: "companies"

   name: "Pulse Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   name: "T-Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   business_name: "T. MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "BOOST MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "SPRING MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919
   ```

4. Expand your query results.

   MongoDB Compass might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and create a new collection.

   On the Databases screen, click the `sample_supplies` database.

   Click Create Collection.

   In the Create Collection dialog, enter `purchaseOrders` in the Collection Name field.

   Click Create Collection.

3. Add documents to the collection.

   Click the newly created `purchaseOrders` collection.

   Click Add Data and select Insert document.

   In the Insert Document dialog, enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-23T21:06:49.506Z"
     },
     "items": [
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": 40.01,
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 35.29,
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": 56.12,
         "quantity": 5
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": 77.71,
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 18.47,
         "quantity": 2
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 19.95,
         "quantity": 8
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 8.08,
         "quantity": 3
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": 14.16,
         "quantity": 3
       }
     ],
     "storeLocation": "Denver",
     "customer": {
       "gender": "M",
       "age": 42,
       "email": "cauho@witwuta.sv",
       "satisfaction": 4
     },
     "couponUsed": true,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

   Click Add Data and select Insert Document again.

   In the Insert Document dialog, enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-25T10:01:02.918Z"
     },
     "items": [
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 8.05,
         "quantity": 10
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": 28.31,
         "quantity": 9
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 20.95,
         "quantity": 3
       },
       {
         "name": "laptop",
         "tags": ["electronics", "school", "office"],
         "price": 866.5,
         "quantity": 4
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 33.09,
         "quantity": 4
       },
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": 37.55,
         "quantity": 1
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": 83.28,
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": 42.9,
         "quantity": 4
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 16.68,
         "quantity": 2
       }
     ],
     "storeLocation": "Seattle",
     "customer": {
       "gender": "M",
       "age": 50,
       "email": "keecade@hem.uy",
       "satisfaction": 5
     },
     "couponUsed": false,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

4. Verify the inserted documents.

   Click the Documents tab if you're not already on it.

   Navigate to the search bar and select the Options dropdown.

   In the Sort field, enter:

   ```json
   { "saleDate": -1 }
   ```

   Click Find.

   The query results should display the two purchase order documents, with the most recent (January 25, 2018) displayed first.

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Navigate to the `sample_supplies` database.

   On the Database screen, click the `sample_supplies` database to access its collections.

3. Create a materialized view using the sales collection.

   First, you'll run an aggregation pipeline on the `sales` collection to create the materialized view:

   Click on the `sales` collection.

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   - Stage 1: `$match`

     ```javascript
     {
       purchaseMethod: "Phone"
     }
     ```

   - Stage 2: `$unwind`

     ```javascript
     {
       path: "$items"
     }
     ```

   - Stage 3: `$group`

     ```javascript
     {
       _id: {
         $dateToString: {
           format: "%Y-%m",
           date: "$saleDate"
         }
       },
       sales_quantity: { $sum: "$items.quantity" },
       sales_price: { $sum: "$items.price" }
     }
     ```

   - Stage 4: `$set`

     ```javascript
     {
       sales_price: { $toDouble: "$sales_price" }
     }
     ```

   - Stage 5: `$merge`

     ```javascript
     {
       into: "monthlyPhoneTransactions",
       whenMatched: "replace"
     }
     ```

   Click Run to execute the aggregation pipeline. This creates the initial `monthlyPhoneTransactions` collection from the `sales` data.

4. Update the materialized view with data from the purchaseOrders collection.

   Next, you'll run a similar aggregation pipeline on the `purchaseOrders` collection to add that data to the materialized view:

   Navigate back to the database view and click on the `purchaseOrders` collection.

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   - Stage 1: `$match`

     ```javascript
     {
       purchaseMethod: "Phone"
     }
     ```

   - Stage 2: `$unwind`

     ```javascript
     {
       path: "$items"
     }
     ```

   - Stage 3: `$group`

     ```javascript
     {
       _id: {
         $dateToString: {
           format: "%Y-%m",
           date: "$saleDate"
         }
       },
       sales_quantity: { $sum: "$items.quantity" },
       sales_price: { $sum: "$items.price" }
     }
     ```

   - Stage 4: `$set`

     ```javascript
     {
       sales_price: { $toDouble: "$sales_price" }
     }
     ```

   - Stage 5: `$merge`

     ```javascript
     {
       into: "monthlyPhoneTransactions",
       whenMatched: "replace"
     }
     ```

   Click Run to execute the aggregation pipeline. This updates the `monthlyPhoneTransactions` collection with data from the `purchaseOrders` collection.

5. Verify that the materialized view was created correctly.

   Navigate back to the database view and click on the newly created `monthlyPhoneTransactions` collection.

   Click the Documents tab to view the documents in the collection.

   You should see documents with monthly aggregated data. The first few documents might look like:

   ```json
   {
     "_id": "2013-01",
     "sales_quantity": 200,
     "sales_price": 2941.47
   }
   {
     "_id": "2013-02",
     "sales_quantity": 191,
     "sales_price": 4822.15
   }
   {
     "_id": "2013-03",
     "sales_quantity": 74,
     "sales_price": 2429.12
   }
   ```

6. Set up a schedule to update the materialized view.

   For production environments, we recommend that you set up a scheduled task to update the materialized view. You can do this by using the following methods:

   1. **MongoDB Scheduled Triggers**: If using MongoDB Atlas, create a scheduled trigger through the Atlas UI to run this aggregation on a monthly basis.

   2. **Cron Job**: Set up a cron job on your server that runs a script to connect to MongoDB and execute the aggregation pipeline.

   3. **Application Logic**: Implement scheduled updates in your application using the example code shown in the programming language examples of this guide.

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify a name for the index.

   Your MongoDB Search index is named `default` by default. If you keep this name, then your index will be the default Search index for any MongoDB Search query that does not specify a different `index` option in its [operators](https://www.mongodb.com/docs/search/query/operators-collectors/overview.md#std-label-fts-operators). If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_supplies` database.

   On the Database screen, click the `sample_supplies` database and then click the `monthlyPhoneTransactions` collection.

3. Run a simple MongoDB Search query against the collection.

   The following query counts the number of months in monthlyPhoneTransactions with total sales greater than or equal to `10000` dollars.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$lookup` | `{
     from: "accounts",
     localField: "accounts",
     foreignField: "account_id",
     as: "purchases",
     pipeline: [
       {
         $search: {
           index: "lookup-with-search-tutorial",
           compound: {
             must: [
               {
                 queryString: {
                 defaultPath: "products",
                 query:
                   "products: (CurrencyService AND InvestmentStock)"
                 }
               }
             ],
             should: [
               {
                 range: {
                   path: "limit",
                   gte: 5000,
                   lte: 10000,
                 }
               }
             ]
           }
         }
       },
       {
         $project: {
           _id: 0,
         }
       }
     ]
   }` |
   | `$limit` | `5` |
   | `$project` | `{
      _id: 0,
      address: 0,
      birthdate: 0,
      username: 0,
      tier_and_details: 0,
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following result:

   ```json
   {
     "months_w_over_10000": 4
   }
   ```

4. Interpret the query results.

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_analytics
   ```

   **Output:**

   ```shell
   switched to db sample_analytics
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index.

   ```shell
   db.accounts.createSearchIndex(
     "lookup-with-search-tutorial",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Connect to your deployment by using [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your deployment. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Switch to the `sample_analytics` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```sh
   use sample_analytics
   ```

   **Output:**

   ```sh
   switched to db sample_analytics
   ```

3. Run the following [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with MongoDB Search [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```json
   db.customers.aggregate([
     {
       $lookup:{
         "from": "accounts",
         "localField": "accounts",
         "foreignField": "account_id",
         "as": "purchases",
         "pipeline": [{
           "$search": {
             "index": "lookup-with-search-tutorial",
             "compound": {
               "must": [{
                 "queryString": {
                   "defaultPath": "products",
                   "query": "products: (CurrencyService AND InvestmentStock)"
                 }
               }],
               "should": [{
                 "range": {
                   "path": "limit",
                   "gte": 5000,
                   "lte": 10000
                 }
               }]
             }
           }
         },{
           "$project": {
             "_id": 0
           }
         }]
       }
     },{
       "$limit": 5
     },{
       "$project": {
         "_id": 0,
         "address": 0,
         "birthdate": 0,
         "username": 0,
         "tier_and_details": 0
       }
     }
   ])
   ```

   **Output:**

   ```json
   [
     {
       name: 'Elizabeth Ray',
       email: 'arroyocolton@gmail.com',
       active: true,
       accounts: [ 371138, 324287, 276528, 332179, 422649, 387979 ],
       purchases: [
         {
           account_id: 422649,
           limit: 10000,
           products: [ 'CurrencyService', 'InvestmentStock' ]
         },
         {
           account_id: 324287,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'Derivatives',
             'InvestmentStock'
           ]
         },
         {
           account_id: 332179,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'InvestmentFund',
             'Brokerage',
             'InvestmentStock'
           ]
         }
       ]
     },
     {
       name: 'Lindsay Cowan',
       email: 'cooperalexis@hotmail.com',
       accounts: [ 116508 ],
       purchases: []
     },
     {
       name: 'Katherine David',
       email: 'timothy78@hotmail.com',
       accounts: [ 462501, 228290, 968786, 515844, 377292 ],
       purchases: [
         {
           account_id: 228290,
           limit: 10000,
           products: [
             'CurrencyService',
             'InvestmentStock',
             'InvestmentFund',
             'Brokerage'
           ]
         },
         {
           account_id: 515844,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'InvestmentFund',
             'Brokerage',
             'InvestmentStock'
           ]
         }
       ]
     },
     {
       name: 'Leslie Martinez',
       email: 'tcrawford@gmail.com',
       accounts: [ 170945, 951849 ],
       purchases: []
     },
     {
       name: 'Brad Cardenas',
       email: 'dustin37@yahoo.com',
       accounts: [ 721914, 817222, 973067, 260799, 87389 ],
       purchases: [
         {
           account_id: 87389,
           limit: 10000,
           products: [ 'CurrencyService', 'InvestmentStock' ]
         },
        {
           account_id: 260799,
           limit: 10000,
           products: [
             'Brokerage',
             'InvestmentStock',
             'Commodity',
             'CurrencyService'
           ]
         }
       ]
     }
   ]
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_training
   ```

   **Output:**

   ```shell
   switched to db sample_training
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index on the companies collection.

   ```shell
   db.companies.createSearchIndex(
     "default",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   default
   ```

4. Run the `db.collection.createSearchIndex()` method to create the index on the inspections collection.

   ```shell
   db.inspections.createSearchIndex(
     "default",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Connect to your deployment in [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your deployment. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Switch to the `sample_training` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```sh
   use sample_training
   ```

   **Output:**

   ```sh
   switched to db sample_training
   ```

3. Run the following [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with a MongoDB Search [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```json
   db.companies.aggregate([
   {
     "$search": {
       "text": {
         "query": "Mobile",
         "path": "name"
       }
     }
   }, {
     "$project": {
       "score": {
         "$meta": "searchScore"
       },
       "_id": 0,
       "number_of_employees": 1,
       "founded_year": 1,
       "name": 1
     }
   }, {
     "$set": {
       "source": "companies"
     }
   }, {
     "$limit": 3
   }, {
     "$unionWith": {
       "coll": "inspections",
       "pipeline": [
         {
           "$search": {
             "text": {
               "query": "Mobile",
               "path": "business_name"
             }
           }
         }, {
           "$set": {
             "source": "inspections"
           }
         }, {
           "$project": {
             "score": {
               "$meta": "searchScore"
             },
             "source": 1,
             "_id": 0,
             "business_name": 1,
             "address": 1
           }
         },  {
           "$limit": 3
         }, {
           "$sort": {
             "score": -1
           }
         }
       ]
     }
   }
   ])
   ```

   **Output:**

   ```json
   [
     {
       name: 'XLR8 Mobile',
       number_of_employees: 21,
       founded_year: 2006,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       name: 'Pulse Mobile',
       number_of_employees: null,
       founded_year: null,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       name: 'T-Mobile',
       number_of_employees: null,
       founded_year: null,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       business_name: 'T. MOBILE',
       address: { city: 'BROOKLYN', zip: 11209, street: '86TH ST', number: 440 },
       score: 2.900916337966919,
       source: 'inspections'
     },
     {
       business_name: 'BOOST MOBILE',
       address: { city: 'BRONX', zip: 10458, street: 'E FORDHAM RD', number: 261 },
       score: 2.900916337966919,
       source: 'inspections'
     },
     {
       business_name: 'SPRING MOBILE',
       address: {
         city: 'SOUTH RICHMOND HILL',
         zip: 11419,
         street: 'LIBERTY AVE',
         number: 12207
       },
       score: 2.900916337966919,
       source: 'inspections'
     }
   ]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Connect to the `sample_supplies` database.

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

   Use the `sample_supplies` database:

   ```sh
   use sample_supplies
   ```

2. Add a new collection.

   Add the `purchaseOrders` collection with new phone purchase order data from January of 2018. Run the following commands:

   ```json
   db.purchaseOrders.insertMany( [
     {
       saleDate: ISODate("2018-01-23T21:06:49.506Z"),
       items: [
         {
           name: 'printer paper',
           tags: [ 'office', 'stationary' ],
           price: Decimal128("40.01"),
           quantity: 2
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("35.29"),
           quantity: 2
         },
         {
           name: 'pens',
           tags: [ 'writing', 'office', 'school', 'stationary' ],
           price: Decimal128("56.12"),
           quantity: 5
         },
         {
           name: 'backpack',
           tags: [ 'school', 'travel', 'kids' ],
           price: Decimal128("77.71"),
           quantity: 2
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("18.47"),
           quantity: 2
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("19.95"),
           quantity: 8
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("8.08"),
           quantity: 3
         },
         {
           name: 'binder',
           tags: [ 'school', 'general', 'organization' ],
           price: Decimal128("14.16"),
           quantity: 3
         }
       ],
       storeLocation: 'Denver',
       customer: {
         gender: 'M',
         age: 42,
         email: 'cauho@witwuta.sv',
         satisfaction: 4
       },
       couponUsed: true,
       purchaseMethod: 'Phone'
     }
   ])
   ```

   ```json
   db.purchaseOrders.insertMany( [
     {
       saleDate: ISODate("2018-01-25T10:01:02.918Z"),
       items: [
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("8.05"),
           quantity: 10
         },
         {
           name: 'binder',
           tags: [ 'school', 'general', 'organization' ],
           price: Decimal128("28.31"),
           quantity: 9
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("20.95"),
           quantity: 3
         },
         {
           name: 'laptop',
           tags: [ 'electronics', 'school', 'office' ],
           price: Decimal128("866.5"),
           quantity: 4
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("33.09"),
           quantity: 4
         },
         {
           name: 'printer paper',
           tags: [ 'office', 'stationary' ],
           price: Decimal128("37.55"),
           quantity: 1
         },
         {
           name: 'backpack',
           tags: [ 'school', 'travel', 'kids' ],
           price: Decimal128("83.28"),
           quantity: 2
         },
         {
           name: 'pens',
           tags: [ 'writing', 'office', 'school', 'stationary' ],
           price: Decimal128("42.9"),
           quantity: 4
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("16.68"),
           quantity: 2
         }
       ],
       storeLocation: 'Seattle',
       customer: { gender: 'M', age: 50, email: 'keecade@hem.uy', satisfaction: 5 },
       couponUsed: false,
       purchaseMethod: 'Phone'
     }
   ])
   ```

3. Query the new collection.

   Query the `purchaseOrders` collection to confirm the new purchase order entries.

   ```sh
   db.purchaseOrders.find().sort( {saleDate: -1} )
   ```

   **Output:**

   ```sh
   {
     _id: ObjectId("62434c07d574cd0ce200ba75"),
     saleDate: ISODate("2018-01-25T10:01:02.918Z"),
     items: [
       {
         name: 'envelopes',
         tags: [ 'stationary', 'office', 'general' ],
         price: Decimal128("8.05"),
         quantity: 10
       },
       {
         name: 'binder',
         tags: [ 'school', 'general', 'organization' ],
         price: Decimal128("28.31"),
         quantity: 9
       },
       {
         name: 'notepad',
         tags: [ 'office', 'writing', 'school' ],
         price: Decimal128("20.95"),
         quantity: 3
       },
       {
         name: 'laptop',
         tags: [ 'electronics', 'school', 'office' ],
         price: Decimal128("866.5"),
         quantity: 4
       },
       {
         name: 'notepad',
         tags: [ 'office', 'writing', 'school' ],
         price: Decimal128("33.09"),
         quantity: 4
       },
       {
         name: 'printer paper',
         tags: [ 'office', 'stationary' ],
         price: Decimal128("37.55"),
         quantity: 1
       },
       {
         name: 'backpack',
         quantity: 2
       },
       {
         name: 'pens',
         quantity: 4
       },
       {
         name: 'envelopes',
         quantity: 2
       }
     ],
     storeLocation: 'Seattle',
     customer: {
       gender: 'M',
       age: 50,
       email: 'keecade@hem.uy',
       satisfaction: 5
     },
     couponUsed: false,
     purchaseMethod: 'Phone'
   },
   {
     _id: ObjectId("62434c07d574cd0ce200ba74"),
     saleDate: ISODate("2018-01-23T21:06:49.506Z"),
     items: [
       {
         name: 'printer paper',
         quantity: 2
       },
       {
         name: 'notepad',
         quantity: 2
       },
       {
         name: 'pens',
         quantity: 5
       },
       {
         name: 'backpack',
         quantity: 2
       },
       {
         name: 'notepad',
         quantity: 2
       },
       {
         name: 'envelopes',
         quantity: 8
       },
       {
         name: 'envelopes',
         quantity: 3
       },
       {
         name: 'binder',
         quantity: 3
       }
     ],
     storeLocation: 'Denver',
     customer: {
       gender: 'M',
       age: 42,
       email: 'cauho@witwuta.sv',
       satisfaction: 4
     },
     couponUsed: true,
     purchaseMethod: 'Phone'
   }
   ```

   The two query results reflect that the purchase order data ends in January of 2018.

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the materialized view.

   ```shell
    use sample_supplies
   ```

   **Output:**

   ```shell
   switched to db sample_supplies
   ```

3. Create the materialized view using an aggregation pipeline.

   Create a materialized view of monthly phone transactions by running the following aggregation pipelines. This creates a new collection that contains aggregated data about phone transactions by month:

   ```shell
      db.sales.aggregate([
        { $match: { purchaseMethod: "Phone" } },
        { $unwind: { path: "$items" } },
        {
          $group: {
            _id: {
              $dateToString: {
                format: "%Y-%m",
                date: "$saleDate"
              }
            },
            sales_quantity: { $sum: "$items.quantity" },
            sales_price: { $sum: "$items.price" }
          }
        },
        { $set: { sales_price: { $toDouble: "$sales_price" } } },
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ])

      db.purchaseOrders.aggregate([
        { $match: { purchaseMethod: "Phone" } },
        { $unwind: { path: "$items" } },
        {
          $group: {
            _id: {
              $dateToString: {
                format: "%Y-%m",
                date: "$saleDate"
              }
            },
            sales_quantity: { $sum: "$items.quantity" },
            sales_price: { $sum: "$items.price" }
          }
        },
        { $set: { sales_price: { $toDouble: "$sales_price" } } },
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ])
   ```

4. Set up a script to periodically update the view.

   For production environments, you should set up a scheduled task to update the materialized view. You can do this with:

   1. **MongoDB Scheduled Triggers**: If using MongoDB Atlas, create a scheduled trigger through the Atlas UI to run this aggregation on a monthly basis.

   2. **Cron Job**: Set up a cron job on your server that runs a script to connect to MongoDB and execute the aggregation pipeline.

   3. **Application Logic**: Implement scheduled updates in your application using the example code shown in the programming language examples of this guide.

5. Verify that the materialized view was created.

   ```shell
   db.monthlyPhoneTransactions.find().sort({_id: 1}).limit(3)
   ```

   **Output:**

   ```text
   [
     { _id: '2013-01', sales_quantity: 200, sales_price: 2941.47 },
     { _id: '2013-02', sales_quantity: 191, sales_price: 4822.15 },
     { _id: '2013-03', sales_quantity: 74, sales_price: 2429.12 }
   ]
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_supplies
   ```

   **Output:**

   ```shell
   switched to db sample_supplies
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index.

   ```shell
   db.monthlyPhoneTransactions.createSearchIndex(
     "monthlySalesIndex",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Connect to your cluster in [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Use the `sample_supplies` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```javascript
   use sample_supplies
   ```

3. Run a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   The following query counts the number of months in monthlyPhoneTransactions with total sales greater than or equal to `10000` dollars:

   ```javascript
   db.monthlyPhoneTransactions.aggregate([
     {
         $search: {
             "index": "monthlySalesIndex",
             "range": {
                 "gt": 10000,
                 "path": ["sales_price"]
             }
         }
     },
     {
       $count: 'months_w_over_10000'
     },
   ])
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

   For complete aggregation pipeline documentation, see the [MongoDB Server Manual.](https://www.mongodb.com/docs/manual/aggregation.md#std-label-aggregation)

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. In Atlas, go to the Search & Vector Search page for your cluster.

   You can go the MongoDB Search page from the Search & Vector Search option, or the Data Explorer.

   ### Search & Vector Search

   If it's not already displayed, select the organization that contains your project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your project from the Projects menu in the navigation bar.

   In the sidebar, click Search & Vector Search under the Database heading.

   - If you have no clusters, click Create cluster to create one. To learn more, see [Create an Atlas Infinite or Atlas Core Cluster.](https://www.mongodb.com/docs/atlas/tutorial/create-new-cluster.md#std-label-create-new-cluster)

   - If your project has multiple clusters, select the cluster you want to use from the Select cluster dropdown, then click Go to Search.

   The [Search & Vector Search](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters%2FatlasSearch%2F%3Ccluster%3E) page displays.

2. Click Create Search Index.

3. Start your index configuration.

   Make the following selections on the page and then click Next.

   | Search Type | Select the MongoDB Search index type. |
   | --- | --- |
   | Index Name and Data Source | Specify the following information: Index Name: `monthlyPhoneTransactions`; Database and Collection:`sample_supplies` database; `monthlyPhoneTransactions` collection |
   | Configuration Method | For a guided experience, select Visual Editor.To edit the raw index definition, select JSON Editor. |

   IMPORTANT: The default index name varies by index type: your MongoDB Search index is named `default`, your Vector Search index is named `vector_index`, and your Automated Embedding index is named `autoembed_index`. If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

4. Specify an index definition.

   The following index definition dynamically indexes the fields of [supported types](https://www.mongodb.com/docs/search/indexes/define-field-mappings.md#std-label-bson-data-chart) in the collection. You can use the MongoDB Search Visual Editor or the MongoDB Search JSON
   Editor in the Atlas user interface to create the index.

   ### Visual Editor

   Review the default index definition for the collection.

5. Click Create Search Index.

6. Close the You're All Set! Modal Window.

   A modal window displays to let you know your index is building. Click the Close button.

7. Wait for the index to finish building.

   The index should take about one minute to build. While it is building, the Status column reads `Build in
               Progress`. When it is finished building, the Status column reads `Active`.

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. In Atlas, go to the Clusters page for your project.

   If it's not already displayed, select the organization that contains your desired project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your desired project from the Projects menu in the navigation bar.

   In the sidebar, click Clusters under the Database heading.

   The [Clusters](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters) page displays.

2. Go to the Aggregation page for your cluster.

   Click the Browse Collections button for your cluster.

   Expand the sample\_analytics database and click the customers collection.

   Click the Aggregation tab for the collection.

3. Run a MongoDB Search `lookup` query on the `customers` and `accounts` collections.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   Click the TEXT view on the Aggregation page. Then, copy and paste the following code into the pipeline to run the query:

   ```js
   [
     {
       $lookup: {
         "from": "accounts",
         "localField": "accounts",
         "foreignField": "account_id",
         "as": "purchases",
         "pipeline": [{
           "$search": {
             "index": "lookup-with-search-tutorial",
             "compound": {
               "must": [{
                 "queryString": {
                   "defaultPath": "products",
                   "query": "products: (CurrencyService AND InvestmentStock)"
                 }
               }],
               "should": [{
                 "range": {
                   "path": "limit",
                   "gte": 5000,
                   "lte": 10000
                 }
               }]
             }
           }
         },{
           "$limit": 5
         },{
           "$project": {
             "_id": 0,
             "address": 0,
             "birthdate": 0,
             "username": 0,
             "tier_and_details": 0
           }
         }]
       }
     },{
       "$limit": 5
     },{
       "$project": {
         "_id": 0,
         "address": 0,
         "birthdate": 0,
         "username": 0,
         "tier_and_details": 0
       }
     }
   ]
   ```

   **Output:**

   ```text
   name: "Lindsay Cowan"
   email: "cooperalexis@hotmail.com"
   accounts: Array (1)
   purchases: Array (empty)

   name: "Dr. Angela Brown"
   email: "michaelespinoza@gmail.com"
   accounts: Array (1)
   purchases: Array (empty)

   name: "Brian Flores"
   email: "april04@gmail.com"
   accounts: Array (2)
   purchases: Array (1)

   name: "Shirley Rodriguez"
   email: "jonathan95@yahoo.com"
   accounts: Array (4)
   purchases: Array (3)

   name: "Clinton Shelton"
   email: "acook@gmail.com"
   accounts: Array (5)
   purchases: Array (empty)
   ```

4. Expand your query results.

   Atlas might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database. You must perform the following steps for each collection.

1. In Atlas, go to the Search & Vector Search page for your cluster.

   You can go the MongoDB Search page from the Search & Vector Search option, or the Data Explorer.

   ### Search & Vector Search

   If it's not already displayed, select the organization that contains your project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your project from the Projects menu in the navigation bar.

   In the sidebar, click Search & Vector Search under the Database heading.

   - If you have no clusters, click Create cluster to create one. To learn more, see [Create an Atlas Infinite or Atlas Core Cluster.](https://www.mongodb.com/docs/atlas/tutorial/create-new-cluster.md#std-label-create-new-cluster)

   - If your project has multiple clusters, select the cluster you want to use from the Select cluster dropdown, then click Go to Search.

   The [Search & Vector Search](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters%2FatlasSearch%2F%3Ccluster%3E) page displays.

2. Click Create Search Index.

3. Start your index configuration.

   Make the following selections on the page and then click Next.

   | Search Type | Select the MongoDB Search index type. |
   | --- | --- |
   | Index Name and Data Source | Specify the following information: Index Name: `monthlyPhoneTransactions`; Database and Collection:`sample_supplies` database; `monthlyPhoneTransactions` collection |
   | Configuration Method | For a guided experience, select Visual Editor.To edit the raw index definition, select JSON Editor. |

   IMPORTANT: The default index name varies by index type: your MongoDB Search index is named `default`, your Vector Search index is named `vector_index`, and your Automated Embedding index is named `autoembed_index`. If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

4. Specify an index definition.

   The following index definition dynamically indexes the fields of [supported types](https://www.mongodb.com/docs/search/indexes/define-field-mappings.md#std-label-bson-data-chart) in the collection. You can use the MongoDB Search Visual Editor or the MongoDB Search JSON
   Editor in the Atlas user interface to create the index.

   ### Visual Editor

   Review the default index definition for the collection.

5. Click Create Search Index.

6. Close the You're All Set! Modal Window.

   A modal window displays to let you know your index is building. Click the Close button.

7. Wait for the index to finish building.

   The index should take about one minute to build. While it is building, the Status column reads `Build in
               Progress`. When it is finished building, the Status column reads `Active`.

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. In Atlas, go to the Clusters page for your project.

   If it's not already displayed, select the organization that contains your desired project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your desired project from the Projects menu in the navigation bar.

   In the sidebar, click Clusters under the Database heading.

   The [Clusters](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters) page displays.

2. Go to the Browse Collections page.

   Click the Browse Collections button for your cluster.

3. Go to the Aggregation page for your cluster.

   Expand the sample\_training database and click the companies collection.

   Click the Aggregation tab for the collection.

4. Run a MongoDB Search `$unionWith` query on the `companies` and `inspections` collections.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   Click the TEXT view on the Aggregation page. Then, copy and paste the following code into the pipeline to run the query:

   ```js
   [
     {
       $search: {
         text: {
           query: "Mobile",
           path: "name"
         }
       }
     }, {
       $project: {
         score: { $meta: "searchScore" },
         _id: 0,
         number_of_employees: 1,
         founded_year: 1,
         name: 1
       }
     }, {
       $set: {
         source: "companies"
       }
     }, {
       $limit: 3
     }, {
       $unionWith: {
         coll: "inspections",
         pipeline: [
           {
             $search: {
               text: {
                 query: "Mobile",
                 path: "business_name"
               }
             }
           }, {
             $set: {
               source: "inspections"
             }
           }, {
             $project: {
               score: { $meta: "searchScore" },
               source: 1,
               _id: 0,
               business_name: 1,
               address: 1
             }
           }, {
             $limit: 3
           }, {
             $sort: {
               score: -1
             }
           }
         ]
       }
     }
   ]
   ```

   **Output:**

   ```text
   name: "XLR8 Mobile"
   number_of_employees: 21
   founded_year: 2006
   score: 2.0815043449401855
   source: "companies"

   name: "Pulse Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   name: "T-Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   business_name: "T. MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "BOOST MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "SPRING MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919
   ```

5. Expand your query results.

   Atlas might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. In Atlas, go to the Clusters page for your project.

   If it's not already displayed, select the organization that contains your desired project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your desired project from the Projects menu in the navigation bar.

   In the sidebar, click Clusters under the Database heading.

   The [Clusters](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters) page displays.

2. Go to the Browse Collections page.

   Click the Browse Collections button for your cluster.

3. Select the database and create a new collection.

   Select the `sample_supplies` database from the dropdown menu.

   Click the + button to the right of the database name.

   In the Create Collection dialog, enter `purchaseOrders` in the Collection Name field.

   Click Create.

4. Add documents to the collection.

   Select the newly created `purchaseOrders` collection.

   Click Insert Document.

   In the Insert Document dialog, select the \{} view and enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-23T21:06:49.506Z"
     },
     "items": [
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": {
           "$numberDecimal": "40.01"
         },
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": {
           "$numberDecimal": "35.29"
         },
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": {
           "$numberDecimal": "56.12"
         },
         "quantity": 5
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": {
           "$numberDecimal": "77.71"
         },
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": {
           "$numberDecimal": "18.47"
         },
         "quantity": 2
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": {
           "$numberDecimal": "19.95"
         },
         "quantity": 8
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": {
           "$numberDecimal": "8.08"
         },
         "quantity": 3
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": {
           "$numberDecimal": "14.16"
         },
         "quantity": 3
       }
     ],
     "storeLocation": "Denver",
     "customer": {
       "gender": "M",
       "age": 42,
       "email": "cauho@witwuta.sv",
       "satisfaction": 4
     },
     "couponUsed": true,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

   Click Insert Document again.

   In the Insert Document dialog, enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-25T10:01:02.918Z"
     },
     "items": [
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": {
           "$numberDecimal": "8.05"
         },
         "quantity": 10
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": {
           "$numberDecimal": "28.31"
         },
         "quantity": 9
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": {
           "$numberDecimal": "20.95"
         },
         "quantity": 3
       },
       {
         "name": "laptop",
         "tags": ["electronics", "school", "office"],
         "price": {
           "$numberDecimal": "866.5"
         },
         "quantity": 4
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": {
           "$numberDecimal": "33.09"
         },
         "quantity": 4
       },
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": {
           "$numberDecimal": "37.55"
         },
         "quantity": 1
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": {
           "$numberDecimal": "83.28"
         },
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": {
           "$numberDecimal": "42.9"
         },
         "quantity": 4
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": {
           "$numberDecimal": "16.68"
         },
         "quantity": 2
       }
     ],
     "storeLocation": "Seattle",
     "customer": {
       "gender": "M",
       "age": 50,
       "email": "keecade@hem.uy",
       "satisfaction": 5
     },
     "couponUsed": false,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

5. Verify the inserted documents.

   Click Find to view the documents in the collection.

   In the search bar, select the Options dropdown and enter the following code in the Sort field to sort the documents by sale date in descending order:

   ```json
   { "saleDate": -1 }
   ```

   Click Apply.

   The query results should display the two purchase order documents, with the most recent (January 25, 2018) displayed first.

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view.

**Note:**

To create the triggers, you must have [`Project Owner`](https://www.mongodb.com/docs/atlas/reference/user-roles.md#mongodb-authrole-Project-Owner) or higher access to the project.

In the following procedures, you create triggers to create a materialized view and schedule a function to update the materialized view daily. Expand the following sections to view the steps for each trigger:

#### Create the updateMonthlySales Trigger

1. In Atlas, go to the Triggers page for your project.

   If it's not already displayed, select the organization that contains your project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your project from the Projects menu in the navigation bar.

   In the sidebar, click Triggers under the Streaming Data heading.

   The [Triggers](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Ftriggers) page displays.

2. Click Add a Trigger to open the Trigger configuration page.

3. Enter configuration values for the Trigger.

   | UI Field Name | Configuration |
   | --- | --- |
   | Trigger Type | Select Scheduled. |
   | Name | Specify `updateMonthlySales`. |
   | Schedule Type | Select Basic.For Repeat once by, select `Day of the Month` and set the value to your preferred date.Alternatively, for testing purposes, set Repeat once by dropdown to a more frequent occurrence, such as Minute or Hour. |
   | Select An Event Type | Select Function. |

4. Create the Function.

   The function for this trigger defines a `monthlyPhoneTransactions` materialized view that contains cumulative monthly sales information. The function updates monthly sales information for sales conducted over the phone.

   Paste the following code into the function:

   ```javascript
   exports = function(){

      var pipeline = [
        { $match: {purchaseMethod: "Phone"} },
        { $unwind: {path: "$items"}},
        { $group: {
         _id: { $dateToString:
         { format: "%Y-%m", date: "$saleDate" } },
         sales_quantity: { $sum: "$items.quantity"},
         sales_price: { $sum: "$items.price"}
        }},
        { $set: { sales_price: { $toDouble: "$sales_price"}}},
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ]

      var monthlyPhoneTransactions = context.services.get("mongodb-atlas").db("sample_supplies").collection("sales");

      return monthlyPhoneTransactions.aggregate(pipeline);
   };
   ```

   The function uses the following aggregation pipeline stages to update `monthlyPhoneTransactions`:

   - The [`$match`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/match.md#mongodb-pipeline-pipe.-match) stage filters the data to process only those sales that were completed over the `Phone`.

   - The [`$group`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/group.md#mongodb-pipeline-pipe.-group) stage groups the sales information by the year-month. This stage outputs documents of the form:

     ```javascript
     { "_id" : "<YYYY-mm>", "sales_quantity" : <num>, "sales_amount" : <NumberDecimal> }
     ```

   - The [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage changes the data type of the `sales_price` field to `double`. MongoDB Search `$search` operators don't support the `Decimal128` data type. Changing the `sales_price` field's data type allows you to query this field using MongoDB Search indexes.

   - The [`$merge`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#mongodb-pipeline-pipe.-merge) stage writes the output to the `monthlyPhoneTransactions` collection.

     [Based](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#std-label-merge-on) on the `_id` field, the stage checks if the document in the aggregation results [matches](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#std-label-merge-whenMatched) an existing document in the collection:

     - When MongoDB Search finds a [match](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#std-label-merge-whenMatched) (that is, a document with the same year-month already exists in the collection), MongoDB Search [replaces](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#std-label-merge-whenMatched-replace) the existing document with the document from the aggregation results as specified in the stage.

     - When MongoDB Search doesn't find a [match](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge.md#std-label-merge-whenNotMatched), MongoDB Search inserts the document from the aggregation results into the collection as specified in the stage.

5. Test the Function.

   Click the Run button in the lower right-hand corner of the Function Editor to create the `monthlyPhoneTransactions` materialized view.

   The Result tab at the bottom of the Function Editor reflects the execution status of the function. Click Save Draft.

#### Create the updateMonthlyPurchaseOrders Trigger

1. In Atlas, go to the Triggers page for your project.

   If it's not already displayed, select the organization that contains your project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your project from the Projects menu in the navigation bar.

   In the sidebar, click Triggers under the Streaming Data heading.

   The [Triggers](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Ftriggers) page displays.

2. Click Add a Trigger to open the Trigger configuration page.

3. Enter configuration values for the Trigger.

   | UI Field Name | Configuration |
   | --- | --- |
   | Trigger Type | Select Scheduled. |
   | Name | Specify `updateMonthlySales`. |
   | Schedule Type | Select Basic.For Repeat once by, select `Day of the Month` and set the value to your preferred date.Alternatively, for testing purposes, set Repeat once by dropdown to a more frequent occurrence, such as Minute or Hour |

4. Create the Function.

   ###### How the `updateMonthlyPurchaseOrders` Function Works

   The `updateMonthlyPurchaseOrders` function adds cumulative monthly purchase order information to the `monthlyPhoneTransactions` materialized view. The function updates the monthly purchase order information for purchase orders conducted over the phone. The following example defines the function:

   ```javascript
   exports = function(){

      var pipeline = [
        { $match: {purchaseMethod: "Phone"} },
        { $unwind: {path: "$items"}},
        { $group: {
         _id: { $dateToString:
         { format: "%Y-%m", date: "$saleDate" } },
         sales_quantity: { $sum: "$items.quantity"},
         sales_price: { $sum: "$items.price"}
        }},
        { $set: { sales_price: { $toDouble: "$sales_price"}}},
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ]

      var monthlyPhoneTransactions = context.services.get("mongodb-atlas").db("sample_supplies").collection("purchaseOrders");

      return monthlyPhoneTransactions.aggregate(pipeline);
   };
   ```

   The `updateMonthlyPurchaseOrders` function uses the same aggregation pipeline stages to update `monthlyPhoneTransactions` as the `updateMonthlySales` function.

5. Test the Function.

   Click the Run button in the lower right-hand corner of the Function Editor to update the `monthlyPhoneTransactions` materialized view.

   The Result tab at the bottom of the Function Editor reflects the execution status of the function.

   The `updateMonthlyPurchaseOrders` function refreshes the `monthlyPhoneTransactions` materialized view with the January 2018 purchase order data.

6. Click Save.

7. Review and deploy the `Sales-App` application draft.

8. Use [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) to query the `monthlyPhoneTransactions` collection to confirm the update:

   ```sh
   db.monthlyPhoneTransactions.find().sort( { _id: -1} )
   ```

   **Output:**

   ```json
   {
     _id: '2018-01',
     sales_quantity: 66,
     sales_price: Decimal128("1407.10")
   }
   ```

   The `monthlyPhoneTransactions` materialized view shows the newly added data. The top result reflects that the most recent transaction took place in January 2018.

9. Review and deploy the `Sales-App` application draft.

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. In Atlas, go to the Clusters page for your project.

   If it's not already displayed, select the organization that contains your desired project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your desired project from the Projects menu in the navigation bar.

   In the sidebar, click Clusters under the Database heading.

   The [Clusters](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters) page displays.

2. In Atlas, go to the Search & Vector Search page for your cluster.

   You can go the MongoDB Search page from the Search & Vector Search option, or the Data Explorer.

   ### Search & Vector Search

   If it's not already displayed, select the organization that contains your project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your project from the Projects menu in the navigation bar.

   In the sidebar, click Search & Vector Search under the Database heading.

   - If you have no clusters, click Create cluster to create one. To learn more, see [Create an Atlas Infinite or Atlas Core Cluster.](https://www.mongodb.com/docs/atlas/tutorial/create-new-cluster.md#std-label-create-new-cluster)

   - If your project has multiple clusters, select the cluster you want to use from the Select cluster dropdown, then click Go to Search.

   The [Search & Vector Search](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters%2FatlasSearch%2F%3Ccluster%3E) page displays.

3. Click Create Search Index.

4. Start your index configuration.

   Make the following selections on the page and then click Next.

   | Search Type | Select the MongoDB Search index type. |
   | --- | --- |
   | Index Name and Data Source | Specify the following information: Index Name: `monthlyPhoneTransactions`; Database and Collection:`sample_supplies` database; `monthlyPhoneTransactions` collection |
   | Configuration Method | For a guided experience, select Visual Editor.To edit the raw index definition, select JSON Editor. |

   IMPORTANT: The default index name varies by index type: your MongoDB Search index is named `default`, your Vector Search index is named `vector_index`, and your Automated Embedding index is named `autoembed_index`. If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

5. Specify an index definition.

   The following index definition dynamically indexes the fields of [supported types](https://www.mongodb.com/docs/search/indexes/define-field-mappings.md#std-label-bson-data-chart) in the collection. You can use the MongoDB Search Visual Editor or the MongoDB Search JSON
   Editor in the Atlas user interface to create the index.

   ### Visual Editor

   Review the default index definition for the collection.

6. Click Create Search Index.

7. Close the You're All Set! Modal Window.

   A modal window displays to let you know your index is building. Click the Close button.

8. Wait for the index to finish building.

   The index should take about one minute to build. While it is building, the Status column reads `Build in
               Progress`. When it is finished building, the Status column reads `Active`.

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. In Atlas, go to the Clusters page for your project.

   If it's not already displayed, select the organization that contains your desired project from the  Organizations menu in the navigation bar.

   If it's not already displayed, select your desired project from the Projects menu in the navigation bar.

   In the sidebar, click Clusters under the Database heading.

   The [Clusters](https://cloud.mongodb.com/go?l=https%3A%2F%2Fcloud.mongodb.com%2Fv2%2F%3Cproject%3E%23%2Fclusters) page displays.

2. Go to the Aggregation page for your cluster.

   Click the Browse Collections button for your cluster.

   Expand the sample\_supplies database and click the monthlyPhoneTransactions collection.

   Click the Aggregation tab for the collection.

3. Run a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Click the TEXT view on the Aggregation page. Then, copy and paste the following code into the pipeline to run the query:

   ```js
   [
     {
       $search: {
         "index": "monthlySalesIndex",
         "range": {
           "gt": 10000,
           "path": ["sales_price"]
         }
       }
     },
     {
       $count: 'months_w_over_10000'
     }
   ]
   ```

   **Output:**

   ```text
   {
     "months_w_over_10000": 4
   }
   ```

4. Interpret your query results.

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` for this project:

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Define the index.

   Create a `create_index.c` file in your project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(atlas-search-index LANGUAGES C)
   add_executable (index.out create_index.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created!
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.c` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.c` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       bson_t *lookup;
       bson_t *lookup_pipeline;
       bson_t *search;
       bson_t *compound;
       bson_t *must_array;
       bson_t *should_array;
       bson_t *query_string;
       bson_t *range;
       bson_t *limit_stage;
       bson_t *project_stage;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_analytics", "customers");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = bson_new();
       
       /* Create the $lookup stage */
       lookup = BCON_NEW("$lookup", "{",
           "from", BCON_UTF8("accounts"),
           "localField", BCON_UTF8("accounts"),
           "foreignField", BCON_UTF8("account_id"),
           "as", BCON_UTF8("purchases"),
           "pipeline", "[",
               /* First stage in lookup pipeline: $search */
               "{",
                   "$search", "{",
                       "index", BCON_UTF8("lookup-with-search-tutorial"),
                       "compound", "{",
                           "must", "[",
                               "{",
                                   "queryString", "{",
                                       "defaultPath", BCON_UTF8("products"),
                                       "query", BCON_UTF8("products: (CurrencyService AND InvestmentStock)"),
                                   "}",
                               "}",
                           "]",
                           "should", "[",
                               "{",
                                   "range", "{",
                                       "path", BCON_UTF8("limit"),
                                       "gte", BCON_INT64(5000),
                                       "lte", BCON_INT64(10000),
                                   "}",
                               "}",
                           "]",
                       "}",
                   "}",
               "}",
               /* $limit stage in lookup pipeline */
               "{",
                   "$limit", BCON_INT64(5),
               "}",
               /* $project stage in lookup pipeline */
               "{",
                   "$project", "{",
                       "_id", BCON_INT32(0),
                       "address", BCON_INT32(0),
                       "birthdate", BCON_INT32(0),
                       "username", BCON_INT32(0),
                       "tier_and_details", BCON_INT32(0),
                   "}",
               "}",
           "]",
       "}");
       
       bson_append_document(pipeline, "0", 1, lookup);
       
       /* $limit stage */
       limit_stage = BCON_NEW("$limit", BCON_INT64(5));
       bson_append_document(pipeline, "1", 1, limit_stage);
       
       /* $project stage */
       project_stage = BCON_NEW("$project", "{",
           "_id", BCON_INT32(0),
           "address", BCON_INT32(0),
           "birthdate", BCON_INT32(0),
           "username", BCON_INT32(0),
           "tier_and_details", BCON_INT32(0),
       "}");
       bson_append_document(pipeline, "2", 1, project_stage);

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(lookup);
       bson_destroy(limit_stage);
       bson_destroy(project_stage);
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(atlas-search-index LANGUAGES C)
   add_executable (index.out lookup-with-search-query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)
   ```

5. Run the command to query your collection.

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```none
   { "name" : "Lindsay Cowan", "email" : "cooperalexis@hotmail.com", "accounts" : [ { "$numberInt" : "116508" } ], "purchases" : [ ] }
   { "name" : "Dr. Angela Brown", "email" : "michaelespinoza@gmail.com", "accounts" : [ { "$numberInt" : "571880" } ], "purchases" : [ ] }
   { "name" : "Brian Flores", "email" : "april04@gmail.com", "accounts" : [ { "$numberInt" : "550665" }, { "$numberInt" : "321695" } ], "purchases" : [ { "account_id" : { "$numberInt" : "321695" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock" ] } ] }
   { "name" : "Shirley Rodriguez", "email" : "jonathan95@yahoo.com", "accounts" : [ { "$numberInt" : "784245" }, { "$numberInt" : "896066" }, { "$numberInt" : "991412" }, { "$numberInt" : "951840" } ], "purchases" : [ { "account_id" : { "$numberInt" : "991412" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock" ] }, { "account_id" : { "$numberInt" : "951840" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock" ] }, { "account_id" : { "$numberInt" : "896066" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock" ] } ] }
   { "name" : "Clinton Shelton", "email" : "acook@gmail.com", "accounts" : [ { "$numberInt" : "602560" }, { "$numberInt" : "986196" }, { "$numberInt" : "51080" }, { "$numberInt" : "690617" }, { "$numberInt" : "225602" } ], "purchases" : [ ] }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` for this project:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Define the index.

   Create a `create_indexes.c` file in your project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (index.out create_indexes.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created on the companies collection!
   Index created on the inspections collection!
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.c` in your project directory.

2. Copy and paste the query into the `search-with-unionwith-query.c` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_training", "companies");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = BCON_NEW (
           "pipeline", "[",
               "{",
                   "$search", "{",
                       "text", "{",
                           "query", "Mobile",
                           "path", "name",
                       "}",
                   "}",
               "}",
               "{",
                   "$project", "{",
                       "score", "{", "$meta", "searchScore", "}",
                       "_id", BCON_INT32(0),
                       "number_of_employees", BCON_INT32(1),
                       "founded_year", BCON_INT32(1),
                       "name", BCON_INT32(1),
                   "}",
               "}",
               "{",
                   "$set", "{",
                       "source", "companies",
                   "}",
               "}",
               "{",
                   "$limit", BCON_INT32(3),
               "}",
               "{",
                   "$unionWith", "{",
                       "coll", "inspections",
                       "pipeline", "[",
                           "{",
                               "$search", "{",
                                   "text", "{",
                                       "query", "Mobile",
                                       "path", "business_name",
                                   "}",
                               "}",
                           "}",
                           "{",
                               "$set", "{",
                                   "source", "inspections",
                               "}",
                           "}",
                           "{",
                               "$project", "{",
                                   "score", "{", "$meta", "searchScore", "}",
                                   "source", BCON_INT32(1),
                                   "_id", BCON_INT32(0),
                                   "business_name", BCON_INT32(1),
                                   "address", BCON_INT32(1),
                               "}",
                           "}",
                           "{",
                               "$limit", BCON_INT32(3),
                           "}",
                           "{",
                               "$sort", "{",
                                   "score", BCON_INT32(-1),
                               "}",
                           "}",
                       "]",
                   "}",
               "}",
           "]"
       );

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(
           collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (unionwith.out search-with-unionwith-query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(unionwith.out mongoc::mongoc)
   ```

5. Run the command to query your collection.

   ### Basic Example

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target unionwith.out
   ./cmake-build/unionwith.out
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : { "$numberInt" : "21" }, "founded_year" : { "$numberInt" : "2006" }, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "name" : "Pulse Mobile", "number_of_employees" : null, "founded_year" : null, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "name" : "T-Mobile", "number_of_employees" : null, "founded_year" : null, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : { "$numberInt" : "11209" }, "street" : "86TH ST", "number" : { "$numberInt" : "440" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : { "$numberInt" : "10458" }, "street" : "E FORDHAM RD", "number" : { "$numberInt" : "261" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : { "$numberInt" : "11419" }, "street" : "LIBERTY AVE", "number" : { "$numberInt" : "12207" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` for this project:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Definethe `purchaseOrders` collection documents.

   Create a `create-collection.c` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *doc1, *doc2;
       bson_t *query;
       bson_t *sort;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_supplies", "purchaseOrders");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the first document */
       doc1 = BCON_NEW(
           "saleDate", BCON_DATE_TIME(1516741609506), /* 2018-01-23T21:06:49.506Z */
           "items", "[", 
               "{", 
                   "name", BCON_UTF8("printer paper"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(40.01), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(35.29), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("pens"), 
                   "tags", "[", 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(56.12), 
                   "quantity", BCON_INT32(5), 
               "}", 
               "{", 
                   "name", BCON_UTF8("backpack"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("travel"), 
                       BCON_UTF8("kids"), 
                   "]", 
                   "price", BCON_DOUBLE(77.71), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(18.47), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(19.95), 
                   "quantity", BCON_INT32(8), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(8.08), 
                   "quantity", BCON_INT32(3), 
               "}", 
               "{", 
                   "name", BCON_UTF8("binder"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("general"), 
                       BCON_UTF8("organization"), 
                   "]", 
                   "price", BCON_DOUBLE(14.16), 
                   "quantity", BCON_INT32(3), 
               "}", 
           "]",
           "storeLocation", BCON_UTF8("Denver"),
           "customer", "{", 
               "gender", BCON_UTF8("M"), 
               "age", BCON_INT32(42), 
               "email", BCON_UTF8("cauho@witwuta.sv"), 
               "satisfaction", BCON_INT32(4),
           "}",
           "couponUsed", BCON_BOOL(true),
           "purchaseMethod", BCON_UTF8("Phone")
       );
       
       /* Create the second document */
       doc2 = BCON_NEW(
           "saleDate", BCON_DATE_TIME(1516874462918), /* 2018-01-25T10:01:02.918Z */
           "items", "[", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(8.05), 
                   "quantity", BCON_INT32(10), 
               "}", 
               "{", 
                   "name", BCON_UTF8("binder"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("general"), 
                       BCON_UTF8("organization"), 
                   "]", 
                   "price", BCON_DOUBLE(28.31), 
                   "quantity", BCON_INT32(9), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(20.95), 
                   "quantity", BCON_INT32(3), 
               "}", 
               "{", 
                   "name", BCON_UTF8("laptop"), 
                   "tags", "[", 
                       BCON_UTF8("electronics"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("office"), 
                   "]", 
                   "price", BCON_DOUBLE(866.5), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(33.09), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("printer paper"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(37.55), 
                   "quantity", BCON_INT32(1), 
               "}", 
               "{", 
                   "name", BCON_UTF8("backpack"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("travel"), 
                       BCON_UTF8("kids"), 
                   "]", 
                   "price", BCON_DOUBLE(83.28), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("pens"), 
                   "tags", "[", 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(42.9), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(16.68), 
                   "quantity", BCON_INT32(2), 
               "}", 
           "]",
           "storeLocation", BCON_UTF8("Seattle"),
           "customer", "{", 
               "gender", BCON_UTF8("M"), 
               "age", BCON_INT32(50), 
               "email", BCON_UTF8("keecade@hem.uy"), 
               "satisfaction", BCON_INT32(5),
           "}",
           "couponUsed", BCON_BOOL(false),
           "purchaseMethod", BCON_UTF8("Phone")
       );

       /* Insert the documents */
       if (!mongoc_collection_insert_one(collection, doc1, NULL, NULL, &error)) {
           fprintf(stderr, "Insert failed: %s\n", error.message);
           return 1;
       }
       
       if (!mongoc_collection_insert_one(collection, doc2, NULL, NULL, &error)) {
           fprintf(stderr, "Insert failed: %s\n", error.message);
           return 1;
       }
       
       printf("Successfully inserted purchase order documents.\n");
       
       /* Query the new collection */
       query = BCON_NEW(NULL);  /* Empty query to match all documents */
       sort = BCON_NEW("saleDate", BCON_INT32(-1));
       
       cursor = mongoc_collection_find_with_opts(collection, query, 
                                              BCON_NEW("sort", BCON_DOCUMENT(sort)), NULL);
       
       printf("\nQuery results:\n");
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }
       
       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(doc1);
       bson_destroy(doc2);
       bson_destroy(query);
       bson_destroy(sort);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (index.out create_indexes.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Populate the `purchaseOrders` collection and display its documents.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target collection.out
   ./cmake-build/collection.out
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { "_id" : { "$oid" : "..." }, "saleDate" : { "$date" : { "$numberLong" : "1516874462918" } }, "items" : [ { "quantity" : { "$numberInt" : "10" } }, { "quantity" : { "$numberInt" : "9" } }, { "quantity" : { "$numberInt" : "3" } }, { "quantity" : { "$numberInt" : "4" } }, { "quantity" : { "$numberInt" : "4" } }, { "quantity" : { "$numberInt" : "1" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "4" } } ], "storeLocation" : "Seattle", "customer" : { "gender" : "M", "age" : { "$numberInt" : "50" }, "email" : "keecade@hem.uy", "satisfaction" : { "$numberInt" : "5" } }, "couponUsed" : false, "purchaseMethod" : "Phone" }
   { "_id" : { "$oid" : "..." }, "saleDate" : { "$date" : { "$numberLong" : "1516741609506" } }, "items" : [ { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "5" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "8" } }, { "quantity" : { "$numberInt" : "3" } } ], "storeLocation" : "Denver", "customer" : { "gender" : "M", "age" : { "$numberInt" : "42" }, "email" : "cauho@witwuta.sv", "satisfaction" : { "$numberInt" : "4" } }, "couponUsed" : true, "purchaseMethod" : "Phone" }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```c
      #include <mongoc/mongoc.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      #include <unistd.h>

      /* Function to update the materialized view */
      void update_monthly_phone_transactions(mongoc_client_t *client, mongoc_collection_t *collection);

      int main(int argc, char *argv[])
      {
          mongoc_client_t *client;
          mongoc_collection_t *sales;
          mongoc_collection_t *purchase_orders;
          
          /* Initialize the MongoDB C Driver */
          mongoc_init();
          
          /* Connect to MongoDB */
          client = mongoc_client_new("<connection-string>");
          sales = mongoc_client_get_collection(client, "sample_supplies", "sales");
          purchase_orders = mongoc_client_get_collection(client, "sample_supplies", "purchaseOrders");
          
          /* Update immediately on startup */
          update_monthly_phone_transactions(client, sales);
          update_monthly_phone_transactions(client, purchase_orders);
          printf("Initial update completed. Materialized view is ready.\n");
          
          /* Example of a simple scheduler that updates monthly */
          time_t now;
          struct tm *local_time;
          int day_of_month = 1; /* Update on the 1st of each month */
          
          while (1) {
              time(&now);
              local_time = localtime(&now);
              
              if (local_time->tm_mday == day_of_month && local_time->tm_hour == 0 && local_time->tm_min == 0) {
                  /* It's midnight on the 1st of the month - update the view */
                  update_monthly_phone_transactions(client, sales);
                  update_monthly_phone_transactions(client, sales);
                  printf("Scheduled update completed at %s", ctime(&now));
                  
                  /* Sleep for an hour to avoid multiple updates */
                  sleep(3600);
              } else {
                  /* Check again in a minute */
                  sleep(60);
              }
          }
          
          /* Clean up */
          mongoc_client_destroy(client);
          mongoc_cleanup();
          
          return 0;
      }

     void update_monthly_phone_transactions(mongoc_client_t *client, mongoc_collection_t *collection)
      {
          bson_t *pipeline;
          mongoc_cursor_t *cursor;
          const bson_t *doc;
          bson_error_t error;
          
          /* Create the aggregation pipeline */
          pipeline = BCON_NEW (
              "pipeline", "[",
                  "{", "$match", "{", "purchaseMethod", BCON_UTF8("Phone"), "}", "}",
                  "{", "$unwind", "{", "path", BCON_UTF8("$items"), "}", "}",
                  "{", "$group", "{",
                      "_id", "{", "$dateToString", "{", 
                          "format", BCON_UTF8("%Y-%m"), 
                          "date", BCON_UTF8("$saleDate"),
                      "}", "}",
                      "sales_quantity", "{", "$sum", BCON_UTF8("$items.quantity"), "}",
                      "sales_price", "{", "$sum", BCON_UTF8("$items.price"), "}",
                  "}", "}",
                  "{", "$set", "{", "sales_price", "{", "$toDouble", BCON_UTF8("$sales_price"), "}", "}", "}",
                  "{", "$merge", "{", "into", BCON_UTF8("monthlyPhoneTransactions"), "whenMatched", BCON_UTF8("replace"), "}", "}",
              "]"
          );
          
          /* Run the aggregation */
          cursor = mongoc_collection_aggregate(
              collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

          while (mongoc_cursor_next(cursor, &doc)) {
           /* Consume the cursor to execute the pipeline */
          }
          
          
          if (mongoc_cursor_error(cursor, &error)) {
              fprintf(stderr, "Aggregation error: %s\n", error.message);
          }
          
          /* Clean up */
          bson_destroy(pipeline);
          mongoc_cursor_destroy(cursor);
          mongoc_collection_destroy(collection);
      }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file to add a new target for the view updater:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (view_updater.out create_update_view.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(view_updater.out mongoc::mongoc)
   ```

3. Run the application.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target view_updater.out
   ./cmake-build/view_updater.out
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (index.out create_index.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)
   ```

3. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created!
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       bson_t *lookup;
       bson_t *lookup_pipeline;
       bson_t *search;
       bson_t *compound;
       bson_t *must_array;
       bson_t *should_array;
       bson_t *query_string;
       bson_t *range;
       bson_t *limit_stage;
       bson_t *project_stage;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_analytics", "customers");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = bson_new();
       
       /* Create the $lookup stage */
       lookup = BCON_NEW("$lookup", "{",
           "from", BCON_UTF8("accounts"),
           "localField", BCON_UTF8("accounts"),
           "foreignField", BCON_UTF8("account_id"),
           "as", BCON_UTF8("purchases"),
           "pipeline", "[",
               /* First stage in lookup pipeline: $search */
               "{",
                   "$search", "{",
                       "index", BCON_UTF8("lookup-with-search-tutorial"),
                       "compound", "{",
                           "must", "[",
                               "{",
                                   "queryString", "{",
                                       "defaultPath", BCON_UTF8("products"),
                                       "query", BCON_UTF8("products: (CurrencyService AND InvestmentStock)"),
                                   "}",
                               "}",
                           "]",
                           "should", "[",
                               "{",
                                   "range", "{",
                                       "path", BCON_UTF8("limit"),
                                       "gte", BCON_INT64(5000),
                                       "lte", BCON_INT64(10000),
                                   "}",
                               "}",
                           "]",
                       "}",
                   "}",
               "}",
               /* $limit stage in lookup pipeline */
               "{",
                   "$limit", BCON_INT64(5),
               "}",
               /* $project stage in lookup pipeline */
               "{",
                   "$project", "{",
                       "_id", BCON_INT32(0),
                       "address", BCON_INT32(0),
                       "birthdate", BCON_INT32(0),
                       "username", BCON_INT32(0),
                       "tier_and_details", BCON_INT32(0),
                   "}",
               "}",
           "]",
       "}");
       
       bson_append_document(pipeline, "0", 1, lookup);
       
       /* $limit stage */
       limit_stage = BCON_NEW("$limit", BCON_INT64(5));
       bson_append_document(pipeline, "1", 1, limit_stage);
       
       /* $project stage */
       project_stage = BCON_NEW("$project", "{",
           "_id", BCON_INT32(0),
           "address", BCON_INT32(0),
           "birthdate", BCON_INT32(0),
           "username", BCON_INT32(0),
           "tier_and_details", BCON_INT32(0),
       "}");
       bson_append_document(pipeline, "2", 1, project_stage);

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(lookup);
       bson_destroy(limit_stage);
       bson_destroy(project_stage);
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (query.out query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(query.out mongoc::mongoc)
   ```

3. Run the query.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target query.out
   ./cmake-build/query.out
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` and initialize your project in that directory:

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   dotnet new console

   # Add the MongoDB .NET/C# Driver to your project
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the index.

   Paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Copy and paste the query into the `Program.cs` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Bson.Serialization.Conventions;
   using MongoDB.Driver;
   using MongoDB.Driver.Core;
   using MongoDB.Driver.Search;

   public class LookupWithSearch{

     static void Main(string[] args) {// allow automapping of the camelCase database fields to our MovieDocument
       var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
       ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

       // connect to your MongoDB deployment
       var mongoClient = new MongoClient("<connection-string>");
       
       // define namespace
       var analyticsDatabase = mongoClient.GetDatabase("sample_analytics");
       var accountsCollection = analyticsDatabase.GetCollection<AccountDocument>("accounts");
       var customersCollection = analyticsDatabase.GetCollection<CustomerDocument>("customers");

       // define pipeline stages
       var lookupStage = new BsonDocument("$lookup", new BsonDocument{
         { "from", "accounts" }, { "localField", "accounts" }, { "foreignField", "account_id" }, 
         { "as", "purchases" }, { "pipeline", new BsonArray{
           new BsonDocument("$search", new BsonDocument{
             { "index", "lookup-with-search-tutorial" }, { "compound", new BsonDocument{
               { "must", new BsonArray{
                 new BsonDocument("queryString", new BsonDocument{
                   { "defaultPath", "products" }, { "query", "products: (CurrencyService AND InvestmentStock)" }
                 })
               }}, 
               { "should", new BsonArray{
                 new BsonDocument("range", new BsonDocument{
                   { "path", "limit" }, { "gte", 5000 }, { "lte", 10000 }
                 })
               }}
             }}
           })
         }}
       });
       var projectStage1 = new BsonDocument("$project", new BsonDocument("_id", 0));
       var limitStage = new BsonDocument("$limit", 5);
       var projectStage2 = new BsonDocument("$project", new BsonDocument{
         { "_id", 0 }, { "address", 0 }, { "birthdate", 0 }, { "username", 0 }, { "tier_and_details", 0 }
       });
       var aggregationPipeline = new List<BsonDocument> {lookupStage, projectStage1, limitStage, projectStage2};

       // run pipeline
       var results = customersCollection.Aggregate<BsonDocument>(aggregationPipeline).ToList();

       // print results
       foreach (var acct in results) {
           Console.WriteLine(acct.ToJson());
       }
     }            
   }

   // define fields in the accounts collection
   [BsonIgnoreExtraElements]
   public class AccountDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public string Id { get; set; }

       [BsonElement("account_id")]
       public int AccountId { get; set; }

       [BsonElement("limit")]
       public int Limit { get; set; }
   }

   // define fields in the customers collection
   [BsonIgnoreExtraElements]
   public class CustomerDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public ObjectId Id { get; set; }

       [BsonElement("name")]
       public string Name { get; set; }

       [BsonElement("email")]
       public string Email { get; set; }

       [BsonElement("active")]
       public bool Active { get; set; }

       [BsonElement("accounts")]
       public List<int> Accounts { get; set; }
   }

   // define new array field for matching documents
   public class CustomerLookedUp: CustomerDocument{
       public List<CustomerDocument> Purchases { get; set; }
   }
   ```

2. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Compile and run the `Program.cs` file.

   ```bash
   dotnet run lookup-with-search.csproj
   ```

   **Output:**

   ```javascript
   {
     "name" : "Elizabeth Ray",
     "email" : "arroyocolton@gmail.com",
     "active" : true,
     "accounts" : [371138, 324287, 276528, 332179, 422649,
     387979],
     "purchases" : [
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee58162402"),
          "account_id" : 422649,
          "limit" : 10000,
          "products" : ["CurrencyService", "InvestmentStock"]
        },
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee581623a9"),
          "account_id" : 324287,
          "limit" : 10000,
          "products" : ["Commodity", "CurrencyService", "Derivatives", "InvestmentStock"]
        },
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee58162400"),
          "account_id" : 332179,
          "limit" : 10000,
          "products" : ["Commodity", "CurrencyService", "InvestmentFund", "Brokerage", "InvestmentStock"]
        }
     ]
   }
   {
     "name" : "Lindsay Cowan",
     "email" : "cooperalexis@hotmail.com",
     "accounts" : [116508],
     "purchases" : []
   }
   {
     "name" : "Katherine David",
     "email" : "timothy78@hotmail.com",
     "accounts" : [462501, 228290, 968786, 515844, 377292],
     "purchases" : [
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623c9"),
         "account_id" : 228290,
         "limit" : 10000,
         "products" : ["CurrencyService", "InvestmentStock", "InvestmentFund", "Brokerage"] },
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623cb"),
         "account_id" : 515844,
         "limit" : 10000,
         "products" : ["Commodity", "CurrencyService", "InvestmentFund", "Brokerage", "InvestmentStock"]
       }
     ]
   }
   {
     "name" : "Leslie Martinez",
     "email" : "tcrawford@gmail.com",
     "accounts" : [170945, 951849],
     "purchases" : []
   }
   {
     "name" : "Brad Cardenas",
     "email" : "dustin37@yahoo.com",
     "accounts" : [721914, 817222, 973067, 260799, 87389],
     "purchases" : [
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623d6"),
         "account_id" : 87389,
         "limit" : 10000,
         "products" : ["CurrencyService", "InvestmentStock"] },
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623d5"),
         "account_id" : 260799,
         "limit" : 10000,
         "products" : ["Brokerage", "InvestmentStock", "Commodity", "CurrencyService"]
       }
     ]
   }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` and initialize your project in that directory:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   dotnet new console
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the index.

   Paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Copy and paste the query into the `Program.cs` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;
   using MongoDB.Driver.Search;

   public class Program
   {
       public static void Main(string[] args)
       {
           // connect to your MongoDB deployment
           string connectionString = "<connection-string>";
           var client = new MongoClient(connectionString);
           
           // define namespace
           var database = client.GetDatabase("sample_training");
           var collection = database.GetCollection<BsonDocument>("companies");
           
           // define pipeline stage
           var searchStage1 = new BsonDocument("$search", new BsonDocument{{ "text", new BsonDocument
               {{ "query", "Mobile" },{ "path", "name" }}
           }});
           var projectStage1 = new BsonDocument("$project", new BsonDocument{
               { "score", new BsonDocument("$meta", "searchScore") },
               { "_id", 0 },{ "number_of_employees", 1 },{ "founded_year", 1 },{ "name", 1 }
           });
           var setStage1 = new BsonDocument("$set", new BsonDocument{{ "source", "companies" }});
           var limitStage1 = new BsonDocument("$limit", 3);
           
           // define subpipeline
           var searchStage2 = new BsonDocument("$search", new BsonDocument{{ "text", new BsonDocument
               {{ "query", "Mobile" },{ "path", "business_name" }}
           }});
           var setStage2 = new BsonDocument("$set", new BsonDocument{ { "source", "inspections" } });
           var projectStage2 = new BsonDocument("$project", new BsonDocument{
               { "score", new BsonDocument("$meta", "searchScore") }, 
               { "source", 1 }, { "_id", 0 }, { "business_name", 1 }, { "address", 1 }
           });
           var limitStage2 = new BsonDocument("$limit", 3);
           var sortStage2 = new BsonDocument("$sort", new BsonDocument{{ "score", -1 }});
           var unionWithPipeline = new List<BsonDocument>{searchStage2, setStage2, projectStage2, limitStage2, sortStage2};
           var unionWithStage = new BsonDocument("$unionWith", new BsonDocument
           {
               { "coll", "inspections" },
               { "pipeline", new BsonArray(unionWithPipeline) }
           });
           var aggregationPipeline = new List<BsonDocument> {searchStage1, projectStage1, setStage1, limitStage1,unionWithStage};
           
           // run pipeline
           var result = collection.Aggregate<BsonDocument>(aggregationPipeline).ToList();
           
           //print results
           foreach (var document in result)
           {
               Console.WriteLine(document);
           }
       }
   }

   ```

2. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Compile and run the `Program.cs` file.

   ### Basic Example

   ```bash
   dotnet run search-with-unionwith.csproj
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : 21, "founded_year" : 2006, "score" : 2.0815043449401855, "source" : "companies" }
   { "name" : "Pulse Mobile", "number_of_employees" : null, "founded_year" : null, "score" : 2.0815043449401855, "source" : "companies" }
   { "name" : "T-Mobile", "number_of_employees" : null, "founded_year" : null, "score" : 2.0815043449401855, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : 11209, "street" : "86TH ST", "number" : 440 }, "source" : "inspections", "score" : 2.9009163379669189 }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : 10458, "street" : "E FORDHAM RD", "number" : 261 }, "source" : "inspections", "score" : 2.9009163379669189 }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : 11419, "street" : "LIBERTY AVE", "number" : 12207 }, "source" : "inspections", "score" : 2.9009163379669189 }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` and initialize your project in that directory:

   ```sh
   mkdir search-materialized-view
   cd search-materialized-view
   dotnet new console
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Paste the following code into the `Program.cs` file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Driver;
   using System;
   using System.Collections.Generic;

   public class CreateCollection {
       static void Main(string[] args) {
           // Connect to your MongoDB deployment
           var mongoClient = new MongoClient("<connection-string>");
           
           // Define namespace
           var suppliesDatabase = mongoClient.GetDatabase("sample_supplies");
           var purchaseOrdersCollection = suppliesDatabase.GetCollection<BsonDocument>("purchaseOrders");
           
           // Create first document
           var purchaseOrder1 = new BsonDocument
           {
               { "saleDate", new BsonDateTime(new DateTime(2018, 1, 23, 21, 6, 49, 506)) },
               { "items", new BsonArray
                   {
                       new BsonDocument
                       {
                           { "name", "printer paper" },
                           { "tags", new BsonArray { "office", "stationary" } },
                           { "price", 40.01 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 35.29 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "pens" },
                           { "tags", new BsonArray { "writing", "office", "school", "stationary" } },
                           { "price", 56.12 },
                           { "quantity", 5 }
                       },
                       new BsonDocument
                       {
                           { "name", "backpack" },
                           { "tags", new BsonArray { "school", "travel", "kids" } },
                           { "price", 77.71 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 18.47 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 19.95 },
                           { "quantity", 8 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 8.08 },
                           { "quantity", 3 }
                       },
                       new BsonDocument
                       {
                           { "name", "binder" },
                           { "tags", new BsonArray { "school", "general", "organization" } },
                           { "price", 14.16 },
                           { "quantity", 3 }
                       }
                   }
               },
               { "storeLocation", "Denver" },
               { "customer", new BsonDocument
                   {
                       { "gender", "M" },
                       { "age", 42 },
                       { "email", "cauho@witwuta.sv" },
                       { "satisfaction", 4 }
                   }
               },
               { "couponUsed", true },
               { "purchaseMethod", "Phone" }
           };
           
           // Create second document
           var purchaseOrder2 = new BsonDocument
           {
               { "saleDate", new BsonDateTime(new DateTime(2018, 1, 25, 10, 1, 2, 918)) },
               { "items", new BsonArray
                   {
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 8.05 },
                           { "quantity", 10 }
                       },
                       new BsonDocument
                       {
                           { "name", "binder" },
                           { "tags", new BsonArray { "school", "general", "organization" } },
                           { "price", 28.31 },
                           { "quantity", 9 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 20.95 },
                           { "quantity", 3 }
                       },
                       new BsonDocument
                       {
                           { "name", "laptop" },
                           { "tags", new BsonArray { "electronics", "school", "office" } },
                           { "price", 866.5 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 33.09 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "printer paper" },
                           { "tags", new BsonArray { "office", "stationary" } },
                           { "price", 37.55 },
                           { "quantity", 1 }
                       },
                       new BsonDocument
                       {
                           { "name", "backpack" },
                           { "tags", new BsonArray { "school", "travel", "kids" } },
                           { "price", 83.28 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "pens" },
                           { "tags", new BsonArray { "writing", "office", "school", "stationary" } },
                           { "price", 42.9 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 16.68 },
                           { "quantity", 2 }
                       }
                   }
               },
               { "storeLocation", "Seattle" },
               { "customer", new BsonDocument
                   {
                       { "gender", "M" },
                       { "age", 50 },
                       { "email", "keecade@hem.uy" },
                       { "satisfaction", 5 }
                   }
               },
               { "couponUsed", false },
               { "purchaseMethod", "Phone" }
           };
           
           // Insert the documents
           purchaseOrdersCollection.InsertOne(purchaseOrder1);
           purchaseOrdersCollection.InsertOne(purchaseOrder2);
           
           Console.WriteLine("Successfully inserted purchase order documents.");
           
           // Query the new collection
           var sort = Builders<BsonDocument>.Sort.Descending("saleDate");
           var findOptions = new FindOptions<BsonDocument> { Sort = sort };
           
           var results = purchaseOrdersCollection.Find(new BsonDocument()).Sort(sort).ToList();
           
           Console.WriteLine("\nQuery results:");
           foreach (var doc in results)
           {
               Console.WriteLine(doc.ToJson());
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { "_id" : ObjectId("..."), "saleDate" : ISODate("2018-01-25T10:01:02.918Z"), "items" : [...], "storeLocation" : "Seattle", "customer" : {...}, "couponUsed" : false, "purchaseMethod" : "Phone" }
   { "_id" : ObjectId("..."), "saleDate" : ISODate("2018-01-23T21:06:49.506Z"), "items" : [...], "storeLocation" : "Denver", "customer" : {...}, "couponUsed" : true, "purchaseMethod" : "Phone" }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Copy and paste the following code into the `Program.cs` file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;
   using System;
   using System.Collections.Generic;
   using System.Threading;
   using System.Threading.Tasks;

   namespace SearchMaterializedView
   {
       class Program
       {
           static async Task Main(string[] args)
           {
               // Connect to MongoDB
               var client = new MongoClient("<connection-string>");
               var database = client.GetDatabase("sample_supplies");
               var sales = database.GetCollection<BsonDocument>("sales");
               var purchaseOrders = database.GetCollection<BsonDocument>("purchaseOrders");
               
               // Update immediately on startup
               await UpdateMonthlyPhoneTransactions(client, sales);
               await UpdateMonthlyPhoneTransactions(client, purchaseOrders);
               Console.WriteLine("Initial update completed. Materialized view is ready.");
               
               // Example of a simple scheduler that updates monthly
               int dayOfMonth = 1; // Update on the 1st of each month
               
               while (true)
               {
                   DateTime now = DateTime.Now;
                   
                   if (now.Day == dayOfMonth && now.Hour == 0 && now.Minute == 0)
                   {
                       // It's midnight on the 1st of the month - update the view
                       await UpdateMonthlyPhoneTransactions(client, sales);
                       await UpdateMonthlyPhoneTransactions(client, purchaseOrders);
                       Console.WriteLine($"Scheduled update completed at {now}");
                       
                       // Sleep for an hour to avoid multiple updates
                       Thread.Sleep(TimeSpan.FromHours(1));
                   }
                   else
                   {
                       // Check again in a minute
                       Thread.Sleep(TimeSpan.FromMinutes(1));
                   }
               }
           }
           
           static async Task UpdateMonthlyPhoneTransactions(MongoClient client, IMongoCollection<BsonDocument> collection)
           {
               // Create the aggregation pipeline
               var pipeline = new BsonDocument[]
               {
                   new BsonDocument("$match", new BsonDocument("purchaseMethod", "Phone")),
                   new BsonDocument("$unwind", new BsonDocument("path", "$items")),
                   new BsonDocument("$group", new BsonDocument
                   {
                       { "_id", new BsonDocument("$dateToString", new BsonDocument
                           {
                               { "format", "%Y-%m" },
                               { "date", "$saleDate" }
                           })
                       },
                       { "sales_quantity", new BsonDocument("$sum", "$items.quantity") },
                       { "sales_price", new BsonDocument("$sum", "$items.price") }
                   }),
                   new BsonDocument("$set", new BsonDocument("sales_price", new BsonDocument("$toDouble", "$sales_price"))),
                   new BsonDocument("$merge", new BsonDocument
                   {
                       { "into", "monthlyPhoneTransactions" },
                       { "whenMatched", "replace" }
                   })
               };

               // Run the aggregation
               await collection.AggregateAsync<BsonDocument>(pipeline);
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following command to build and run this application:

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Paste the following code into the `Program.cs` file:

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Copy and paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Bson.Serialization.Conventions;
   using MongoDB.Driver;
   using MongoDB.Driver.Core;
   using MongoDB.Driver.Search;

   public class MaterializedViewSearchQuery {

     static void Main(string[] args) {
       // allow automapping of the camelCase database fields
       var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
       ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

       // connect to your MongoDB deployment
       var mongoClient = new MongoClient("<connection-string>");
       
       // define namespace
       var suppliesDatabase = mongoClient.GetDatabase("sample_supplies");
       var transactionsCollection = suppliesDatabase.GetCollection<BsonDocument>("monthlyPhoneTransactions");

       // define pipeline stages
       var searchStage = new BsonDocument("$search", new BsonDocument{
         { "index", "monthlySalesIndex" },
         { "range", new BsonDocument{
           { "gt", 10000 },
           { "path", new BsonArray{ "sales_price" } }
         }}
       });
       
       var countStage = new BsonDocument("$count", "months_w_over_10000");
       
       var aggregationPipeline = new List<BsonDocument> { searchStage, countStage };

       // run pipeline
       var results = transactionsCollection.Aggregate<BsonDocument>(aggregationPipeline).ToList();

       // print results
       foreach (var result in results) {
           Console.WriteLine(result.ToJson());
       }
     }            
   }

   // Define a class to represent the materialized view document structure if needed
   [BsonIgnoreExtraElements]
   public class MonthlyTransactionDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public string Id { get; set; }

       [BsonElement("month")]
       public string Month { get; set; }

       [BsonElement("sales_price")]
       public double SalesPrice { get; set; }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` for this project:

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the index.

   Create a `CreateIndex.cpp` file in your project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["accounts"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "lookup-with-search-tutorial";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   c++ --std=c++17 CreateIndex.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndex.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.cpp` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.cpp` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["customers"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // Create documents for the lookup pipeline stages
       auto search_doc = make_document(
           kvp("$search", make_document(
               kvp("index", "lookup-with-search-tutorial"),
               kvp("compound", make_document(
                   kvp("must", make_array(
                       make_document(
                           kvp("queryString", make_document(
                               kvp("defaultPath", "products"),
                               kvp("query", "products: (CurrencyService AND InvestmentStock)")
                           ))
                       )
                   )),
                   kvp("should", make_array(
                       make_document(
                           kvp("range", make_document(
                               kvp("path", "limit"),
                               kvp("gte", 5000),
                               kvp("lte", 10000)
                           ))
                       )
                   ))
               ))
           ))
       );
       
       
       auto limit_doc = make_document(kvp("$limit", 5));
       
       auto project_doc = make_document(
           kvp("$project", make_document(
               kvp("_id", 0),
               kvp("address", 0),
               kvp("birthdate", 0),
               kvp("username", 0),
               kvp("tier_and_details", 0)
           ))
       );
       
       auto lookup_pipeline_array = make_array(
           search_doc.view(),
           limit_doc.view(),
           project_doc.view()
       );
       
       pipeline.append_stage(make_document(
           kvp("$lookup", make_document(
               kvp("from", "accounts"),
               kvp("localField", "accounts"),
               kvp("foreignField", "account_id"),
               kvp("as", "purchases"),
               kvp("pipeline", lookup_pipeline_array.view())
           ))
       ));
       
       // Add $limit stage to the main pipeline
       pipeline.limit(5);
       
       // Add $project stage to the main pipeline
       pipeline.project(make_document(
           kvp("_id", 0),
           kvp("address", 0),
           kvp("birthdate", 0),
           kvp("username", 0),
           kvp("tier_and_details", 0)
       ));
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }
   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `lookup-with-search-query.cpp` file.

   ```bash
   c++ --std=c++17 lookup-with-search-query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./o.out
   ./o.out
   ```

   **Output:**

   ```javascript
   { "name" : "Lindsay Cowan", "email" : "cooperalexis@hotmail.com", "accounts" : [ 116508 ], "purchases" : [ ] }
   { "name" : "Dr. Angela Brown", "email" : "michaelespinoza@gmail.com", "accounts" : [ 571880 ], "purchases" : [ ] }
   { "name" : "Brian Flores", "email" : "april04@gmail.com", "accounts" : [ 550665, 321695 ], "purchases" : [ { "account_id" : 321695, "limit" : 10000, "products" : [ "Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock" ] } ] }
   { "name" : "Shirley Rodriguez", "email" : "jonathan95@yahoo.com", "accounts" : [ 784245, 896066, 991412, 951840 ], "purchases" : [ { "account_id" : 991412, "limit" : 10000, "products" : [ "CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock" ] }, { "account_id" : 951840, "limit" : 10000, "products" : [ "InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock" ] }, { "account_id" : 896066, "limit" : 10000, "products" : [ "Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock" ] } ] }
   { "name" : "Clinton Shelton", "email" : "acook@gmail.com", "accounts" : [ 602560, 986196, 51080, 690617, 225602 ], "purchases" : [ ] }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

#### Create the MongoDB Search Index

In this section, you will create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` for this project:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the index.

   Create a `CreateIndexes.cpp` file in your project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["accounts"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "lookup-with-search-tutorial";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   c++ --std=c++17 CreateIndexes.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndexes.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.cpp` in your project directory.

2. Copy and paste the query into the `search-with-unionwith-query.cpp` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_training"];
       auto collection = db["companies"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // $search stage
       pipeline.append_stage(make_document(
           kvp("$search", make_document(
               kvp("text", make_document(
                   kvp("query", "Mobile"),
                   kvp("path", "name")
               ))
           ))
       ));
       
       // $project stage
       pipeline.append_stage(make_document(
           kvp("$project", make_document(
               kvp("score", make_document(kvp("$meta", "searchScore"))),
               kvp("_id", 0),
               kvp("number_of_employees", 1),
               kvp("founded_year", 1),
               kvp("name", 1)
           ))
       ));
       
       // $set stage to add source
       pipeline.append_stage(make_document(
           kvp("$set", make_document(
               kvp("source", "companies")
           ))
       ));
       
       // $limit stage
       pipeline.append_stage(make_document(
           kvp("$limit", 3)
       ));
       
       // Create the lookup pipeline for the $unionWith stage
       auto lookup_pipeline = mongocxx::pipeline{};
       
       lookup_pipeline.append_stage(make_document(
           kvp("$search", make_document(
               kvp("text", make_document(
                   kvp("query", "Mobile"),
                   kvp("path", "business_name")
               ))
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$set", make_document(
               kvp("source", "inspections")
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$project", make_document(
               kvp("score", make_document(kvp("$meta", "searchScore"))),
               kvp("source", 1),
               kvp("_id", 0),
               kvp("business_name", 1),
               kvp("address", 1)
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$limit", 3)
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$sort", make_document(
               kvp("score", -1)
           ))
       ));
       
       // Add $unionWith stage to the main pipeline
       pipeline.append_stage(make_document(
           kvp("$unionWith", make_document(
               kvp("coll", "inspections"),
               kvp("pipeline", lookup_pipeline.view_array())
           ))
       ));
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `search-with-unionwith-query.cpp` file.

   ### Basic Example

   ```bash
   c++ --std=c++17 search-with-unionwith-query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./o.out
   ./o.out
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : 21, "founded_year" : 2006, "score" : 3.33040714263916, "source" : "companies" }
   { "name" : "Pulse Mobile", "score" : 3.33040714263916, "source" : "companies" }
   { "name" : "T-Mobile", "score" : 3.33040714263916, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : 11209, "street" : "86TH ST", "number" : 440 }, "score" : 2.900916337966919, "source" : "inspections" }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : 10458, "street" : "E FORDHAM RD", "number" : 261 }, "score" : 2.900916337966919, "source" : "inspections" }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : 11419, "street" : "LIBERTY AVE", "number" : 12207 }, "score" : 2.900916337966919, "source" : "inspections" }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` for this project:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.cpp` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <bsoncxx/types.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/options/find.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;
   using bsoncxx::types::b_date;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["purchaseOrders"];
       
       // Create the first document
       auto doc1 = make_document(
           kvp("saleDate", b_date(std::chrono::milliseconds(1516741609506))), // 2018-01-23T21:06:49.506Z
           kvp("items", make_array(
               make_document(
                   kvp("name", "printer paper"),
                   kvp("tags", make_array("office", "stationary")),
                   kvp("price", 40.01),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 35.29),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "pens"),
                   kvp("tags", make_array("writing", "office", "school", "stationary")),
                   kvp("price", 56.12),
                   kvp("quantity", 5)
               ),
               make_document(
                   kvp("name", "backpack"),
                   kvp("tags", make_array("school", "travel", "kids")),
                   kvp("price", 77.71),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 18.47),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 19.95),
                   kvp("quantity", 8)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 8.08),
                   kvp("quantity", 3)
               ),
               make_document(
                   kvp("name", "binder"),
                   kvp("tags", make_array("school", "general", "organization")),
                   kvp("price", 14.16),
                   kvp("quantity", 3)
               )
           )),
           kvp("storeLocation", "Denver"),
           kvp("customer", make_document(
               kvp("gender", "M"),
               kvp("age", 42),
               kvp("email", "cauho@witwuta.sv"),
               kvp("satisfaction", 4)
           )),
           kvp("couponUsed", true),
           kvp("purchaseMethod", "Phone")
       );
       
       // Create the second document
       auto doc2 = make_document(
           kvp("saleDate", b_date(std::chrono::milliseconds(1516874462918))), // 2018-01-25T10:01:02.918Z
           kvp("items", make_array(
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 8.05),
                   kvp("quantity", 10)
               ),
               make_document(
                   kvp("name", "binder"),
                   kvp("tags", make_array("school", "general", "organization")),
                   kvp("price", 28.31),
                   kvp("quantity", 9)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 20.95),
                   kvp("quantity", 3)
               ),
               make_document(
                   kvp("name", "laptop"),
                   kvp("tags", make_array("electronics", "school", "office")),
                   kvp("price", 866.5),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 33.09),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "printer paper"),
                   kvp("tags", make_array("office", "stationary")),
                   kvp("price", 37.55),
                   kvp("quantity", 1)
               ),
               make_document(
                   kvp("name", "backpack"),
                   kvp("tags", make_array("school", "travel", "kids")),
                   kvp("price", 83.28),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "pens"),
                   kvp("tags", make_array("writing", "office", "school", "stationary")),
                   kvp("price", 42.9),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 16.68),
                   kvp("quantity", 2)
               )
           )),
           kvp("storeLocation", "Seattle"),
           kvp("customer", make_document(
               kvp("gender", "M"),
               kvp("age", 50),
               kvp("email", "keecade@hem.uy"),
               kvp("satisfaction", 5)
           )),
           kvp("couponUsed", false),
           kvp("purchaseMethod", "Phone")
       );
       
       try {
           // Insert the documents
           auto result1 = collection.insert_one(doc1.view());
           auto result2 = collection.insert_one(doc2.view());
           
           std::cout << "Successfully inserted purchase order documents." << std::endl;
           
           // Query the new collection
           mongocxx::options::find options;
           options.sort(make_document(kvp("saleDate", -1)));
           
           auto cursor = collection.find({}, options);
           
           std::cout << "\nQuery results:" << std::endl;
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   c++ --std=c++17 create-collection.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { _id: ObjectId("..."), saleDate: 2018-01-25T10:01:02.918Z, items: [ ... ], storeLocation: "Seattle", customer: { ... }, couponUsed: false, purchaseMethod: "Phone" }
   { _id: ObjectId("..."), saleDate: 2018-01-23T21:06:49.506Z, items: [ ... ], storeLocation: "Denver", customer: { ... }, couponUsed: true, purchaseMethod: "Phone" }
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 create-collection.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```cpp
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <bsoncxx/builder/stream/document.hpp>
   #include <bsoncxx/builder/stream/helpers.hpp>
   #include <bsoncxx/json.hpp>
   #include <bsoncxx/types.hpp>
   #include <iostream>
   #include <chrono>
   #include <thread>
   #include <ctime>

   using bsoncxx::builder::stream::close_array;
   using bsoncxx::builder::stream::close_document;
   using bsoncxx::builder::stream::document;
   using bsoncxx::builder::stream::finalize;
   using bsoncxx::builder::stream::open_array;
   using bsoncxx::builder::stream::open_document;

   // Function to update the materialized view
   void update_monthly_phone_transactions(mongocxx::client& client, mongocxx::collection collection) {
       // Create the aggregation pipeline
       auto pipeline = mongocxx::pipeline{};
       pipeline.match(document{} << "purchaseMethod" << "Phone" << finalize);
       pipeline.unwind("$items");
       pipeline.group(document{} << "_id" << open_document 
                                 << "$dateToString" << open_document 
                                 << "format" << "%Y-%m" 
                                 << "date" << "$saleDate" 
                                 << close_document << close_document
                                 << "sales_quantity" << open_document 
                                 << "$sum" << "$items.quantity" 
                                 << close_document
                                 << "sales_price" << open_document 
                                 << "$sum" << "$items.price" 
                                 << close_document << finalize);
       pipeline.add_fields(document{} << "sales_price" << open_document 
                                     << "$toDouble" << "$sales_price" 
                                     << close_document << finalize);
       pipeline.merge(document{} << "into" << "monthlyPhoneTransactions" 
                                << "whenMatched" << "replace" << finalize);
       
       // Run the aggregation
       auto cursor = collection.aggregate(pipeline);
       
       for (auto&& doc : cursor) {
           //  Consume the cursor to execute the pipeline
       }
   }

   int main() {
       // Initialize the MongoDB C++ Driver
       mongocxx::instance instance{};
       
       // Connect to MongoDB
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);
       
       auto sales = client["sample_supplies"]["sales"];
       auto purchase_orders = client["sample_supplies"]["purchaseOrders"];
       
       // Update immediately on startup
       update_monthly_phone_transactions(client, sales);
       update_monthly_phone_transactions(client, purchase_orders);
       std::cout << "Initial update completed. Materialized view is ready." << std::endl;
       
       // Example of a simple scheduler that updates monthly
       int day_of_month = 1; // Update on the 1st of each month
       
       while (true) {
           auto now = std::chrono::system_clock::now();
           std::time_t now_c = std::chrono::system_clock::to_time_t(now);
           std::tm* local_time = std::localtime(&now_c);
           
           if (local_time->tm_mday == day_of_month && local_time->tm_hour == 0 && local_time->tm_min == 0) {
               // It's midnight on the 1st of the month - update the view
               update_monthly_phone_transactions(client, sales);
               update_monthly_phone_transactions(client, purchase_orders);
               std::cout << "Scheduled update completed at " << std::ctime(&now_c);
               
               // Sleep for an hour to avoid multiple updates
               std::this_thread::sleep_for(std::chrono::hours(1));
           } else {
               // Check again in a minute
               std::this_thread::sleep_for(std::chrono::minutes(1));
           }
       }
       
       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following commands to build and run this application:

   ```shell
   c++ --std=c++17 create_update_view.cpp $(pkg-config --cflags --libs libmongocxx) -o ./view_updater.out
   ./view_updater.out
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 create_update_view.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./view_updater.out
   ./view_updater.out
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["monthlyPhoneTransactions"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "monthlySalesIndex";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   c++ --std=c++17 CreateIndex.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndex.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["monthlyPhoneTransactions"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // Create the $search stage
       auto search_doc = make_document(
           kvp("$search", make_document(
               kvp("index", "monthlySalesIndex"),
               kvp("range", make_document(
                   kvp("gt", 10000),
                   kvp("path", make_array("sales_price"))
               ))
           ))
       );
       
       // Create the $count stage
       auto count_doc = make_document(
           kvp("$count", "months_w_over_10000")
       );
       
       // Add stages to the pipeline
       pipeline.append_stage(search_doc.view());
       pipeline.append_stage(count_doc.view());
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the query.

   ```shell
   c++ --std=c++17 query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./query.out
   ./query.out
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 query.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./query.out
   ./query.out
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` and initialize your project in that directory:

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   go mod init atlas-search-project

   # Add the MongoDB Go Driver to your project
   go get go.mongodb.org/mongo-driver/v2/mongo
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the index.

   Create a `create_index.go` file in your project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Create the index.

   ```shell
   go run create_index.go
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.go`

2. Copy and paste the query into the `lookup-with-search-query.go` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	var err error
   	// connect to the MongoDB deployment
   	ctx := context.Background()
   	clOpts := options.Client().ApplyURI("<connection-string>").SetTimeout(5 * time.Second)
   	client, err := mongo.Connect(clOpts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)
   	// set namespace
   	collection := client.Database("sample_analytics").Collection("customers")
   	// define pipeline
   	lookupStage := bson.D{{Key: "$lookup", Value: bson.D{
   		{Key: "from", Value: "accounts"},
   		{Key: "localField", Value: "accounts"},
   		{Key: "foreignField", Value: "account_id"},
   		{Key: "as", Value: "purchases"},
   		{Key: "pipeline", Value: bson.A{
   			bson.D{
   				{Key: "$search", Value: bson.D{
   					{Key: "index", Value: "lookup-with-search-tutorial"},
   					{Key: "compound", Value: bson.D{
   						{Key: "must", Value: bson.A{
   							bson.D{{Key: "queryString", Value: bson.D{
   								{Key: "defaultPath", Value: "products"},
   								{Key: "query", Value: "products: (CurrencyService AND InvestmentStock)"},
   							}}},
   						}},
   						{Key: "should", Value: bson.A{
   							bson.D{{Key: "range", Value: bson.D{
   								{Key: "path", Value: "limit"},
   								{Key: "gte", Value: 5000},
   								{Key: "lte", Value: 10000},
   							}}},
   						}},
   					}},
   				}},
   			},
   			bson.D{{Key: "$project", Value: bson.D{
   				{Key: "_id", Value: 0},
   				{Key: "address", Value: 0},
   				{Key: "birthdate", Value: 0},
   				{Key: "username", Value: 0},
   				{Key: "tier_and_details", Value: 0},
   			}}},
   		}},
   	}}}
   	limitStage := bson.D{{Key: "$limit", Value: 5}}
   	projectStage := bson.D{{Key: "$project", Value: bson.D{
   		{Key: "name", Value: 1},
   		{Key: "email", Value: 1},
   		{Key: "active", Value: 1},
   		{Key: "accounts", Value: 1},
   		{Key: "purchases", Value: 1},
   	}}}

   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{lookupStage, limitStage, projectStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ```shell
   go run lookup-with-search-query.go
   ```

   **Output:**

   ```none
   {"_id":{"$oid":"..."},"name":"Lindsay Cowan","email":"cooperalexis@hotmail.com","accounts":[{"$numberInt":"116508"}],"purchases":[]}
   {"_id":{"$oid":"..."},"name":"Dr. Angela Brown","email":"michaelespinoza@gmail.com","accounts":[{"$numberInt":"571880"}],"purchases":[]}
   {"_id":{"$oid":"..."},"name":"Brian Flores","email":"april04@gmail.com","accounts":[{"$numberInt":"550665"},{"$numberInt":"321695"}],"purchases":[{"account_id":{"$numberInt":"321695"},"limit":{"$numberInt":"10000"},"products":["Derivatives","Commodity","CurrencyService","Brokerage","InvestmentStock"]}]}
   {"_id":{"$oid":"..."},"name":"Shirley Rodriguez","email":"jonathan95@yahoo.com","accounts":[{"$numberInt":"784245"},{"$numberInt":"896066"},{"$numberInt":"991412"},{"$numberInt":"951840"}],"purchases":[{"account_id":{"$numberInt":"991412"},"limit":{"$numberInt":"10000"},"products":["CurrencyService","Commodity","InvestmentFund","InvestmentStock"]},{"account_id":{"$numberInt":"951840"},"limit":{"$numberInt":"10000"},"products":["InvestmentFund","Commodity","CurrencyService","InvestmentStock"]},{"account_id":{"$numberInt":"896066"},"limit":{"$numberInt":"10000"},"products":["Derivatives","InvestmentFund","Brokerage","CurrencyService","InvestmentStock"]}]}
   {"_id":{"$oid":"..."},"name":"Clinton Shelton","email":"acook@gmail.com","accounts":[{"$numberInt":"602560"},{"$numberInt":"986196"},{"$numberInt":"51080"},{"$numberInt":"690617"},{"$numberInt":"225602"}],"purchases":[]}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` and initialize your project in that directory:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   go mod init search-with-unionwith
   go get go.mongodb.org/mongo-driver/v2
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the index.

   Create a `create_indexes.go` file in your project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Create the index.

   ```shell
   go run create_indexes.go
   ```

   **Output:**

   ```text
   Index created on companies collection: default
   Index created on inspections collection: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.go`

2. Copy and paste the query into the `search-with-unionwith-query.go` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	// connect to your MongoDB deployment and set a maximum operation time
   	ctx := context.Background()
   	opts := options.Client().
   		SetTimeout(5 * time.Second).
   		ApplyURI("<connection-string>")

   	client, err := mongo.Connect(opts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)

   	// set namespace
   	collection := client.Database("sample_training").Collection("companies")
   	// define pipeline
   	searchStage := bson.D{{"$search", bson.D{
   		{"text", bson.D{
   			{"query", "Mobile"}, {"path", "name"},
   		}},
   	}}}
   	projectStage := bson.D{{"$project", bson.D{
   		{"score", bson.D{{"$meta", "searchScore"}}},
   		{"_id", 0},
   		{"number_of_employees", 1},
   		{"founded_year", 1},
   		{"name", 1},
   	}}}
   	setStage := bson.D{{"$set", bson.D{{"source", "companies"}}}}
   	limitStage := bson.D{{"$limit", 5}}
   	unionWithStage := bson.D{{"$unionWith", bson.D{
   		{"coll", "inspections"},
   		{"pipeline", bson.A{
   			bson.D{{"$search", bson.D{
   				{"text", bson.D{
   					{"query", "Mobile"}, {"path", "business_name"},
   				}},
   			}}},
   			bson.D{{"$set", bson.D{{"source", "inspections"}}}},
   			bson.D{{"$project", bson.D{
   				{"score", bson.D{{"$meta", "searchScore"}}},
   				{"source", 1},
   				{"_id", 0},
   				{"business_name", 1},
   				{"address", 1},
   			}}},
   			bson.D{{"$limit", 3}},
   			bson.D{{"$sort", bson.D{{"score", -1}}}},
   		}},
   	}}}
   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{searchStage, projectStage, setStage, limitStage, unionWithStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ### Basic Example

   ```shell
   go run search-with-unionwith-query.go
   ```

   **Output:**

   ```javascript
   [{name XLR8 Mobile} {number_of_employees 21} {founded_year 2006} {score 3.33040714263916} {source companies} {source_count 52}]
   [{name Pulse Mobile} {number_of_employees <nil>} {founded_year <nil>} {score 3.33040714263916} {source companies} {source_count 52}]
   [{name T-Mobile} {number_of_employees <nil>} {founded_year <nil>} {score 3.33040714263916} {source companies} {source_count 52}]
   [{business_name T. MOBILE} {address [{city BROOKLYN} {zip 11209} {street 86TH ST} {number 440}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   [{business_name BOOST MOBILE} {address [{city BRONX} {zip 10458} {street E FORDHAM RD} {number 261}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   [{business_name SPRING MOBILE} {address [{city SOUTH RICHMOND HILL} {zip 11419} {street LIBERTY AVE} {number 12207}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` and initialize your project in that directory:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   go mod init search-materialized-view
   go get go.mongodb.org/mongo-driver/v2/mongo
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.go` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	// Connect to the MongoDB deployment
   	ctx := context.Background()
   	client, err := mongo.Connect(options.Client().ApplyURI("<connection-string>"))
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)

   	// Set namespace
   	collection := client.Database("sample_supplies").Collection("purchaseOrders")

   	// Create first document
   	jan23, _ := time.Parse(time.RFC3339, "2018-01-23T21:06:49.506Z")
   	purchaseOrder1 := bson.M{
   		"saleDate": jan23,
   		"items": []bson.M{
   			{
   				"name":     "printer paper",
   				"tags":     []string{"office", "stationary"},
   				"price":    40.01,
   				"quantity": 2,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    35.29,
   				"quantity": 2,
   			},
   			{
   				"name":     "pens",
   				"tags":     []string{"writing", "office", "school", "stationary"},
   				"price":    56.12,
   				"quantity": 5,
   			},
   			{
   				"name":     "backpack",
   				"tags":     []string{"school", "travel", "kids"},
   				"price":    77.71,
   				"quantity": 2,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    18.47,
   				"quantity": 2,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    19.95,
   				"quantity": 8,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    8.08,
   				"quantity": 3,
   			},
   			{
   				"name":     "binder",
   				"tags":     []string{"school", "general", "organization"},
   				"price":    14.16,
   				"quantity": 3,
   			},
   		},
   		"storeLocation": "Denver",
   		"customer": bson.M{
   			"gender":       "M",
   			"age":          42,
   			"email":        "cauho@witwuta.sv",
   			"satisfaction": 4,
   		},
   		"couponUsed":     true,
   		"purchaseMethod": "Phone",
   	}

   	// Create second document
   	jan25, _ := time.Parse(time.RFC3339, "2018-01-25T10:01:02.918Z")
   	purchaseOrder2 := bson.M{
   		"saleDate": jan25,
   		"items": []bson.M{
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    8.05,
   				"quantity": 10,
   			},
   			{
   				"name":     "binder",
   				"tags":     []string{"school", "general", "organization"},
   				"price":    28.31,
   				"quantity": 9,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    20.95,
   				"quantity": 3,
   			},
   			{
   				"name":     "laptop",
   				"tags":     []string{"electronics", "school", "office"},
   				"price":    866.5,
   				"quantity": 4,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    33.09,
   				"quantity": 4,
   			},
   			{
   				"name":     "printer paper",
   				"tags":     []string{"office", "stationary"},
   				"price":    37.55,
   				"quantity": 1,
   			},
   			{
   				"name":     "backpack",
   				"tags":     []string{"school", "travel", "kids"},
   				"price":    83.28,
   				"quantity": 2,
   			},
   			{
   				"name":     "pens",
   				"tags":     []string{"writing", "office", "school", "stationary"},
   				"price":    42.9,
   				"quantity": 4,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    16.68,
   				"quantity": 2,
   			},
   		},
   		"storeLocation": "Seattle",
   		"customer": bson.M{
   			"gender":       "M",
   			"age":          50,
   			"email":        "keecade@hem.uy",
   			"satisfaction": 5,
   		},
   		"couponUsed":     false,
   		"purchaseMethod": "Phone",
   	}

   	// Insert the documents
   	_, err = collection.InsertOne(ctx, purchaseOrder1)
   	if err != nil {
   		panic(err)
   	}

   	_, err = collection.InsertOne(ctx, purchaseOrder2)
   	if err != nil {
   		panic(err)
   	}

   	fmt.Println("Successfully inserted purchase order documents.")

   	// Query the new collection
   	findOptions := options.Find().SetSort(bson.D{{Key: "saleDate", Value: -1}})
   	cursor, err := collection.Find(ctx, bson.D{}, findOptions)
   	if err != nil {
   		panic(err)
   	}
   	defer cursor.Close(ctx)

   	// Print results
   	var results []bson.M
   	if err = cursor.All(ctx, &results); err != nil {
   		panic(err)
   	}

   	fmt.Println("\nQuery results:")
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   go run create-collection.go
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   map[_id:ObjectId("...") couponUsed:false customer:map[age:50 email:keecade@hem.uy gender:M satisfaction:5] items:[map[quantity:10] map[quantity:9] map[quantity:3] map[quantity:4] map[quantity:4] map[quantity:1] map[quantity:2] map[quantity:4]] purchaseMethod:Phone saleDate:2018-01-25 10:01:02.918 +0000 UTC storeLocation:Seattle]
   map[_id:ObjectId("...") couponUsed:true customer:map[age:42 email:cauho@witwuta.sv gender:M satisfaction:4] items:[map[quantity:2] map[quantity:2] map[quantity:5] map[quantity:2] map[quantity:2] map[quantity:8] map[quantity:3]] purchaseMethod:Phone saleDate:2018-01-23 21:06:49.506 +0000 UTC storeLocation:Denver]
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func updateMonthlyPhoneTransactions(ctx context.Context, client *mongo.Client, collection *mongo.Collection) error {
   	// Create the aggregation pipeline
   	matchStage := bson.D{bson.E{Key: "$match", Value: bson.D{bson.E{Key: "purchaseMethod", Value: "Phone"}}}}
   	unwindStage := bson.D{bson.E{Key: "$unwind", Value: bson.D{bson.E{Key: "path", Value: "$items"}}}}
   	groupStage := bson.D{bson.E{Key: "$group", Value: bson.D{
   		bson.E{Key: "_id", Value: bson.D{
   			bson.E{Key: "$dateToString", Value: bson.D{
   				bson.E{Key: "format", Value: "%Y-%m"},
   				bson.E{Key: "date", Value: "$saleDate"},
   			}},
   		}},
   		bson.E{Key: "sales_quantity", Value: bson.D{bson.E{Key: "$sum", Value: "$items.quantity"}}},
   		bson.E{Key: "sales_price", Value: bson.D{bson.E{Key: "$sum", Value: "$items.price"}}},
   	}}}
   	setStage := bson.D{bson.E{Key: "$set", Value: bson.D{bson.E{Key: "sales_price", Value: bson.D{bson.E{Key: "$toDouble", Value: "$sales_price"}}}}}}
   	mergeStage := bson.D{bson.E{Key: "$merge", Value: bson.D{
   		bson.E{Key: "into", Value: "monthlyPhoneTransactions"},
   		bson.E{Key: "whenMatched", Value: "replace"},
   	}}}

   	pipeline := mongo.Pipeline{matchStage, unwindStage, groupStage, setStage, mergeStage}

   	// Run the aggregation
   	cursor, err := collection.Aggregate(ctx, pipeline)
   	if err != nil {
   		return err
   	}
   	defer cursor.Close(ctx)
   	return nil
   }

   func main() {
   	// Create a context
   	ctx := context.Background()

   	// Connect to MongoDB
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().ApplyURI(uri))
   	if err != nil {
   		log.Fatal(err)
   	}

   	// Ensure the client will be properly disconnected when main exits
   	defer func() {
   		if err := client.Disconnect(ctx); err != nil {
   			log.Fatal(err)
   		}
   	}()

   	// Ping the MongoDB server to verify the connection
   	if err := client.Ping(ctx, nil); err != nil {
   		log.Fatal(err)
   	}

   	sales := client.Database("sample_supplies").Collection("sales")
   	purchaseOrders := client.Database("sample_supplies").Collection("purchaseOrders")

   	// Update immediately on startup
   	if err := updateMonthlyPhoneTransactions(ctx, client, sales); err != nil {
   		log.Fatal(err)
   	}
   	if err := updateMonthlyPhoneTransactions(ctx, client, purchaseOrders); err != nil {
   		log.Fatal(err)
   	}
   	fmt.Println("Initial update completed. Materialized view is ready.")

   	// Example of a simple scheduler that updates monthly
   	dayOfMonth := 1 // Update on the 1st of each month

   	for {
   		now := time.Now()

   		if now.Day() == dayOfMonth && now.Hour() == 0 && now.Minute() == 0 {
   			// Create a new context for each update
   			updateCtx, cancel := context.WithTimeout(ctx, 30*time.Minute)

   			// It's midnight on the 1st of the month - update the view
   			if err := updateMonthlyPhoneTransactions(updateCtx, client, sales); err != nil {
   				log.Println("Error updating from sales:", err)
   			}
   			if err := updateMonthlyPhoneTransactions(updateCtx, client, purchaseOrders); err != nil {
   				log.Println("Error updating from purchase orders:", err)
   			}

   			// Cancel the context when done
   			cancel()

   			fmt.Printf("Scheduled update completed at %s\n", now.Format(time.RFC1123))

   			// Sleep for an hour to avoid multiple updates
   			time.Sleep(1 * time.Hour)
   		} else {
   			// Check again in a minute
   			time.Sleep(1 * time.Minute)
   		}
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following command to build and run this application:

   ```shell
   go run create_update_view.go
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   go run create_index.go
   ```

   **Output:**

   ```text
   Name of index created: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	var err error
   	// connect to the MongoDB deployment
   	ctx := context.Background()
   	clOpts := options.Client().ApplyURI("<connection-string>").SetTimeout(5 * time.Second)
   	client, err := mongo.Connect(clOpts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)
   	// set namespace
   	collection := client.Database("sample_analytics").Collection("customers")
   	// define pipeline
   	lookupStage := bson.D{{Key: "$lookup", Value: bson.D{
   		{Key: "from", Value: "accounts"},
   		{Key: "localField", Value: "accounts"},
   		{Key: "foreignField", Value: "account_id"},
   		{Key: "as", Value: "purchases"},
   		{Key: "pipeline", Value: bson.A{
   			bson.D{
   				{Key: "$search", Value: bson.D{
   					{Key: "index", Value: "lookup-with-search-tutorial"},
   					{Key: "compound", Value: bson.D{
   						{Key: "must", Value: bson.A{
   							bson.D{{Key: "queryString", Value: bson.D{
   								{Key: "defaultPath", Value: "products"},
   								{Key: "query", Value: "products: (CurrencyService AND InvestmentStock)"},
   							}}},
   						}},
   						{Key: "should", Value: bson.A{
   							bson.D{{Key: "range", Value: bson.D{
   								{Key: "path", Value: "limit"},
   								{Key: "gte", Value: 5000},
   								{Key: "lte", Value: 10000},
   							}}},
   						}},
   					}},
   				}},
   			},
   			bson.D{{Key: "$project", Value: bson.D{
   				{Key: "_id", Value: 0},
   				{Key: "address", Value: 0},
   				{Key: "birthdate", Value: 0},
   				{Key: "username", Value: 0},
   				{Key: "tier_and_details", Value: 0},
   			}}},
   		}},
   	}}}
   	limitStage := bson.D{{Key: "$limit", Value: 5}}
   	projectStage := bson.D{{Key: "$project", Value: bson.D{
   		{Key: "name", Value: 1},
   		{Key: "email", Value: 1},
   		{Key: "active", Value: 1},
   		{Key: "accounts", Value: 1},
   		{Key: "purchases", Value: 1},
   	}}}

   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{lookupStage, limitStage, projectStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   go run query.go
   ```

   **Output:**

   ```text
   map[months_w_over_10000:4]
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.java` file and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `LookupWithSearchQuery.java` in your project directory.

2. Copy and paste the query into the `LookupWithSearchQuery.java` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```java
   import static com.mongodb.client.model.Aggregates.limit;
   import static com.mongodb.client.model.Aggregates.project;
   import static com.mongodb.client.model.Projections.*;
   import java.util.Arrays;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;

   public class LookupWithSearchQuery {

     public static void main(String[] args) {
       // connect to your MongoDB deployment
       String uri = "<connection-string>";
           
       try (MongoClient mongoClient = MongoClients.create(uri)) {
         // set namespace
         MongoDatabase database = mongoClient.getDatabase("sample_analytics");
         MongoCollection<Document> collection = database.getCollection("customers");
               
         // define pipeline
         Document agg = new Document("$lookup", 
           new Document("from", "accounts")
             .append("localField", "accounts")
             .append("foreignField", "account_id")
             .append("as", "purchases")
             .append("pipeline", Arrays.asList(new Document("$search", 
               new Document("index", "lookup-with-search-tutorial")
                 .append("compound", 
                 new Document("must", Arrays.asList(new Document("queryString", 
                   new Document("defaultPath", "products")
                     .append("query", "products: (CurrencyService AND InvestmentStock)"))))
                     .append("should", Arrays.asList(new Document("range", 
                       new Document("path", "limit")
                         .append("gte", 5000L)
                         .append("lte", 10000L)
                     )))
                 )
             ), 
             new Document("$limit", 5L),
             new Document("$project", 
             new Document("_id", 0L)
               .append("address", 0L)
               .append("birthdate", 0L)
               .append("username", 0L)
               .append("tier_and_details", 0L)
           )))
         );
         // run pipeline and print results
         collection.aggregate(Arrays.asList(agg,
           limit(5),
           project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
         ))
         .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `LookupWithSearchQuery.java` file.

   ```bash
   javac LookupWithSearchQuery.java
   java LookupWithSearchQuery
   ```

   **Output:**

   ```json
   {"name": "Lindsay Cowan", "email": "cooperalexis@hotmail.com", "accounts": [116508], "purchases": []}
   {"name": "Dr. Angela Brown", "email": "michaelespinoza@gmail.com", "accounts": [571880], "purchases": []}
   {"name": "Brian Flores", "email": "april04@gmail.com", "accounts": [550665, 321695], "purchases": [{"account_id": 321695, "limit": 10000, "products": ["Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock"]}]}
   {"name": "Shirley Rodriguez", "email": "jonathan95@yahoo.com", "accounts": [784245, 896066, 991412, 951840], "purchases": [{"account_id": 991412, "limit": 10000, "products": ["CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock"]}, {"account_id": 951840, "limit": 10000, "products": ["InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock"]}, {"account_id": 896066, "limit": 10000, "products": ["Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock"]}]}
   {"name": "Clinton Shelton", "email": "acook@gmail.com", "accounts": [602560, 986196, 51080, 690617, 225602], "purchases": []}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.java` file and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `SearchWithUnionwithQuery.java`.

2. Copy and paste the query into the `SearchWithUnionwithQuery.java` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```java
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;
   import java.util.ArrayList;
   import java.util.Arrays;
   import java.util.List;

   public class SearchWithUnionwith {
     public static void main(String[] args) {
     // connect to your MongoDB deployment
       try (MongoClient mongoClient = MongoClients.create("<connection-string>")) {
         // get database name
         MongoDatabase database = mongoClient.getDatabase("sample_training");
         // define pipeline
         List<Document> pipeline1 = Arrays.asList(
           new Document("$search", new Document("text",
             new Document("query", "Mobile")
               .append("path", "name"))),
           new Document("$project", new Document("score",
             new Document("$meta", "searchScore"))
               .append("_id", 0)
               .append("number_of_employees", 1)
               .append("founded_year", 1)
               .append("name", 1)),
           new Document("$set", new Document("source", "companies")),
           new Document("$limit", 3)
         );

         List<Document> pipeline2 = Arrays.asList(
           new Document("$search", new Document("text",
             new Document("query", "Mobile")
               .append("path", "business_name"))),
           new Document("$set", new Document("source", "inspections")),
           new Document("$project", new Document("score",
             new Document("$meta", "searchScore"))
               .append("source", 1)
               .append("_id", 0)
               .append("business_name", 1)
               .append("address", 1)),
           new Document("$limit", 3),
           new Document("$sort", new Document("score", -1))
         );

         List<Document> unionWithStage = new ArrayList<>();
         Document unionWith = new Document("$unionWith", new Document("coll", "inspections")
           .append("pipeline", pipeline2));
         unionWithStage.add(unionWith);

         List<Document> finalPipeline = new ArrayList<>(pipeline1);
         finalPipeline.addAll(unionWithStage);
         // run pipeline and print results
         database.getCollection("companies").aggregate(finalPipeline)
           .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `UinionwithWithSearchQuery.java` file.

   ### Basic Example

   ```bash
   javac SearchWithUnionwithQuery.java
   java SearchWithUnionwithQuery
   ```

   **Output:**

   ```json
   {"name": "XLR8 Mobile", "number_of_employees": 21, "founded_year": 2006, "score": 2.0815043449401855, "source": "companies"}
   {"name": "Pulse Mobile", "number_of_employees": null, "founded_year": null, "score": 2.0815043449401855, "source": "companies"}
   {"name": "T-Mobile", "number_of_employees": null, "founded_year": null, "score": 2.0815043449401855, "source": "companies"}
   {"business_name": "T. MOBILE", "address": {"city": "BROOKLYN", "zip": 11209, "street": "86TH ST", "number": 440}, "source": "inspections", "score": 2.900916337966919}
   {"business_name": "BOOST MOBILE", "address": {"city": "BRONX", "zip": 10458, "street": "E FORDHAM RD", "number": 261}, "source": "inspections", "score": 2.900916337966919}
   {"business_name": "SPRING MOBILE", "address": {"city": "SOUTH RICHMOND HILL", "zip": 11419, "street": "LIBERTY AVE", "number": 12207}, "source": "inspections", "score": 2.900916337966919}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   In your project's base package directory, create a `CreateCollection.java` file and copy and paste the following code into this file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```java
   import java.util.Arrays;
   import java.util.Date;
   import java.util.ArrayList;
   import java.util.List;

   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoCursor;
   import com.mongodb.client.MongoDatabase;
   import com.mongodb.client.model.Sorts;
   import org.bson.Document;

   public class CreateCollection {

       public static void main(String[] args) {
           // Connect to your MongoDB deployment
           String uri = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(uri)) {
               // Set namespace
               MongoDatabase database = mongoClient.getDatabase("sample_supplies");
               MongoCollection<Document> collection = database.getCollection("purchaseOrders");
               
               // Create first document
               Document purchaseOrder1 = new Document("saleDate", new Date(1516741609506L)) // 2018-01-23T21:06:49.506Z
                   .append("items", Arrays.asList(
                       new Document("name", "printer paper")
                           .append("tags", Arrays.asList("office", "stationary"))
                           .append("price", 40.01)
                           .append("quantity", 2),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 35.29)
                           .append("quantity", 2),
                       new Document("name", "pens")
                           .append("tags", Arrays.asList("writing", "office", "school", "stationary"))
                           .append("price", 56.12)
                           .append("quantity", 5),
                       new Document("name", "backpack")
                           .append("tags", Arrays.asList("school", "travel", "kids"))
                           .append("price", 77.71)
                           .append("quantity", 2),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 18.47)
                           .append("quantity", 2),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 19.95)
                           .append("quantity", 8),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 8.08)
                           .append("quantity", 3),
                       new Document("name", "binder")
                           .append("tags", Arrays.asList("school", "general", "organization"))
                           .append("price", 14.16)
                           .append("quantity", 3)
                   ))
                   .append("storeLocation", "Denver")
                   .append("customer", new Document()
                       .append("gender", "M")
                       .append("age", 42)
                       .append("email", "cauho@witwuta.sv")
                       .append("satisfaction", 4)
                   )
                   .append("couponUsed", true)
                   .append("purchaseMethod", "Phone");
               
               // Create second document
               Document purchaseOrder2 = new Document("saleDate", new Date(1516874462918L)) // 2018-01-25T10:01:02.918Z
                   .append("items", Arrays.asList(
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 8.05)
                           .append("quantity", 10),
                       new Document("name", "binder")
                           .append("tags", Arrays.asList("school", "general", "organization"))
                           .append("price", 28.31)
                           .append("quantity", 9),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 20.95)
                           .append("quantity", 3),
                       new Document("name", "laptop")
                           .append("tags", Arrays.asList("electronics", "school", "office"))
                           .append("price", 866.5)
                           .append("quantity", 4),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 33.09)
                           .append("quantity", 4),
                       new Document("name", "printer paper")
                           .append("tags", Arrays.asList("office", "stationary"))
                           .append("price", 37.55)
                           .append("quantity", 1),
                       new Document("name", "backpack")
                           .append("tags", Arrays.asList("school", "travel", "kids"))
                           .append("price", 83.28)
                           .append("quantity", 2),
                       new Document("name", "pens")
                           .append("tags", Arrays.asList("writing", "office", "school", "stationary"))
                           .append("price", 42.9)
                           .append("quantity", 4),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 16.68)
                           .append("quantity", 2)
                   ))
                   .append("storeLocation", "Seattle")
                   .append("customer", new Document()
                       .append("gender", "M")
                       .append("age", 50)
                       .append("email", "keecade@hem.uy")
                       .append("satisfaction", 5)
                   )
                   .append("couponUsed", false)
                   .append("purchaseMethod", "Phone");
               
               // Insert the documents
               collection.insertOne(purchaseOrder1);
               collection.insertOne(purchaseOrder2);
               
               System.out.println("Successfully inserted purchase order documents.");
               
               // Query the new collection
               MongoCursor<Document> cursor = collection.find()
                   .sort(Sorts.descending("saleDate"))
                   .iterator();
               
               System.out.println("\nQuery results:");
               try {
                   while (cursor.hasNext()) {
                       System.out.println(cursor.next().toJson());
                   }
               } finally {
                   cursor.close();
               }
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to populate the collection and display its documents.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateCollection.java
   java CreateCollection
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   Document{{_id=ObjectId(...), saleDate=Sun Jan 25 10:01:02 UTC 2018, items=[...], storeLocation=Seattle, customer=Document{{gender=M, age=50, email=keecade@hem.uy, satisfaction=5}}, couponUsed=false, purchaseMethod=Phone}}
   Document{{_id=ObjectId(...), saleDate=Tue Jan 23 21:06:49 UTC 2018, items=[...], storeLocation=Denver, customer=Document{{gender=M, age=42, email=cauho@witwuta.sv, satisfaction=4}}, couponUsed=true, purchaseMethod=Phone}}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `CreateUpdateView.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```java
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import com.mongodb.client.model.ReplaceOptions;
   import com.mongodb.client.AggregateIterable;
   import org.bson.Document;

   import java.text.SimpleDateFormat;
   import java.util.Arrays;
   import java.util.Calendar;
   import java.util.Date;
   import java.util.List;
   import java.util.concurrent.Executors;
   import java.util.concurrent.ScheduledExecutorService;
   import java.util.concurrent.TimeUnit;

   public class CreateUpdateView {

       private static void updateMonthlyPhoneTransactions(MongoClient client, MongoCollection<Document> collection) {
           // Create the aggregation pipeline
           List<Document> pipeline = Arrays.asList(
               new Document("$match", new Document("purchaseMethod", "Phone")),
               new Document("$unwind", new Document("path", "$items")),
               new Document("$group", new Document("_id", new Document("$dateToString",
                   new Document("format", "%Y-%m")
                       .append("date", "$saleDate")))
                   .append("sales_quantity", new Document("$sum", "$items.quantity"))
                   .append("sales_price", new Document("$sum", "$items.price"))
               ),
               new Document("$set", new Document("sales_price", new Document("$toDouble", "$sales_price"))),
               new Document("$merge", new Document("into", "monthlyPhoneTransactions")
                   .append("whenMatched", "replace"))
           );

           // Run the aggregation
           AggregateIterable<Document> results = collection.aggregate(pipeline);
           results.toCollection();
       }

       public static void main(String[] args) {
           // Connect to MongoDB
           MongoClient client = MongoClients.create("<connection-string>");
           MongoDatabase database = client.getDatabase("sample_supplies");
           MongoCollection<Document> sales = database.getCollection("sales");
           MongoCollection<Document> purchaseOrders = database.getCollection("purchaseOrders");

           // Update immediately on startup
           updateMonthlyPhoneTransactions(client, sales);
           updateMonthlyPhoneTransactions(client, purchaseOrders);
           System.out.println("Initial update completed. Materialized view is ready.");

           // Example of a simple scheduler that updates monthly
           final int dayOfMonth = 1; // Update on the 1st of each month
           final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

           // Create a scheduled executor for periodic updates
           ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

           // Schedule a task that checks if it's time to update
           Runnable updateTask = new Runnable() {
               @Override
               public void run() {
                   Calendar calendar = Calendar.getInstance();
                   Date now = calendar.getTime();

                   if (calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth &&
                       calendar.get(Calendar.HOUR_OF_DAY) == 0 &&
                       calendar.get(Calendar.MINUTE) == 0) {

                       // It's midnight on the 1st of the month - update the view
                       updateMonthlyPhoneTransactions(client, sales);
                       updateMonthlyPhoneTransactions(client, purchaseOrders);
                       System.out.println("Scheduled update completed at " + dateFormat.format(now));

                       // Skip the next hour's checks to avoid multiple updates
                       try {
                           Thread.sleep(3600000); // 1 hour in milliseconds
                       } catch (InterruptedException e) {
                           Thread.currentThread().interrupt();
                       }
                   }
               }
           };

           // Run the check every minute
           scheduler.scheduleAtFixedRate(updateTask, 0, 60, TimeUnit.SECONDS);

           // Keep the application running
           try {
               // Run forever until external termination
               Thread.currentThread().join();
           } catch (InterruptedException e) {
               scheduler.shutdown();
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create and update the view.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateUpdateView.java
   java CreateUpdateView
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `MaterializedViewSearchQuery.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.model.Aggregates;
   import java.util.Arrays;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;

   public class MaterializedViewSearchQuery {

     public static void main(String[] args) {
       // connect to your MongoDB deployment
       String uri = "<connection-string>";
           
       try (MongoClient mongoClient = MongoClients.create(uri)) {
         // set namespace
         MongoDatabase database = mongoClient.getDatabase("sample_supplies");
         MongoCollection<Document> collection = database.getCollection("monthlyPhoneTransactions");
               
         // define pipeline
         Document searchStage = new Document("$search", 
           new Document("index", "monthlySalesIndex")
             .append("range", 
               new Document("gt", 10000)
                 .append("path", Arrays.asList("sales_price"))
             )
         );
         
         Document countStage = new Document("$count", "months_w_over_10000");
         
         // run pipeline and print results
         collection.aggregate(Arrays.asList(searchStage, countStage))
           .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to execute the query.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac MaterializedViewSearchQuery.java
   java MaterializedViewSearchQuery
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.kt` file and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `LookupWithSearchQuery.kt` in your project directory.

2. Copy and paste the query into the `LookupWithSearchQuery.kt` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```kotlin
   import com.mongodb.client.model.Aggregates.limit
   import com.mongodb.client.model.Aggregates.project
   import com.mongodb.client.model.Projections.*
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.util.*

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_analytics")
       val collection = database.getCollection<Document>("customers")

       runBlocking {
           // define pipeline
           val agg = Document(
               "\$lookup",
               Document("from", "accounts")
                   .append("localField", "accounts")
                   .append("foreignField", "account_id")
                   .append("as", "purchases")
                   .append(
                       "pipeline", Arrays.asList(
                           Document(
                               "\$search",
                               Document("index", "lookup-with-search-tutorial")
                                   .append(
                                       "compound",
                                       Document(
                                           "must", Arrays.asList(
                                               Document(
                                                   "queryString",
                                                   Document("defaultPath", "products")
                                                       .append("query", "products: (CurrencyService AND InvestmentStock)")
                                               )
                                           )
                                       )
                                           .append(
                                               "should", Arrays.asList(
                                                   Document(
                                                       "range",
                                                       Document("path", "limit")
                                                           .append("gte", 5000)
                                                           .append("lte", 10000)
                                                   )
                                               )
                                           )
                                   )
                           ),
                           Document("\$limit", 5),
                           Document(
                               "\$project",
                               Document("_id", 0)
                                   .append("address", 0)
                                   .append("birthdate", 0)
                                   .append("username", 0)
                                   .append("tier_and_details", 0)
                           )
                       )
                   )
           )

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(
               listOf(
                   agg,
                   limit(5),
                   project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
               )
           )
           resultsFlow.collect { println(it) }
       }
       mongoClient.close()
   }


   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the `LookupWithSearchQuery.kt` file.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc LookupWithSearchQuery.kt -include-runtime -d LookupWithSearchQuery.jar
   java -jar LookupWithSearchQuery.jar
   ```

   **Output:**

   ```text
   Document{{name=Lindsay Cowan, email=cooperalexis@hotmail.com, accounts=[116508], purchases=[]}}
   Document{{name=Dr. Angela Brown, email=michaelespinoza@gmail.com, accounts=[571880], purchases=[]}}
   Document{{name=Brian Flores, email=april04@gmail.com, accounts=[550665, 321695], purchases=[Document{{account_id=321695, limit=10000, products=[Derivatives, Commodity, CurrencyService, Brokerage, InvestmentStock]}}]}}
   Document{{name=Shirley Rodriguez, email=jonathan95@yahoo.com, accounts=[784245, 896066, 991412, 951840], purchases=[Document{{account_id=991412, limit=10000, products=[CurrencyService, Commodity, InvestmentFund, InvestmentStock]}}, Document{{account_id=951840, limit=10000, products=[InvestmentFund, Commodity, CurrencyService, InvestmentStock]}}, Document{{account_id=896066, limit=10000, products=[Derivatives, InvestmentFund, Brokerage, CurrencyService, InvestmentStock]}}]}}
   Document{{name=Clinton Shelton, email=acook@gmail.com, accounts=[602560, 986196, 51080, 690617, 225602], purchases=[]}}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.kt` file and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `SearchWithUnionwithQuery.kt`.

2. Copy and paste the query into the `SearchWithUnionwithQuery.kt` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_training")
       val collection = database.getCollection<Document>("companies")

       runBlocking {
           // define pipeline
           val pipeline1 = listOf(
               Document("\$search", Document("text",
                   Document("query", "Mobile")
                       .append("path", "name"))), Document("\$project", Document("score",
                   Document("\$meta", "searchScore"))
                   .append("_id", 0)
                   .append("number_of_employees", 1)
                   .append("founded_year", 1)
                   .append("name", 1)), Document("\$set", Document("source", "companies")),
               Document("\$limit", 3)
           )

           val pipeline2 = listOf(
               Document(
                   "\$search", Document(
                       "text",
                       Document("query", "Mobile")
                           .append("path", "business_name")
                   )
               ),
               Document("\$set", Document("source", "inspections")),
               Document(
                   "\$project", Document(
                       "score",
                       Document("\$meta", "searchScore")
                   )
                       .append("source", 1)
                       .append("_id", 0)
                       .append("business_name", 1)
                       .append("address", 1)
               ),
               Document("\$limit", 3),
               Document("\$sort", Document("score", -1))
           )

           val unionWithStage: MutableList<Document> = ArrayList()
           val unionWith = Document(
               "\$unionWith", Document("coll", "inspections")
                   .append("pipeline", pipeline2)
           )
           unionWithStage.add(unionWith)
           val finalPipeline: MutableList<Document> = ArrayList(pipeline1)
           finalPipeline.addAll(unionWithStage)

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(finalPipeline)
           resultsFlow.collect { println(it) }

       }
       mongoClient.close()
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the `SearchWithUnionwithQuery.kt` file.

   ### Basic Example

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc SearchWithUnionwithQuery.kt -include-runtime -d SearchWithUnionwithQuery.jar
   java -jar SearchWithUnionwithQuery.jar
   ```

   **Output:**

   ```text
   Document{{name=XLR8 Mobile, number_of_employees=21, founded_year=2006, score=2.0815043449401855, source=companies}}
   Document{{name=Pulse Mobile, number_of_employees=null, founded_year=null, score=2.0815043449401855, source=companies}}
   Document{{name=Mobile Trend, number_of_employees=null, founded_year=2003, score=2.0815043449401855, source=companies}}
   Document{{business_name=T-MOBILE, address=Document{{city=BROOKLYN, zip=11229, street=AVENUE U, number=1616}}, source=inspections, score=2.900916337966919}}
   Document{{business_name=BOOST MOBILE, address=Document{{city=BRONX, zip=10458, street=E FORDHAM RD, number=261}}, source=inspections, score=2.900916337966919}}
   Document{{business_name=SPRING MOBILE, address=Document{{city=SOUTH RICHMOND HILL, zip=11419, street=LIBERTY AVE, number=12207}}, source=inspections, score=2.900916337966919}}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the `purchaseOrders` collection documents.

   In your project's base package directory, create a `CreateCollection.kt` file and copy and paste the following code into this file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.flow.toList
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.text.SimpleDateFormat
   import java.util.*

   fun main() = runBlocking {
       // Connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)
       
       try {
           // Set namespace
           val database = mongoClient.getDatabase("sample_supplies")
           val collection = database.getCollection<Document>("purchaseOrders")
           
           // Parse dates
           val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
           dateFormat.timeZone = TimeZone.getTimeZone("UTC")
           val date1 = dateFormat.parse("2018-01-23T21:06:49.506Z")
           val date2 = dateFormat.parse("2018-01-25T10:01:02.918Z")
           
           // Create first document
           val purchaseOrder1 = Document("saleDate", date1)
               .append("items", listOf(
                   Document()
                       .append("name", "printer paper")
                       .append("tags", listOf("office", "stationary"))
                       .append("price", 40.01)
                       .append("quantity", 2),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 35.29)
                       .append("quantity", 2),
                   Document()
                       .append("name", "pens")
                       .append("tags", listOf("writing", "office", "school", "stationary"))
                       .append("price", 56.12)
                       .append("quantity", 5),
                   Document()
                       .append("name", "backpack")
                       .append("tags", listOf("school", "travel", "kids"))
                       .append("price", 77.71)
                       .append("quantity", 2),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 18.47)
                       .append("quantity", 2),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 19.95)
                       .append("quantity", 8),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 8.08)
                       .append("quantity", 3),
                   Document()
                       .append("name", "binder")
                       .append("tags", listOf("school", "general", "organization"))
                       .append("price", 14.16)
                       .append("quantity", 3)
               ))
               .append("storeLocation", "Denver")
               .append("customer", Document()
                   .append("gender", "M")
                   .append("age", 42)
                   .append("email", "cauho@witwuta.sv")
                   .append("satisfaction", 4)
               )
               .append("couponUsed", true)
               .append("purchaseMethod", "Phone")
           
           // Create second document
           val purchaseOrder2 = Document("saleDate", date2)
               .append("items", listOf(
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 8.05)
                       .append("quantity", 10),
                   Document()
                       .append("name", "binder")
                       .append("tags", listOf("school", "general", "organization"))
                       .append("price", 28.31)
                       .append("quantity", 9),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 20.95)
                       .append("quantity", 3),
                   Document()
                       .append("name", "laptop")
                       .append("tags", listOf("electronics", "school", "office"))
                       .append("price", 866.5)
                       .append("quantity", 4),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 33.09)
                       .append("quantity", 4),
                   Document()
                       .append("name", "printer paper")
                       .append("tags", listOf("office", "stationary"))
                       .append("price", 37.55)
                       .append("quantity", 1),
                   Document()
                       .append("name", "backpack")
                       .append("tags", listOf("school", "travel", "kids"))
                       .append("price", 83.28)
                       .append("quantity", 2),
                   Document()
                       .append("name", "pens")
                       .append("tags", listOf("writing", "office", "school", "stationary"))
                       .append("price", 42.9)
                       .append("quantity", 4),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 16.68)
                       .append("quantity", 2)
               ))
               .append("storeLocation", "Seattle")
               .append("customer", Document()
                   .append("gender", "M")
                   .append("age", 50)
                   .append("email", "keecade@hem.uy")
                   .append("satisfaction", 5)
               )
               .append("couponUsed", false)
               .append("purchaseMethod", "Phone")
           
           // Insert the documents
           collection.insertOne(purchaseOrder1)
           collection.insertOne(purchaseOrder2)
           
           println("Successfully inserted purchase order documents.")
           
           // Query the new collection
           val sort = Document("saleDate", -1)
           val results = collection.find().sort(sort).toList()
           
           println("\nQuery results:")
           results.forEach { println(it) }
       } finally {
           mongoClient.close()
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to populate the collection and display its documents.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateCollection.kt -include-runtime -d CreateCollection.jar
   java -jar CreateCollection.jar
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   Document{{_id=ObjectId(...), saleDate=Thu Jan 25 10:01:02 UTC 2018, items=[...], storeLocation=Seattle, customer=Document{{gender=M, age=50, email=keecade@hem.uy, satisfaction=5}}, couponUsed=false, purchaseMethod=Phone}}
   Document{{_id=ObjectId(...), saleDate=Tue Jan 23 21:06:49 UTC 2018, items=[...], storeLocation=Denver, customer=Document{{gender=M, age=42, email=cauho@witwuta.sv, satisfaction=4}}, couponUsed=true, purchaseMethod=Phone}}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `CreateUpdateView.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import com.mongodb.kotlin.client.coroutine.MongoCollection
   import kotlinx.coroutines.*
   import org.bson.Document
   import java.text.SimpleDateFormat
   import java.util.*
   import com.mongodb.client.model.ReplaceOptions

   class CreateUpdateView {

       companion object {
           private suspend fun updateMonthlyPhoneTransactions(client: MongoClient, collection: MongoCollection<Document>) {
               // Create the aggregation pipeline
               val pipeline = listOf(
                   Document("\$match", Document("purchaseMethod", "Phone")),
                   Document("\$unwind", Document("path", "\$items")),
                   Document("\$group", Document("_id", Document("\$dateToString",
                       Document("format", "%Y-%m")
                           .append("date", "\$saleDate")))
                       .append("sales_quantity", Document("\$sum", "\$items.quantity"))
                       .append("sales_price", Document("\$sum", "\$items.price"))
                   ),
                   Document("\$set", Document("sales_price", Document("\$toDouble", "\$sales_price"))),
                   Document("\$merge", Document("into", "monthlyPhoneTransactions")
                       .append("whenMatched", "replace"))
               )

               // Run the aggregation
               val results = collection.aggregate<Document>(pipeline)
               results.toCollection()
           }

           @JvmStatic
           fun main(args: Array<String>) = runBlocking {
               // Connect to MongoDB
               val client = MongoClient.create("<connection-string>")
               val database = client.getDatabase("sample_supplies")
               val sales = database.getCollection<Document>("sales")
               val purchaseOrders = database.getCollection<Document>("purchaseOrders")

               // Update immediately on startup
               updateMonthlyPhoneTransactions(client, sales)
               updateMonthlyPhoneTransactions(client, purchaseOrders)
               println("Initial update completed. Materialized view is ready.")

               // Example of a simple scheduler that updates monthly
               val dayOfMonth = 1 // Update on the 1st of each month
               val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

               // Create a coroutine scope for our periodic updates
               val scope = CoroutineScope(Dispatchers.Default)

               // Launch a coroutine for periodic updates
               scope.launch {
                   while (isActive) {
                       val calendar = Calendar.getInstance()
                       val now = calendar.time

                       if (calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth &&
                           calendar.get(Calendar.HOUR_OF_DAY) == 0 &&
                           calendar.get(Calendar.MINUTE) == 0) {

                           // It's midnight on the 1st of the month - update the view
                           updateMonthlyPhoneTransactions(client, sales)
                           updateMonthlyPhoneTransactions(client, purchaseOrders)
                           println("Scheduled update completed at ${dateFormat.format(now)}")

                           // Delay for an hour to avoid multiple updates
                           delay(3600000) // 1 hour in milliseconds
                       } else {
                           // Check again in a minute
                           delay(60000) // 60000ms = 1 minute
                       }
                   }
               }

               // Keep the application running
               while(true) {
                   delay(1000)
               }
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create and update the view.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateUpdateView.kt -include-runtime -d CreateUpdateView.jar
   java -jar CreateUpdateView.jar
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.client.model.Aggregates.limit
   import com.mongodb.client.model.Aggregates.project
   import com.mongodb.client.model.Projections.*
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.util.*

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_analytics")
       val collection = database.getCollection<Document>("customers")

       runBlocking {
           // define pipeline
           val agg = Document(
               "\$lookup",
               Document("from", "accounts")
                   .append("localField", "accounts")
                   .append("foreignField", "account_id")
                   .append("as", "purchases")
                   .append(
                       "pipeline", Arrays.asList(
                           Document(
                               "\$search",
                               Document("index", "lookup-with-search-tutorial")
                                   .append(
                                       "compound",
                                       Document(
                                           "must", Arrays.asList(
                                               Document(
                                                   "queryString",
                                                   Document("defaultPath", "products")
                                                       .append("query", "products: (CurrencyService AND InvestmentStock)")
                                               )
                                           )
                                       )
                                           .append(
                                               "should", Arrays.asList(
                                                   Document(
                                                       "range",
                                                       Document("path", "limit")
                                                           .append("gte", 5000)
                                                           .append("lte", 10000)
                                                   )
                                               )
                                           )
                                   )
                           ),
                           Document("\$limit", 5),
                           Document(
                               "\$project",
                               Document("_id", 0)
                                   .append("address", 0)
                                   .append("birthdate", 0)
                                   .append("username", 0)
                                   .append("tier_and_details", 0)
                           )
                       )
                   )
           )

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(
               listOf(
                   agg,
                   limit(5),
                   project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
               )
           )
           resultsFlow.collect { println(it) }
       }
       mongoClient.close()
   }


   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to execute the query.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc query.kt -include-runtime -d query.jar
   java -jar query.jar
   ```

   **Output:**

   ```text
   Document{{months_w_over_10000=4}}
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Initialize your Node.js project.

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   npm init -y

   # Add the MongoDB Node.js Driver to your project
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the index.

   Create a `create-index.js` file in your project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.js` in your project directory.

2. Copy and paste the sample query into the `lookup-with-search-query.js` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```go
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$lookup': {
           'from': 'accounts', 
           'localField': 'accounts', 
           'foreignField': 'account_id', 
           'as': 'purchases', 
           'pipeline': [
             {
               '$search': {
                 'index': 'lookup-with-search-tutorial', 
                 'compound': {
                   'must': [
                     {
                       'queryString': {
                         'defaultPath': 'products', 
                         'query': 'products: (CurrencyService AND InvestmentStock)'
                       }
                     }
                   ], 
                   'should': [
                     {
                       'range': {
                         'path': 'limit', 
                         'gte': 5000, 
                         'lte': 10000
                       }
                     }
                   ]
                 }
               }
             }, {
               '$project': {
                 '_id': 0
               }
             }
           ]
         }
       }, {
         '$limit': 5
       }, {
         '$project': {
           '_id': 0, 
           'address': 0, 
           'birthdate': 0, 
           'username': 0, 
           'tier_and_details': 0
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_analytics").collection("customers");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Query your collection.

   Run the following command to query your collection:

   ```bash
   node lookup-with-search-query.js
   ```

   **Output:**

   ```js
   {
     name: 'Lindsay Cowan',
     email: 'cooperalexis@hotmail.com',
     accounts: [ 116508 ],
     purchases: []
   }
   {
     name: 'Dr. Angela Brown',
     email: 'michaelespinoza@gmail.com',
     accounts: [ 571880 ],
     purchases: []
   }
   {
     name: 'Brian Flores',
     email: 'april04@gmail.com',
     accounts: [ 550665, 321695 ],
     purchases: [
       { account_id: 321695, limit: 10000, products: [Array] }
     ]
   }
   {
     name: 'Shirley Rodriguez',
     email: 'jonathan95@yahoo.com',
     accounts: [ 784245, 896066, 991412, 951840 ],
     purchases: [
       { account_id: 991412, limit: 10000, products: [Array] },
       { account_id: 951840, limit: 10000, products: [Array] },
       { account_id: 896066, limit: 10000, products: [Array] }
     ]
   }
   {
     name: 'Clinton Shelton',
     email: 'acook@gmail.com',
     accounts: [ 602560, 986196, 51080, 690617, 225602 ],
     purchases: []
   }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Initialize your Node.js project.

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   npm init -y
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the index.

   Create a `create-index.js` file in your project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.js`.

2. Copy and paste the sample query into the `search-with-unionwith-query.js` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$search': {
           'text': { 'query': 'Mobile', 'path': 'name'  }
         }
       }, {
         '$project': {
           'score': { '$meta': 'searchScore' }, 
           '_id': 0, 'number_of_employees': 1, 'founded_year': 1, 'name': 1
         }
       }, {
         '$set': { 'source': 'companies' }
       }, {
         '$limit': 3
       }, {
         '$unionWith': {
           'coll': 'inspections', 
           'pipeline': [
             {
               '$search': {
                 'text': { 'query': 'Mobile', 'path': 'business_name' }
               }
             }, {
               '$set': { 'source': 'inspections' }
             }, {
               '$project': {
                 'score': { '$meta': 'searchScore' }, 
                 'source': 1, '_id': 0, 'business_name': 1, 'address': 1
               }
             }, {
               '$limit': 3
             }, {
               '$sort': { 'score': -1 }
             }
           ]
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_training").collection("companies");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Query your collection.

   Run the following command to query your collection:

   ### Basic Example

   ```bash
   node unionwith-with-search-query.js
   ```

   **Output:**

   ```js
   [
     {
       "name": "XLR8 Mobile",
       "number_of_employees": 21,
       "founded_year": 2006,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "name": "Pulse Mobile",
       "number_of_employees": null,
       "founded_year": null,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "name": "T-Mobile",
       "number_of_employees": null,
       "founded_year": null,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "business_name": "T. MOBILE",
       "address": {
         "city": "BROOKLYN",
         "zip": 11209,
         "street": "86TH ST",
         "number": 440
       },
       "source": "inspections",
       "score": 2.900916337966919
     },
     {
       "business_name": "BOOST MOBILE",
       "address": {
         "city": "BRONX",
         "zip": 10458,
         "street": "E FORDHAM RD",
         "number": 261
       },
       "source": "inspections",
       "score": 2.900916337966919
     },
     {
       "business_name": "SPRING MOBILE",
       "address": {
         "city": "SOUTH RICHMOND HILL",
         "zip": 11419,
         "street": "LIBERTY AVE",
         "number": 12207
       },
       "source": "inspections",
       "score": 2.900916337966919
     }
   ]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Initialize your Node.js project.

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   npm init -y
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.js` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```javascript
   const { MongoClient } = require("mongodb");
   const assert = require("assert");

   async function run() {
     // Connect to your MongoDB deployment
     const client = new MongoClient("<connection-string>");

     try {
       await client.connect();
       
       // Set namespace
       const database = client.db("sample_supplies");
       const collection = database.collection("purchaseOrders");
       
       // Create the documents
       const purchaseOrder1 = {
         saleDate: new Date("2018-01-23T21:06:49.506Z"),
         items: [
           {
             name: 'printer paper',
             tags: ['office', 'stationary'],
             price: 40.01,
             quantity: 2
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 35.29,
             quantity: 2
           },
           {
             name: 'pens',
             tags: ['writing', 'office', 'school', 'stationary'],
             price: 56.12,
             quantity: 5
           },
           {
             name: 'backpack',
             tags: ['school', 'travel', 'kids'],
             price: 77.71,
             quantity: 2
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 18.47,
             quantity: 2
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 19.95,
             quantity: 8
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 8.08,
             quantity: 3
           },
           {
             name: 'binder',
             tags: ['school', 'general', 'organization'],
             price: 14.16,
             quantity: 3
           }
         ],
         storeLocation: "Denver",
         customer: {
           gender: "M",
           age: 42,
           email: "cauho@witwuta.sv",
           satisfaction: 4
         },
         couponUsed: true,
         purchaseMethod: "Phone"
       };
       
       const purchaseOrder2 = {
         saleDate: new Date("2018-01-25T10:01:02.918Z"),
         items: [
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 8.05,
             quantity: 10
           },
           {
             name: 'binder',
             tags: ['school', 'general', 'organization'],
             price: 28.31,
             quantity: 9
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 20.95,
             quantity: 3
           },
           {
             name: 'laptop',
             tags: ['electronics', 'school', 'office'],
             price: 866.5,
             quantity: 4
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 33.09,
             quantity: 4
           },
           {
             name: 'printer paper',
             tags: ['office', 'stationary'],
             price: 37.55,
             quantity: 1
           },
           {
             name: 'backpack',
             tags: ['school', 'travel', 'kids'],
             price: 83.28,
             quantity: 2
           },
           {
             name: 'pens',
             tags: ['writing', 'office', 'school', 'stationary'],
             price: 42.9,
             quantity: 4
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 16.68,
             quantity: 2
           }
         ],
         storeLocation: "Seattle",
         customer: {
           gender: "M",
           age: 50,
           email: "keecade@hem.uy",
           satisfaction: 5
         },
         couponUsed: false,
         purchaseMethod: "Phone"
       };
       
       // Insert the documents
       await collection.insertMany([purchaseOrder1, purchaseOrder2]);
       
       console.log("Successfully inserted purchase order documents.");
       
       // Query the new collection
       const cursor = collection.find().sort({ saleDate: -1 });
       
       console.log("\nQuery results:");
       await cursor.forEach(doc => console.log(doc));
       
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   node create-collection.js
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   {
     _id: new ObjectId("..."),
     saleDate: 2018-01-25T10:01:02.918Z,
     items: [
       { quantity: 10 },
       { quantity: 9 },
       { quantity: 3 },
       { quantity: 4 },
       { quantity: 4 },
       { quantity: 1 },
       { quantity: 2 },
       { quantity: 4 }
     ],
     storeLocation: 'Seattle',
     customer: {
       gender: 'M',
       age: 50,
       email: 'keecade@hem.uy',
       satisfaction: 5
     },
     couponUsed: false,
     purchaseMethod: 'Phone'
   }
   {
     _id: new ObjectId("..."),
     saleDate: 2018-01-23T21:06:49.506Z,
     items: [
       { quantity: 2 },
       { quantity: 2 },
       { quantity: 5 },
       { quantity: 2 },
       { quantity: 2 },
       { quantity: 8 },
       { quantity: 3 }
     ],
     storeLocation: 'Denver',
     customer: {
       gender: 'M',
       age: 42,
       email: 'cauho@witwuta.sv',
       satisfaction: 4
     },
     couponUsed: true,
     purchaseMethod: 'Phone'
   }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Install the required package.

   Before running the application, install the `node-schedule` package in your `search-materialized-view` project directory. This package is used to schedule the periodic updates of the materialized view.

   ```shell
   npm install node-schedule
   ```

2. Define the materialized view and its updater.

   Create a `create-update-view.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```javascript
   const { MongoClient } = require('mongodb');
   const schedule = require('node-schedule');

   // Function to update the materialized view
   async function updateMonthlyPhoneTransactions(client, collection) {
       // Create the aggregation pipeline
       const pipeline = [
           { $match: { purchaseMethod: 'Phone' } },
           { $unwind: { path: '$items' } },
           { 
               $group: {
                   _id: { 
                       $dateToString: {
                           format: '%Y-%m',
                           date: '$saleDate'
                       }
                   },
                   sales_quantity: { $sum: '$items.quantity' },
                   sales_price: { $sum: '$items.price' }
               }
           },
           { $set: { sales_price: { $toDouble: '$sales_price' } } },
           { 
               $merge: {
                   into: 'monthlyPhoneTransactions',
                   whenMatched: 'replace'
               }
           }
       ];
       
       // Run the aggregation
       const cursor = collection.aggregate(pipeline);
       
       for await (const doc of cursor) {
           // Consume the cursor to execute the pipeline
       }
   }

   async function main() {
       // Connect to MongoDB
       const uri = '<connection-string>';
       const client = new MongoClient(uri);
       
       try {
           await client.connect();
           const database = client.db('sample_supplies');
           const sales = database.collection('sales');
           const purchaseOrders = database.collection('purchaseOrders');
           
           // Update immediately on startup
           await updateMonthlyPhoneTransactions(client, sales);
           await updateMonthlyPhoneTransactions(client, purchaseOrders);
           console.log('Initial update completed. Materialized view is ready.');
           
           // Example of a simple scheduler that updates monthly
           // This schedules the job to run at midnight on the 1st of each month
           const job = schedule.scheduleJob('0 0 1 * *', async function() {
               try {
                   await updateMonthlyPhoneTransactions(client, sales);
                   await updateMonthlyPhoneTransactions(client, purchaseOrders);
                   console.log(`Scheduled update completed at ${new Date().toISOString()}`);
               } catch (err) {
                   console.error('Error during scheduled update:', err);
               }
           });
           
           console.log('Scheduler is running. Press Ctrl+C to exit.');
           
           // Keep the process running
           process.stdin.resume();
           
       } catch (err) {
           console.error('Error:', err);
       } finally {
           await client.close();
       }
   }

   main().catch(console.error);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create and update the view.

   ```shell
   node create-update-view.js
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   Scheduler is running. Press Ctrl+C to exit.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create-index.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$lookup': {
           'from': 'accounts', 
           'localField': 'accounts', 
           'foreignField': 'account_id', 
           'as': 'purchases', 
           'pipeline': [
             {
               '$search': {
                 'index': 'lookup-with-search-tutorial', 
                 'compound': {
                   'must': [
                     {
                       'queryString': {
                         'defaultPath': 'products', 
                         'query': 'products: (CurrencyService AND InvestmentStock)'
                       }
                     }
                   ], 
                   'should': [
                     {
                       'range': {
                         'path': 'limit', 
                         'gte': 5000, 
                         'lte': 10000
                       }
                     }
                   ]
                 }
               }
             }, {
               '$project': {
                 '_id': 0
               }
             }
           ]
         }
       }, {
         '$limit': 5
       }, {
         '$project': {
           '_id': 0, 
           'address': 0, 
           'birthdate': 0, 
           'username': 0, 
           'tier_and_details': 0
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_analytics").collection("customers");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   node query.js
   ```

   **Output:**

   ```text
   { months_w_over_10000: 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the Python project.

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project

   # Install PyMongo
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the index.

   Create a `create_index.py` file in your project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.py` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.py` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```python
   import pymongo

   # connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # define pipeline
   pipeline = [
     {
       '$lookup': {
         'from': 'accounts', 
         'localField': 'accounts', 
         'foreignField': 'account_id', 
         'as': 'purchases', 
         'pipeline': [
           {
             '$search': {
               'index': 'lookup-with-search-tutorial', 
               'compound': {
                 'must': [
                   {
                     'queryString': {
                       'defaultPath': 'products', 
                       'query': 'products: (CurrencyService AND InvestmentStock)'
                     }
                   }
                 ], 
                 'should': [
                   {
                     'range': {
                       'path': 'limit', 
                       'gte': 5000, 
                       'lte': 10000
                     }
                   }
                 ]
               }
             }
           }, 
           { '$project': { '_id': 0 }  }
         ]
       }
     }, 
     { '$limit': 5 }, 
     {
       '$project': {
         '_id': 0, 
         'address': 0, 
         'birthdate': 0, 
         'username': 0, 
         'tier_and_details': 0
       }
     }
   ]

   # run pipeline
   result = client['sample_analytics']['customers'].aggregate(pipeline)

   # print results
   for i in result:
       print(i)

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ```shell
   python lookup-with-search-query.py
   ```

   **Output:**

   ```json
   {'name': 'Lindsay Cowan', 'email': 'cooperalexis@hotmail.com', 'accounts': [116508], 'purchases': []}
   {'name': 'Dr. Angela Brown', 'email': 'michaelespinoza@gmail.com', 'accounts': [571880], 'purchases': []}
   {'name': 'Brian Flores', 'email': 'april04@gmail.com', 'accounts': [550665, 321695], 'purchases': [{'account_id': 321695, 'limit': 10000, 'products': ['Derivatives', 'Commodity', 'CurrencyService', 'Brokerage', 'InvestmentStock']}]}
   {'name': 'Shirley Rodriguez', 'email': 'jonathan95@yahoo.com', 'accounts': [784245, 896066, 991412, 951840], 'purchases': [{'account_id': 991412, 'limit': 10000, 'products': ['CurrencyService', 'Commodity', 'InvestmentFund', 'InvestmentStock']}, {'account_id': 951840, 'limit': 10000, 'products': ['InvestmentFund', 'Commodity', 'CurrencyService', 'InvestmentStock']}, {'account_id': 896066, 'limit': 10000, 'products': ['Derivatives', 'InvestmentFund', 'Brokerage', 'CurrencyService', 'InvestmentStock']}]}
   {'name': 'Clinton Shelton', 'email': 'acook@gmail.com', 'accounts': [602560, 986196, 51080, 690617, 225602], 'purchases': []}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the Python project.

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the index.

   Create a `create_index.py` file in your project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.py`.

2. Copy and paste the query into the `search-with-unionwith-query.py` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   import pymongo
   import dns

   client = pymongo.MongoClient('<connection-string>')
   result = client['sample_training']['companies'].aggregate([
     {
       '$search': {
         'text': { 'query': 'Mobile', 'path': 'name' }
       }
     }, {
       '$project': {
         'score': { '$meta': 'searchScore' }, '_id': 0, 'number_of_employees': 1, 'founded_year': 1, 'name': 1
       }
     }, {
       '$set': { 'source': 'companies' }
     }, {
       '$limit': 3
     }, {
       '$unionWith': {
         'coll': 'inspections', 
         'pipeline': [
           {
             '$search': {
               'text': { 'query': 'Mobile', 'path': 'business_name' }
             }
           }, {
             '$set': { 'source': 'inspections' }
           }, {
             '$project': {
               'score': { '$meta': 'searchScore' }, 'source': 1, '_id': 0, 'business_name': 1, 'address': 1
             }
           }, {
             '$limit': 3
           }, {
             '$sort': { 'score': -1 }
           }
         ]
       }
     }
   ])

   for i in result:
       print(i)

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ### Basic Example

   ```shell
   python search-with-unionwith-query.py
   ```

   **Output:**

   ```json
   {'name': 'XLR8 Mobile', 'number_of_employees': 21, 'founded_year': 2006, 'score': 2.0815043449401855, 'source': 'companies'}
   {'name': 'Pulse Mobile', 'number_of_employees': None, 'founded_year': None, 'score': 2.0815043449401855, 'source': 'companies'}
   {'name': 'T-Mobile', 'number_of_employees': None, 'founded_year': None, 'score': 2.0815043449401855, 'source': 'companies'}
   {'business_name': 'T. MOBILE', 'address': {'city': 'BROOKLYN', 'zip': 11209, 'street': '86TH ST', 'number': 440}, 'source': 'inspections', 'score': 2.900916337966919}
   {'business_name': 'BOOST MOBILE', 'address': {'city': 'BRONX', 'zip': 10458, 'street': 'E FORDHAM RD', 'number': 261}, 'source': 'inspections', 'score': 2.900916337966919}
   {'business_name': 'SPRING MOBILE', 'address': {'city': 'SOUTH RICHMOND HILL', 'zip': 11419, 'street': 'LIBERTY AVE', 'number': 12207}, 'source': 'inspections', 'score': 2.900916337966919}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the Python project.

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create_collection.py` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```python
   import pymongo
   from datetime import datetime

   # Connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # Set namespace
   database = client['sample_supplies']
   collection = database['purchaseOrders']

   # Create first document
   purchase_order1 = {
       'saleDate': datetime(2018, 1, 23, 21, 6, 49, 506000),
       'items': [
           {
               'name': 'printer paper',
               'tags': ['office', 'stationary'],
               'price': 40.01,
               'quantity': 2
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 35.29,
               'quantity': 2
           },
           {
               'name': 'pens',
               'tags': ['writing', 'office', 'school', 'stationary'],
               'price': 56.12,
               'quantity': 5
           },
           {
               'name': 'backpack',
               'tags': ['school', 'travel', 'kids'],
               'price': 77.71,
               'quantity': 2
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 18.47,
               'quantity': 2
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 19.95,
               'quantity': 8
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 8.08,
               'quantity': 3
           },
           {
               'name': 'binder',
               'tags': ['school', 'general', 'organization'],
               'price': 14.16,
               'quantity': 3
           }
       ],
       'storeLocation': 'Denver',
       'customer': {
           'gender': 'M',
           'age': 42,
           'email': 'cauho@witwuta.sv',
           'satisfaction': 4
       },
       'couponUsed': True,
       'purchaseMethod': 'Phone'
   }

   # Create second document
   purchase_order2 = {
       'saleDate': datetime(2018, 1, 25, 10, 1, 2, 918000),
       'items': [
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 8.05,
               'quantity': 10
           },
           {
               'name': 'binder',
               'tags': ['school', 'general', 'organization'],
               'price': 28.31,
               'quantity': 9
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 20.95,
               'quantity': 3
           },
           {
               'name': 'laptop',
               'tags': ['electronics', 'school', 'office'],
               'price': 866.5,
               'quantity': 4
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 33.09,
               'quantity': 4
           },
           {
               'name': 'printer paper',
               'tags': ['office', 'stationary'],
               'price': 37.55,
               'quantity': 1
           },
           {
               'name': 'backpack',
               'tags': ['school', 'travel', 'kids'],
               'price': 83.28,
               'quantity': 2
           },
           {
               'name': 'pens',
               'tags': ['writing', 'office', 'school', 'stationary'],
               'price': 42.9,
               'quantity': 4
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 16.68,
               'quantity': 2
           }
       ],
       'storeLocation': 'Seattle',
       'customer': {
           'gender': 'M',
           'age': 50,
           'email': 'keecade@hem.uy',
           'satisfaction': 5
       },
       'couponUsed': False,
       'purchaseMethod': 'Phone'
   }

   # Insert the documents
   collection.insert_many([purchase_order1, purchase_order2])

   print("Successfully inserted purchase order documents.")

   # Query the new collection
   results = collection.find().sort('saleDate', pymongo.DESCENDING)

   print("\nQuery results:")
   for doc in results:
       print(doc)

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   python create_collection.py
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   {'_id': ObjectId('...'), 'saleDate': datetime.datetime(2018, 1, 25, 10, 1, 2, 918000), 'items': [{'quantity': 10}, {'quantity': 9}, {'quantity': 3}, {'quantity': 4}, {'quantity': 4}, {'quantity': 1}, {'quantity': 2}, {'quantity': 4}], 'storeLocation': 'Seattle', 'customer': {'gender': 'M', 'age': 50, 'email': 'keecade@hem.uy', 'satisfaction': 5}, 'couponUsed': False, 'purchaseMethod': 'Phone'}
   {'_id': ObjectId('...'), 'saleDate': datetime.datetime(2018, 1, 23, 21, 6, 49, 506000), 'items': [{'quantity': 2}, {'quantity': 2}, {'quantity': 5}, {'quantity': 2}, {'quantity': 2}, {'quantity': 8}, {'quantity': 3}], 'storeLocation': 'Denver', 'customer': {'gender': 'M', 'age': 42, 'email': 'cauho@witwuta.sv', 'satisfaction': 4}, 'couponUsed': True, 'purchaseMethod': 'Phone'}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Install the required package.

   Before running the application, install the `schedule` package. This package allows you to schedule the materialized view updates to run at a specific time each month.

   Run the following command in your `search-materialized-view` project directory:

   ```shell
   pip install schedule
   ```

2. Define the materialized view and its updater.

   Create a `create_update_view.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```python
   import pymongo
   import time
   import datetime
   import schedule

   def update_monthly_phone_transactions(client, collection):
       """Update the materialized view of monthly phone transactions."""
       # Create the aggregation pipeline
       pipeline = [
           {"$match": {"purchaseMethod": "Phone"}},
           {"$unwind": {"path": "$items"}},
           {"$group": {
               "_id": {
                   "$dateToString": {
                       "format": "%Y-%m",
                       "date": "$saleDate"
                   }
               },
               "sales_quantity": {"$sum": "$items.quantity"},
               "sales_price": {"$sum": "$items.price"}
           }},
           {"$set": {"sales_price": {"$toDouble": "$sales_price"}}},
           {"$merge": {
               "into": "monthlyPhoneTransactions",
               "whenMatched": "replace"
           }}
       ]
       
       # Run the aggregation
       collection.aggregate(pipeline)

   def scheduled_update(client, sales, purchase_orders):
       """Run the monthly update task."""
       update_monthly_phone_transactions(client, sales)
       update_monthly_phone_transactions(client, purchase_orders)
       print(f"Scheduled update completed at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

   def main():
       # Connect to MongoDB
       client = pymongo.MongoClient("<connection-string>")
       sales = client.sample_supplies.sales
       purchase_orders = client.sample_supplies.purchaseOrders
       
       # Update immediately on startup
       update_monthly_phone_transactions(client, sales)
       update_monthly_phone_transactions(client, purchase_orders)
       print("Initial update completed. Materialized view is ready.")
       
       # Schedule update to run at midnight on the 1st of each month
       schedule.every().day.at("00:00").do(
           lambda: scheduled_update(client, sales, purchase_orders) if datetime.datetime.now().day == 1 else None
       )
       
       print("Scheduler is running. Press Ctrl+C to exit.")
       
       # Keep the process running and execute scheduled tasks
       try:
           while True:
               schedule.run_pending()
               time.sleep(60)  # Check every minute
       except KeyboardInterrupt:
           print("\nShutting down...")
       finally:
           client.close()

   if __name__ == "__main__":
       main()

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create and update the view.

   ```shell
   python create_update_view.py
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   Scheduler is running. Press Ctrl+C to exit.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```python
   import pymongo

   # connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # define pipeline
   pipeline = [
     {
       '$lookup': {
         'from': 'accounts', 
         'localField': 'accounts', 
         'foreignField': 'account_id', 
         'as': 'purchases', 
         'pipeline': [
           {
             '$search': {
               'index': 'lookup-with-search-tutorial', 
               'compound': {
                 'must': [
                   {
                     'queryString': {
                       'defaultPath': 'products', 
                       'query': 'products: (CurrencyService AND InvestmentStock)'
                     }
                   }
                 ], 
                 'should': [
                   {
                     'range': {
                       'path': 'limit', 
                       'gte': 5000, 
                       'lte': 10000
                     }
                   }
                 ]
               }
             }
           }, 
           { '$project': { '_id': 0 }  }
         ]
       }
     }, 
     { '$limit': 5 }, 
     {
       '$project': {
         '_id': 0, 
         'address': 0, 
         'birthdate': 0, 
         'username': 0, 
         'tier_and_details': 0
       }
     }
   ]

   # run pipeline
   result = client['sample_analytics']['customers'].aggregate(pipeline)

   # print results
   for i in result:
       print(i)

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   python query.py
   ```

   **Output:**

   ```text
   {'months_w_over_10000': 4}
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify a name for the index.

   Your MongoDB Search index is named `default` by default. If you keep this name, then your index will be the default Search index for any MongoDB Search query that does not specify a different `index` option in its [operators](https://www.mongodb.com/docs/search/query/operators-collectors/overview.md#std-label-fts-operators). If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_mflix.movies` collection.

   On the Database screen, click the `sample_analytics` database and then click the `customers` collection.

3. Run a MongoDB Search query against the collection.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$lookup` | `{
     from: "accounts",
     localField: "accounts",
     foreignField: "account_id",
     as: "purchases",
     pipeline: [
       {
         $search: {
           index: "lookup-with-search-tutorial",
           compound: {
             must: [
               {
                 queryString: {
                 defaultPath: "products",
                 query:
                   "products: (CurrencyService AND InvestmentStock)"
                 }
               }
             ],
             should: [
               {
                 range: {
                   path: "limit",
                   gte: 5000,
                   lte: 10000,
                 }
               }
             ]
           }
         }
       },
       {
         $project: {
           _id: 0,
         }
       }
     ]
   }` |
   | `$limit` | `5` |
   | `$project` | `{
      _id: 0,
      address: 0,
      birthdate: 0,
      username: 0,
      tier_and_details: 0,
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following documents next to the `$project` pipeline stage:

   ```json
   name: Elizabeth Ray
   email: arroyocolton@gmail.com
   active: True
   accounts: Array (6)
   purchases: Array (3)

   name: "Lindsay Cowan"
   email: "cooperalexis@hotmail.com"
   accounts: Array (1)
   purchases: Array (empty)

   name: "Katherine David"
   email: "timothy78@hotmail.com"
   accounts: Array (5)
   urchases: Array (2)

   name: "Leslie Martinez"
   email: "tcrawford@gmail.com"
   accounts: Array (2)
   purchases: Array (empty)

   name: "Brad Cardenas"
   email: "dustin37@yahoo.com"
   accounts: Array (5)
   purchases: Array (2)
   ```

4. Expand your query results.

   MongoDB Compass might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database. You must perform the following steps for each collection.

#### Create the MongoDB Search Index

In this section, you will create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database. You must perform the following steps for each collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

#### Run `$unionWith` with `$search` to Search the Collections

In this section, you will connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_training.companies` collection.

   On the Database screen, click the `sample_training` database and then click the `companies` collection.

3. Run the MongoDB Search query against the collection.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$search` | `{
     "text": {
       "query": "Mobile",
       "path": "name"
     }
   }` |
   | `$project` | `{
     "score": {
       "$meta": "searchScore",
     },
     "_id": 0,
     "number_of_employees": 1,
     "founded_year": 1,
     "name": 1
   }` |
   | `$set` | `{
     "source": "companies"
   }` |
   | `$limit` | `3` |
   | `$unionWith` | `{
     "coll": "inspections",
     "pipeline": [
       {
         "$search": {
           "text": {
             "query": "Mobile",
             "path": "business_name",
           }
         }
       },
       {
         "$set": {
           "source": "inspections",
         }
       },
       {
         "$project": {
           "score": {
             "$meta": "searchScore"
           },
           "source": 1,
           "_id": 0,
           "business_name": 1,
           "address": 1
         }
       },
       {
         "$limit": 3
       },
       {
         "$sort": {
           "score": -1
         }
       }
     ]
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following documents next to the `$project` pipeline stage:

   ```json
   name: "XLR8 Mobile"
   number_of_employees: 21
   founded_year: 2006
   score: 2.0815043449401855
   source: "companies"

   name: "Pulse Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   name: "T-Mobile"
   number_of_employees: null
   founded_year: null
   score: 2.0815043449401855
   source: "companies"

   business_name: "T. MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "BOOST MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919

   business_name: "SPRING MOBILE"
   address: Object
   source: "inspections"
   score: 2.900916337966919
   ```

4. Expand your query results.

   MongoDB Compass might not display all the fields inside objects and all the values inside arrays for the documents it returns in the results. To view all the fields and values, expand the field in the results.

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and create a new collection.

   On the Databases screen, click the `sample_supplies` database.

   Click Create Collection.

   In the Create Collection dialog, enter `purchaseOrders` in the Collection Name field.

   Click Create Collection.

3. Add documents to the collection.

   Click the newly created `purchaseOrders` collection.

   Click Add Data and select Insert document.

   In the Insert Document dialog, enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-23T21:06:49.506Z"
     },
     "items": [
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": 40.01,
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 35.29,
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": 56.12,
         "quantity": 5
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": 77.71,
         "quantity": 2
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 18.47,
         "quantity": 2
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 19.95,
         "quantity": 8
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 8.08,
         "quantity": 3
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": 14.16,
         "quantity": 3
       }
     ],
     "storeLocation": "Denver",
     "customer": {
       "gender": "M",
       "age": 42,
       "email": "cauho@witwuta.sv",
       "satisfaction": 4
     },
     "couponUsed": true,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

   Click Add Data and select Insert Document again.

   In the Insert Document dialog, enter the following document:

   ```json
   {
     "saleDate": {
       "$date": "2018-01-25T10:01:02.918Z"
     },
     "items": [
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 8.05,
         "quantity": 10
       },
       {
         "name": "binder",
         "tags": ["school", "general", "organization"],
         "price": 28.31,
         "quantity": 9
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 20.95,
         "quantity": 3
       },
       {
         "name": "laptop",
         "tags": ["electronics", "school", "office"],
         "price": 866.5,
         "quantity": 4
       },
       {
         "name": "notepad",
         "tags": ["office", "writing", "school"],
         "price": 33.09,
         "quantity": 4
       },
       {
         "name": "printer paper",
         "tags": ["office", "stationary"],
         "price": 37.55,
         "quantity": 1
       },
       {
         "name": "backpack",
         "tags": ["school", "travel", "kids"],
         "price": 83.28,
         "quantity": 2
       },
       {
         "name": "pens",
         "tags": ["writing", "office", "school", "stationary"],
         "price": 42.9,
         "quantity": 4
       },
       {
         "name": "envelopes",
         "tags": ["stationary", "office", "general"],
         "price": 16.68,
         "quantity": 2
       }
     ],
     "storeLocation": "Seattle",
     "customer": {
       "gender": "M",
       "age": 50,
       "email": "keecade@hem.uy",
       "satisfaction": 5
     },
     "couponUsed": false,
     "purchaseMethod": "Phone"
   }
   ```

   Click Insert.

4. Verify the inserted documents.

   Click the Documents tab if you're not already on it.

   Navigate to the search bar and select the Options dropdown.

   In the Sort field, enter:

   ```json
   { "saleDate": -1 }
   ```

   Click Find.

   The query results should display the two purchase order documents, with the most recent (January 25, 2018) displayed first.

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Navigate to the `sample_supplies` database.

   On the Database screen, click the `sample_supplies` database to access its collections.

3. Create a materialized view using the sales collection.

   First, you'll run an aggregation pipeline on the `sales` collection to create the materialized view:

   Click on the `sales` collection.

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   - Stage 1: `$match`

     ```javascript
     {
       purchaseMethod: "Phone"
     }
     ```

   - Stage 2: `$unwind`

     ```javascript
     {
       path: "$items"
     }
     ```

   - Stage 3: `$group`

     ```javascript
     {
       _id: {
         $dateToString: {
           format: "%Y-%m",
           date: "$saleDate"
         }
       },
       sales_quantity: { $sum: "$items.quantity" },
       sales_price: { $sum: "$items.price" }
     }
     ```

   - Stage 4: `$set`

     ```javascript
     {
       sales_price: { $toDouble: "$sales_price" }
     }
     ```

   - Stage 5: `$merge`

     ```javascript
     {
       into: "monthlyPhoneTransactions",
       whenMatched: "replace"
     }
     ```

   Click Run to execute the aggregation pipeline. This creates the initial `monthlyPhoneTransactions` collection from the `sales` data.

4. Update the materialized view with data from the purchaseOrders collection.

   Next, you'll run a similar aggregation pipeline on the `purchaseOrders` collection to add that data to the materialized view:

   Navigate back to the database view and click on the `purchaseOrders` collection.

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   - Stage 1: `$match`

     ```javascript
     {
       purchaseMethod: "Phone"
     }
     ```

   - Stage 2: `$unwind`

     ```javascript
     {
       path: "$items"
     }
     ```

   - Stage 3: `$group`

     ```javascript
     {
       _id: {
         $dateToString: {
           format: "%Y-%m",
           date: "$saleDate"
         }
       },
       sales_quantity: { $sum: "$items.quantity" },
       sales_price: { $sum: "$items.price" }
     }
     ```

   - Stage 4: `$set`

     ```javascript
     {
       sales_price: { $toDouble: "$sales_price" }
     }
     ```

   - Stage 5: `$merge`

     ```javascript
     {
       into: "monthlyPhoneTransactions",
       whenMatched: "replace"
     }
     ```

   Click Run to execute the aggregation pipeline. This updates the `monthlyPhoneTransactions` collection with data from the `purchaseOrders` collection.

5. Verify that the materialized view was created correctly.

   Navigate back to the database view and click on the newly created `monthlyPhoneTransactions` collection.

   Click the Documents tab to view the documents in the collection.

   You should see documents with monthly aggregated data. The first few documents might look like:

   ```json
   {
     "_id": "2013-01",
     "sales_quantity": 200,
     "sales_price": 2941.47
   }
   {
     "_id": "2013-02",
     "sales_quantity": 191,
     "sales_price": 4822.15
   }
   {
     "_id": "2013-03",
     "sales_quantity": 74,
     "sales_price": 2429.12
   }
   ```

6. Set up a schedule to update the materialized view.

   For production environments, we recommend that you set up a scheduled task to update the materialized view. You can do this by using the following methods:

   1. **MongoDB Scheduled Triggers**: If using MongoDB Atlas, create a scheduled trigger through the Atlas UI to run this aggregation on a monthly basis.

   2. **Cron Job**: Set up a cron job on your server that runs a script to connect to MongoDB and execute the aggregation pipeline.

   3. **Application Logic**: Implement scheduled updates in your application using the example code shown in the programming language examples of this guide.

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Connect to your deployment by using MongoDB Compass.

   Open [Compass](https://www.mongodb.com/try/download/compass) and connect to your cluster. For detailed instructions, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Specify the database and collection.

   On the Database screen, click the name of the database, then click the name of the collection.

3. Create the MongoDB Search index.

   Click the Indexes tab, then select Search Indexes.

   Click Create Atlas Search Index to open the index creation dialog box.

   Specify a name for the index.

   Your MongoDB Search index is named `default` by default. If you keep this name, then your index will be the default Search index for any MongoDB Search query that does not specify a different `index` option in its [operators](https://www.mongodb.com/docs/search/query/operators-collectors/overview.md#std-label-fts-operators). If you are creating multiple indexes, we recommend that you maintain a consistent, descriptive naming convention across your indexes.

   Specify the JSON (Javascript Object Notation) MongoDB Search index definition.

   ```json
   {
     "mappings": {
       "dynamic": true
     }
   }
   ```

   Click Create Search Index.

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Connect to your cluster in MongoDB Compass.

   Open MongoDB Compass and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via Compass.](https://www.mongodb.com/docs/atlas/compass-connection.md#std-label-atlas-connect-via-compass)

2. Use the `sample_supplies` database.

   On the Database screen, click the `sample_supplies` database and then click the `monthlyPhoneTransactions` collection.

3. Run a simple MongoDB Search query against the collection.

   The following query counts the number of months in monthlyPhoneTransactions with total sales greater than or equal to `10000` dollars.

   To run this query in MongoDB Compass:

   Click the Aggregations tab.

   Click Select..., then configure each of the following pipeline stages by selecting the stage from the dropdown and adding the query for that stage. Click Add Stage to add additional stages.

   | Pipeline Stage | Query |
   | --- | --- |
   | `$lookup` | `{
     from: "accounts",
     localField: "accounts",
     foreignField: "account_id",
     as: "purchases",
     pipeline: [
       {
         $search: {
           index: "lookup-with-search-tutorial",
           compound: {
             must: [
               {
                 queryString: {
                 defaultPath: "products",
                 query:
                   "products: (CurrencyService AND InvestmentStock)"
                 }
               }
             ],
             should: [
               {
                 range: {
                   path: "limit",
                   gte: 5000,
                   lte: 10000,
                 }
               }
             ]
           }
         }
       },
       {
         $project: {
           _id: 0,
         }
       }
     ]
   }` |
   | `$limit` | `5` |
   | `$project` | `{
      _id: 0,
      address: 0,
      birthdate: 0,
      username: 0,
      tier_and_details: 0,
   }` |

   If you [enabled](https://www.mongodb.com/docs/compass/current/aggregation-pipeline-builder/#set-the-documents-limit-or-auto-preview-documents) Auto Preview, MongoDB Compass displays the following result:

   ```json
   {
     "months_w_over_10000": 4
   }
   ```

4. Interpret the query results.

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_analytics
   ```

   **Output:**

   ```shell
   switched to db sample_analytics
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index.

   ```shell
   db.accounts.createSearchIndex(
     "lookup-with-search-tutorial",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Connect to your deployment by using [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your deployment. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Switch to the `sample_analytics` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```sh
   use sample_analytics
   ```

   **Output:**

   ```sh
   switched to db sample_analytics
   ```

3. Run the following [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with MongoDB Search [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```json
   db.customers.aggregate([
     {
       $lookup:{
         "from": "accounts",
         "localField": "accounts",
         "foreignField": "account_id",
         "as": "purchases",
         "pipeline": [{
           "$search": {
             "index": "lookup-with-search-tutorial",
             "compound": {
               "must": [{
                 "queryString": {
                   "defaultPath": "products",
                   "query": "products: (CurrencyService AND InvestmentStock)"
                 }
               }],
               "should": [{
                 "range": {
                   "path": "limit",
                   "gte": 5000,
                   "lte": 10000
                 }
               }]
             }
           }
         },{
           "$project": {
             "_id": 0
           }
         }]
       }
     },{
       "$limit": 5
     },{
       "$project": {
         "_id": 0,
         "address": 0,
         "birthdate": 0,
         "username": 0,
         "tier_and_details": 0
       }
     }
   ])
   ```

   **Output:**

   ```json
   [
     {
       name: 'Elizabeth Ray',
       email: 'arroyocolton@gmail.com',
       active: true,
       accounts: [ 371138, 324287, 276528, 332179, 422649, 387979 ],
       purchases: [
         {
           account_id: 422649,
           limit: 10000,
           products: [ 'CurrencyService', 'InvestmentStock' ]
         },
         {
           account_id: 324287,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'Derivatives',
             'InvestmentStock'
           ]
         },
         {
           account_id: 332179,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'InvestmentFund',
             'Brokerage',
             'InvestmentStock'
           ]
         }
       ]
     },
     {
       name: 'Lindsay Cowan',
       email: 'cooperalexis@hotmail.com',
       accounts: [ 116508 ],
       purchases: []
     },
     {
       name: 'Katherine David',
       email: 'timothy78@hotmail.com',
       accounts: [ 462501, 228290, 968786, 515844, 377292 ],
       purchases: [
         {
           account_id: 228290,
           limit: 10000,
           products: [
             'CurrencyService',
             'InvestmentStock',
             'InvestmentFund',
             'Brokerage'
           ]
         },
         {
           account_id: 515844,
           limit: 10000,
           products: [
             'Commodity',
             'CurrencyService',
             'InvestmentFund',
             'Brokerage',
             'InvestmentStock'
           ]
         }
       ]
     },
     {
       name: 'Leslie Martinez',
       email: 'tcrawford@gmail.com',
       accounts: [ 170945, 951849 ],
       purchases: []
     },
     {
       name: 'Brad Cardenas',
       email: 'dustin37@yahoo.com',
       accounts: [ 721914, 817222, 973067, 260799, 87389 ],
       purchases: [
         {
           account_id: 87389,
           limit: 10000,
           products: [ 'CurrencyService', 'InvestmentStock' ]
         },
        {
           account_id: 260799,
           limit: 10000,
           products: [
             'Brokerage',
             'InvestmentStock',
             'Commodity',
             'CurrencyService'
           ]
         }
       ]
     }
   ]
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_training
   ```

   **Output:**

   ```shell
   switched to db sample_training
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index on the companies collection.

   ```shell
   db.companies.createSearchIndex(
     "default",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   default
   ```

4. Run the `db.collection.createSearchIndex()` method to create the index on the inspections collection.

   ```shell
   db.inspections.createSearchIndex(
     "default",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Connect to your deployment in [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your deployment. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Switch to the `sample_training` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```sh
   use sample_training
   ```

   **Output:**

   ```sh
   switched to db sample_training
   ```

3. Run the following [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with a MongoDB Search [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```json
   db.companies.aggregate([
   {
     "$search": {
       "text": {
         "query": "Mobile",
         "path": "name"
       }
     }
   }, {
     "$project": {
       "score": {
         "$meta": "searchScore"
       },
       "_id": 0,
       "number_of_employees": 1,
       "founded_year": 1,
       "name": 1
     }
   }, {
     "$set": {
       "source": "companies"
     }
   }, {
     "$limit": 3
   }, {
     "$unionWith": {
       "coll": "inspections",
       "pipeline": [
         {
           "$search": {
             "text": {
               "query": "Mobile",
               "path": "business_name"
             }
           }
         }, {
           "$set": {
             "source": "inspections"
           }
         }, {
           "$project": {
             "score": {
               "$meta": "searchScore"
             },
             "source": 1,
             "_id": 0,
             "business_name": 1,
             "address": 1
           }
         },  {
           "$limit": 3
         }, {
           "$sort": {
             "score": -1
           }
         }
       ]
     }
   }
   ])
   ```

   **Output:**

   ```json
   [
     {
       name: 'XLR8 Mobile',
       number_of_employees: 21,
       founded_year: 2006,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       name: 'Pulse Mobile',
       number_of_employees: null,
       founded_year: null,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       name: 'T-Mobile',
       number_of_employees: null,
       founded_year: null,
       score: 2.0815043449401855,
       source: 'companies'
     },
     {
       business_name: 'T. MOBILE',
       address: { city: 'BROOKLYN', zip: 11209, street: '86TH ST', number: 440 },
       score: 2.900916337966919,
       source: 'inspections'
     },
     {
       business_name: 'BOOST MOBILE',
       address: { city: 'BRONX', zip: 10458, street: 'E FORDHAM RD', number: 261 },
       score: 2.900916337966919,
       source: 'inspections'
     },
     {
       business_name: 'SPRING MOBILE',
       address: {
         city: 'SOUTH RICHMOND HILL',
         zip: 11419,
         street: 'LIBERTY AVE',
         number: 12207
       },
       score: 2.900916337966919,
       source: 'inspections'
     }
   ]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Connect to the `sample_supplies` database.

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

   Use the `sample_supplies` database:

   ```sh
   use sample_supplies
   ```

2. Add a new collection.

   Add the `purchaseOrders` collection with new phone purchase order data from January of 2018. Run the following commands:

   ```json
   db.purchaseOrders.insertMany( [
     {
       saleDate: ISODate("2018-01-23T21:06:49.506Z"),
       items: [
         {
           name: 'printer paper',
           tags: [ 'office', 'stationary' ],
           price: Decimal128("40.01"),
           quantity: 2
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("35.29"),
           quantity: 2
         },
         {
           name: 'pens',
           tags: [ 'writing', 'office', 'school', 'stationary' ],
           price: Decimal128("56.12"),
           quantity: 5
         },
         {
           name: 'backpack',
           tags: [ 'school', 'travel', 'kids' ],
           price: Decimal128("77.71"),
           quantity: 2
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("18.47"),
           quantity: 2
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("19.95"),
           quantity: 8
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("8.08"),
           quantity: 3
         },
         {
           name: 'binder',
           tags: [ 'school', 'general', 'organization' ],
           price: Decimal128("14.16"),
           quantity: 3
         }
       ],
       storeLocation: 'Denver',
       customer: {
         gender: 'M',
         age: 42,
         email: 'cauho@witwuta.sv',
         satisfaction: 4
       },
       couponUsed: true,
       purchaseMethod: 'Phone'
     }
   ])
   ```

   ```json
   db.purchaseOrders.insertMany( [
     {
       saleDate: ISODate("2018-01-25T10:01:02.918Z"),
       items: [
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("8.05"),
           quantity: 10
         },
         {
           name: 'binder',
           tags: [ 'school', 'general', 'organization' ],
           price: Decimal128("28.31"),
           quantity: 9
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("20.95"),
           quantity: 3
         },
         {
           name: 'laptop',
           tags: [ 'electronics', 'school', 'office' ],
           price: Decimal128("866.5"),
           quantity: 4
         },
         {
           name: 'notepad',
           tags: [ 'office', 'writing', 'school' ],
           price: Decimal128("33.09"),
           quantity: 4
         },
         {
           name: 'printer paper',
           tags: [ 'office', 'stationary' ],
           price: Decimal128("37.55"),
           quantity: 1
         },
         {
           name: 'backpack',
           tags: [ 'school', 'travel', 'kids' ],
           price: Decimal128("83.28"),
           quantity: 2
         },
         {
           name: 'pens',
           tags: [ 'writing', 'office', 'school', 'stationary' ],
           price: Decimal128("42.9"),
           quantity: 4
         },
         {
           name: 'envelopes',
           tags: [ 'stationary', 'office', 'general' ],
           price: Decimal128("16.68"),
           quantity: 2
         }
       ],
       storeLocation: 'Seattle',
       customer: { gender: 'M', age: 50, email: 'keecade@hem.uy', satisfaction: 5 },
       couponUsed: false,
       purchaseMethod: 'Phone'
     }
   ])
   ```

3. Query the new collection.

   Query the `purchaseOrders` collection to confirm the new purchase order entries.

   ```sh
   db.purchaseOrders.find().sort( {saleDate: -1} )
   ```

   **Output:**

   ```sh
   {
     _id: ObjectId("62434c07d574cd0ce200ba75"),
     saleDate: ISODate("2018-01-25T10:01:02.918Z"),
     items: [
       {
         name: 'envelopes',
         tags: [ 'stationary', 'office', 'general' ],
         price: Decimal128("8.05"),
         quantity: 10
       },
       {
         name: 'binder',
         tags: [ 'school', 'general', 'organization' ],
         price: Decimal128("28.31"),
         quantity: 9
       },
       {
         name: 'notepad',
         tags: [ 'office', 'writing', 'school' ],
         price: Decimal128("20.95"),
         quantity: 3
       },
       {
         name: 'laptop',
         tags: [ 'electronics', 'school', 'office' ],
         price: Decimal128("866.5"),
         quantity: 4
       },
       {
         name: 'notepad',
         tags: [ 'office', 'writing', 'school' ],
         price: Decimal128("33.09"),
         quantity: 4
       },
       {
         name: 'printer paper',
         tags: [ 'office', 'stationary' ],
         price: Decimal128("37.55"),
         quantity: 1
       },
       {
         name: 'backpack',
         quantity: 2
       },
       {
         name: 'pens',
         quantity: 4
       },
       {
         name: 'envelopes',
         quantity: 2
       }
     ],
     storeLocation: 'Seattle',
     customer: {
       gender: 'M',
       age: 50,
       email: 'keecade@hem.uy',
       satisfaction: 5
     },
     couponUsed: false,
     purchaseMethod: 'Phone'
   },
   {
     _id: ObjectId("62434c07d574cd0ce200ba74"),
     saleDate: ISODate("2018-01-23T21:06:49.506Z"),
     items: [
       {
         name: 'printer paper',
         quantity: 2
       },
       {
         name: 'notepad',
         quantity: 2
       },
       {
         name: 'pens',
         quantity: 5
       },
       {
         name: 'backpack',
         quantity: 2
       },
       {
         name: 'notepad',
         quantity: 2
       },
       {
         name: 'envelopes',
         quantity: 8
       },
       {
         name: 'envelopes',
         quantity: 3
       },
       {
         name: 'binder',
         quantity: 3
       }
     ],
     storeLocation: 'Denver',
     customer: {
       gender: 'M',
       age: 42,
       email: 'cauho@witwuta.sv',
       satisfaction: 4
     },
     couponUsed: true,
     purchaseMethod: 'Phone'
   }
   ```

   The two query results reflect that the purchase order data ends in January of 2018.

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the materialized view.

   ```shell
    use sample_supplies
   ```

   **Output:**

   ```shell
   switched to db sample_supplies
   ```

3. Create the materialized view using an aggregation pipeline.

   Create a materialized view of monthly phone transactions by running the following aggregation pipelines. This creates a new collection that contains aggregated data about phone transactions by month:

   ```shell
      db.sales.aggregate([
        { $match: { purchaseMethod: "Phone" } },
        { $unwind: { path: "$items" } },
        {
          $group: {
            _id: {
              $dateToString: {
                format: "%Y-%m",
                date: "$saleDate"
              }
            },
            sales_quantity: { $sum: "$items.quantity" },
            sales_price: { $sum: "$items.price" }
          }
        },
        { $set: { sales_price: { $toDouble: "$sales_price" } } },
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ])

      db.purchaseOrders.aggregate([
        { $match: { purchaseMethod: "Phone" } },
        { $unwind: { path: "$items" } },
        {
          $group: {
            _id: {
              $dateToString: {
                format: "%Y-%m",
                date: "$saleDate"
              }
            },
            sales_quantity: { $sum: "$items.quantity" },
            sales_price: { $sum: "$items.price" }
          }
        },
        { $set: { sales_price: { $toDouble: "$sales_price" } } },
        { $merge: { into: "monthlyPhoneTransactions", whenMatched: "replace" } }
      ])
   ```

4. Set up a script to periodically update the view.

   For production environments, you should set up a scheduled task to update the materialized view. You can do this with:

   1. **MongoDB Scheduled Triggers**: If using MongoDB Atlas, create a scheduled trigger through the Atlas UI to run this aggregation on a monthly basis.

   2. **Cron Job**: Set up a cron job on your server that runs a script to connect to MongoDB and execute the aggregation pipeline.

   3. **Application Logic**: Implement scheduled updates in your application using the example code shown in the programming language examples of this guide.

5. Verify that the materialized view was created.

   ```shell
   db.monthlyPhoneTransactions.find().sort({_id: 1}).limit(3)
   ```

   **Output:**

   ```text
   [
     { _id: '2013-01', sales_quantity: 200, sales_price: 2941.47 },
     { _id: '2013-02', sales_quantity: 191, sales_price: 4822.15 },
     { _id: '2013-03', sales_quantity: 74, sales_price: 2429.12 }
   ]
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Connect to the deployment by using `mongosh`.

   In your terminal, connect to your Atlas cloud-hosted deployment or local deployment from [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh). For detailed instructions on how to connect, see [Connect to a Deployment.](https://www.mongodb.com/docs/mongodb-shell/connect/)

2. Switch to the database that contains the collection for which you want to create the index.

   ```shell
    use sample_supplies
   ```

   **Output:**

   ```shell
   switched to db sample_supplies
   ```

3. Run the `db.collection.createSearchIndex()` method to create the index.

   ```shell
   db.monthlyPhoneTransactions.createSearchIndex(
     "monthlySalesIndex",
     {
       "mappings": {
         "dynamic": true
       }
     }
   )
   ```

   **Output:**

   ```text
   monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Connect to your cluster in [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh).

   Open [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) in a terminal window and connect to your cluster. For detailed instructions on connecting, see [Connect to a Cluster via mongosh.](https://www.mongodb.com/docs/atlas/mongo-shell-connection.md#std-label-connect-mongo-shell)

2. Use the `sample_supplies` database.

   Run the following command at [`mongosh`](https://www.mongodb.com/docs/mongodb-shell.md#mongodb-binary-bin.mongosh) prompt:

   ```javascript
   use sample_supplies
   ```

3. Run a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   The following query counts the number of months in monthlyPhoneTransactions with total sales greater than or equal to `10000` dollars:

   ```javascript
   db.monthlyPhoneTransactions.aggregate([
     {
         $search: {
             "index": "monthlySalesIndex",
             "range": {
                 "gt": 10000,
                 "path": ["sales_price"]
             }
         }
     },
     {
       $count: 'months_w_over_10000'
     },
   ])
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

   For complete aggregation pipeline documentation, see the [MongoDB Server Manual.](https://www.mongodb.com/docs/manual/aggregation.md#std-label-aggregation)

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` for this project:

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Define the index.

   Create a `create_index.c` file in your project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(atlas-search-index LANGUAGES C)
   add_executable (index.out create_index.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created!
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.c` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.c` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       bson_t *lookup;
       bson_t *lookup_pipeline;
       bson_t *search;
       bson_t *compound;
       bson_t *must_array;
       bson_t *should_array;
       bson_t *query_string;
       bson_t *range;
       bson_t *limit_stage;
       bson_t *project_stage;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_analytics", "customers");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = bson_new();
       
       /* Create the $lookup stage */
       lookup = BCON_NEW("$lookup", "{",
           "from", BCON_UTF8("accounts"),
           "localField", BCON_UTF8("accounts"),
           "foreignField", BCON_UTF8("account_id"),
           "as", BCON_UTF8("purchases"),
           "pipeline", "[",
               /* First stage in lookup pipeline: $search */
               "{",
                   "$search", "{",
                       "index", BCON_UTF8("lookup-with-search-tutorial"),
                       "compound", "{",
                           "must", "[",
                               "{",
                                   "queryString", "{",
                                       "defaultPath", BCON_UTF8("products"),
                                       "query", BCON_UTF8("products: (CurrencyService AND InvestmentStock)"),
                                   "}",
                               "}",
                           "]",
                           "should", "[",
                               "{",
                                   "range", "{",
                                       "path", BCON_UTF8("limit"),
                                       "gte", BCON_INT64(5000),
                                       "lte", BCON_INT64(10000),
                                   "}",
                               "}",
                           "]",
                       "}",
                   "}",
               "}",
               /* $limit stage in lookup pipeline */
               "{",
                   "$limit", BCON_INT64(5),
               "}",
               /* $project stage in lookup pipeline */
               "{",
                   "$project", "{",
                       "_id", BCON_INT32(0),
                       "address", BCON_INT32(0),
                       "birthdate", BCON_INT32(0),
                       "username", BCON_INT32(0),
                       "tier_and_details", BCON_INT32(0),
                   "}",
               "}",
           "]",
       "}");
       
       bson_append_document(pipeline, "0", 1, lookup);
       
       /* $limit stage */
       limit_stage = BCON_NEW("$limit", BCON_INT64(5));
       bson_append_document(pipeline, "1", 1, limit_stage);
       
       /* $project stage */
       project_stage = BCON_NEW("$project", "{",
           "_id", BCON_INT32(0),
           "address", BCON_INT32(0),
           "birthdate", BCON_INT32(0),
           "username", BCON_INT32(0),
           "tier_and_details", BCON_INT32(0),
       "}");
       bson_append_document(pipeline, "2", 1, project_stage);

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(lookup);
       bson_destroy(limit_stage);
       bson_destroy(project_stage);
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(atlas-search-index LANGUAGES C)
   add_executable (index.out lookup-with-search-query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)
   ```

5. Run the command to query your collection.

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```none
   { "name" : "Lindsay Cowan", "email" : "cooperalexis@hotmail.com", "accounts" : [ { "$numberInt" : "116508" } ], "purchases" : [ ] }
   { "name" : "Dr. Angela Brown", "email" : "michaelespinoza@gmail.com", "accounts" : [ { "$numberInt" : "571880" } ], "purchases" : [ ] }
   { "name" : "Brian Flores", "email" : "april04@gmail.com", "accounts" : [ { "$numberInt" : "550665" }, { "$numberInt" : "321695" } ], "purchases" : [ { "account_id" : { "$numberInt" : "321695" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock" ] } ] }
   { "name" : "Shirley Rodriguez", "email" : "jonathan95@yahoo.com", "accounts" : [ { "$numberInt" : "784245" }, { "$numberInt" : "896066" }, { "$numberInt" : "991412" }, { "$numberInt" : "951840" } ], "purchases" : [ { "account_id" : { "$numberInt" : "991412" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock" ] }, { "account_id" : { "$numberInt" : "951840" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock" ] }, { "account_id" : { "$numberInt" : "896066" }, "limit" : { "$numberInt" : "10000" }, "products" : [ "Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock" ] } ] }
   { "name" : "Clinton Shelton", "email" : "acook@gmail.com", "accounts" : [ { "$numberInt" : "602560" }, { "$numberInt" : "986196" }, { "$numberInt" : "51080" }, { "$numberInt" : "690617" }, { "$numberInt" : "225602" } ], "purchases" : [ ] }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` for this project:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Define the index.

   Create a `create_indexes.c` file in your project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (index.out create_indexes.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created on the companies collection!
   Index created on the inspections collection!
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.c` in your project directory.

2. Copy and paste the query into the `search-with-unionwith-query.c` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_training", "companies");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = BCON_NEW (
           "pipeline", "[",
               "{",
                   "$search", "{",
                       "text", "{",
                           "query", "Mobile",
                           "path", "name",
                       "}",
                   "}",
               "}",
               "{",
                   "$project", "{",
                       "score", "{", "$meta", "searchScore", "}",
                       "_id", BCON_INT32(0),
                       "number_of_employees", BCON_INT32(1),
                       "founded_year", BCON_INT32(1),
                       "name", BCON_INT32(1),
                   "}",
               "}",
               "{",
                   "$set", "{",
                       "source", "companies",
                   "}",
               "}",
               "{",
                   "$limit", BCON_INT32(3),
               "}",
               "{",
                   "$unionWith", "{",
                       "coll", "inspections",
                       "pipeline", "[",
                           "{",
                               "$search", "{",
                                   "text", "{",
                                       "query", "Mobile",
                                       "path", "business_name",
                                   "}",
                               "}",
                           "}",
                           "{",
                               "$set", "{",
                                   "source", "inspections",
                               "}",
                           "}",
                           "{",
                               "$project", "{",
                                   "score", "{", "$meta", "searchScore", "}",
                                   "source", BCON_INT32(1),
                                   "_id", BCON_INT32(0),
                                   "business_name", BCON_INT32(1),
                                   "address", BCON_INT32(1),
                               "}",
                           "}",
                           "{",
                               "$limit", BCON_INT32(3),
                           "}",
                           "{",
                               "$sort", "{",
                                   "score", BCON_INT32(-1),
                               "}",
                           "}",
                       "]",
                   "}",
               "}",
           "]"
       );

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(
           collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (unionwith.out search-with-unionwith-query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(unionwith.out mongoc::mongoc)
   ```

5. Run the command to query your collection.

   ### Basic Example

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target unionwith.out
   ./cmake-build/unionwith.out
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : { "$numberInt" : "21" }, "founded_year" : { "$numberInt" : "2006" }, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "name" : "Pulse Mobile", "number_of_employees" : null, "founded_year" : null, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "name" : "T-Mobile", "number_of_employees" : null, "founded_year" : null, "score" : { "$numberDouble" : "2.0815043449401855469" }, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : { "$numberInt" : "11209" }, "street" : "86TH ST", "number" : { "$numberInt" : "440" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : { "$numberInt" : "10458" }, "street" : "E FORDHAM RD", "number" : { "$numberInt" : "261" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : { "$numberInt" : "11419" }, "street" : "LIBERTY AVE", "number" : { "$numberInt" : "12207" } }, "source" : "inspections", "score" : { "$numberDouble" : "2.9009163379669189453" } }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the C project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` for this project:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   ```

   Add the C driver to your project by following the instructions in the [MongoDB C Driver documentation.](https://www.mongodb.com/docs/languages/c/c-driver/current/get-started/)

2. Definethe `purchaseOrders` collection documents.

   Create a `create-collection.c` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *doc1, *doc2;
       bson_t *query;
       bson_t *sort;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_supplies", "purchaseOrders");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the first document */
       doc1 = BCON_NEW(
           "saleDate", BCON_DATE_TIME(1516741609506), /* 2018-01-23T21:06:49.506Z */
           "items", "[", 
               "{", 
                   "name", BCON_UTF8("printer paper"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(40.01), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(35.29), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("pens"), 
                   "tags", "[", 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(56.12), 
                   "quantity", BCON_INT32(5), 
               "}", 
               "{", 
                   "name", BCON_UTF8("backpack"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("travel"), 
                       BCON_UTF8("kids"), 
                   "]", 
                   "price", BCON_DOUBLE(77.71), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(18.47), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(19.95), 
                   "quantity", BCON_INT32(8), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(8.08), 
                   "quantity", BCON_INT32(3), 
               "}", 
               "{", 
                   "name", BCON_UTF8("binder"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("general"), 
                       BCON_UTF8("organization"), 
                   "]", 
                   "price", BCON_DOUBLE(14.16), 
                   "quantity", BCON_INT32(3), 
               "}", 
           "]",
           "storeLocation", BCON_UTF8("Denver"),
           "customer", "{", 
               "gender", BCON_UTF8("M"), 
               "age", BCON_INT32(42), 
               "email", BCON_UTF8("cauho@witwuta.sv"), 
               "satisfaction", BCON_INT32(4),
           "}",
           "couponUsed", BCON_BOOL(true),
           "purchaseMethod", BCON_UTF8("Phone")
       );
       
       /* Create the second document */
       doc2 = BCON_NEW(
           "saleDate", BCON_DATE_TIME(1516874462918), /* 2018-01-25T10:01:02.918Z */
           "items", "[", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(8.05), 
                   "quantity", BCON_INT32(10), 
               "}", 
               "{", 
                   "name", BCON_UTF8("binder"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("general"), 
                       BCON_UTF8("organization"), 
                   "]", 
                   "price", BCON_DOUBLE(28.31), 
                   "quantity", BCON_INT32(9), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(20.95), 
                   "quantity", BCON_INT32(3), 
               "}", 
               "{", 
                   "name", BCON_UTF8("laptop"), 
                   "tags", "[", 
                       BCON_UTF8("electronics"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("office"), 
                   "]", 
                   "price", BCON_DOUBLE(866.5), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("notepad"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("school"), 
                   "]", 
                   "price", BCON_DOUBLE(33.09), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("printer paper"), 
                   "tags", "[", 
                       BCON_UTF8("office"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(37.55), 
                   "quantity", BCON_INT32(1), 
               "}", 
               "{", 
                   "name", BCON_UTF8("backpack"), 
                   "tags", "[", 
                       BCON_UTF8("school"), 
                       BCON_UTF8("travel"), 
                       BCON_UTF8("kids"), 
                   "]", 
                   "price", BCON_DOUBLE(83.28), 
                   "quantity", BCON_INT32(2), 
               "}", 
               "{", 
                   "name", BCON_UTF8("pens"), 
                   "tags", "[", 
                       BCON_UTF8("writing"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("school"), 
                       BCON_UTF8("stationary"), 
                   "]", 
                   "price", BCON_DOUBLE(42.9), 
                   "quantity", BCON_INT32(4), 
               "}", 
               "{", 
                   "name", BCON_UTF8("envelopes"), 
                   "tags", "[", 
                       BCON_UTF8("stationary"), 
                       BCON_UTF8("office"), 
                       BCON_UTF8("general"), 
                   "]", 
                   "price", BCON_DOUBLE(16.68), 
                   "quantity", BCON_INT32(2), 
               "}", 
           "]",
           "storeLocation", BCON_UTF8("Seattle"),
           "customer", "{", 
               "gender", BCON_UTF8("M"), 
               "age", BCON_INT32(50), 
               "email", BCON_UTF8("keecade@hem.uy"), 
               "satisfaction", BCON_INT32(5),
           "}",
           "couponUsed", BCON_BOOL(false),
           "purchaseMethod", BCON_UTF8("Phone")
       );

       /* Insert the documents */
       if (!mongoc_collection_insert_one(collection, doc1, NULL, NULL, &error)) {
           fprintf(stderr, "Insert failed: %s\n", error.message);
           return 1;
       }
       
       if (!mongoc_collection_insert_one(collection, doc2, NULL, NULL, &error)) {
           fprintf(stderr, "Insert failed: %s\n", error.message);
           return 1;
       }
       
       printf("Successfully inserted purchase order documents.\n");
       
       /* Query the new collection */
       query = BCON_NEW(NULL);  /* Empty query to match all documents */
       sort = BCON_NEW("saleDate", BCON_INT32(-1));
       
       cursor = mongoc_collection_find_with_opts(collection, query, 
                                              BCON_NEW("sort", BCON_DOCUMENT(sort)), NULL);
       
       printf("\nQuery results:\n");
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }
       
       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(doc1);
       bson_destroy(doc2);
       bson_destroy(query);
       bson_destroy(sort);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Set up a CMake application

   To configure your application, create a `CMakeLists.txt` file in your project directory. Then, add the following code to the file:

   ```txt
   cmake_minimum_required(VERSION 3.11)
   project(search-with-unionwith LANGUAGES C)
   add_executable (index.out create_indexes.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)

   ```

   The preceding code performs the following actions:

   - Configures a C project.

   - Creates a `index.out` executable for your application.

   - Finds and requires the C driver. Replace the `<version>` placeholder with your C driver version, such as `2.0.0`.

   - Links the program to the `libmongoc` library.

   **Note:**

   In the sample `CMakeLists.txt` file, the `mongoc::mongoc` target points to either the static library or the shared library. The library type depends on which one is available and whichever type the user specifies in the `MONGOC_DEFAULT_IMPORTED_LIBRARY_TYPE` CMake configuration setting. If you don't set this value and both library types are available, `mongoc::mongoc` uses the static library.

   You can use the `mongoc::static` target to explicitly use the static library or the `mongoc::shared` target to use the shared library.

4. Populate the `purchaseOrders` collection and display its documents.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target collection.out
   ./cmake-build/collection.out
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { "_id" : { "$oid" : "..." }, "saleDate" : { "$date" : { "$numberLong" : "1516874462918" } }, "items" : [ { "quantity" : { "$numberInt" : "10" } }, { "quantity" : { "$numberInt" : "9" } }, { "quantity" : { "$numberInt" : "3" } }, { "quantity" : { "$numberInt" : "4" } }, { "quantity" : { "$numberInt" : "4" } }, { "quantity" : { "$numberInt" : "1" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "4" } } ], "storeLocation" : "Seattle", "customer" : { "gender" : "M", "age" : { "$numberInt" : "50" }, "email" : "keecade@hem.uy", "satisfaction" : { "$numberInt" : "5" } }, "couponUsed" : false, "purchaseMethod" : "Phone" }
   { "_id" : { "$oid" : "..." }, "saleDate" : { "$date" : { "$numberLong" : "1516741609506" } }, "items" : [ { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "5" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "2" } }, { "quantity" : { "$numberInt" : "8" } }, { "quantity" : { "$numberInt" : "3" } } ], "storeLocation" : "Denver", "customer" : { "gender" : "M", "age" : { "$numberInt" : "42" }, "email" : "cauho@witwuta.sv", "satisfaction" : { "$numberInt" : "4" } }, "couponUsed" : true, "purchaseMethod" : "Phone" }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```c
      #include <mongoc/mongoc.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      #include <unistd.h>

      /* Function to update the materialized view */
      void update_monthly_phone_transactions(mongoc_client_t *client, mongoc_collection_t *collection);

      int main(int argc, char *argv[])
      {
          mongoc_client_t *client;
          mongoc_collection_t *sales;
          mongoc_collection_t *purchase_orders;
          
          /* Initialize the MongoDB C Driver */
          mongoc_init();
          
          /* Connect to MongoDB */
          client = mongoc_client_new("<connection-string>");
          sales = mongoc_client_get_collection(client, "sample_supplies", "sales");
          purchase_orders = mongoc_client_get_collection(client, "sample_supplies", "purchaseOrders");
          
          /* Update immediately on startup */
          update_monthly_phone_transactions(client, sales);
          update_monthly_phone_transactions(client, purchase_orders);
          printf("Initial update completed. Materialized view is ready.\n");
          
          /* Example of a simple scheduler that updates monthly */
          time_t now;
          struct tm *local_time;
          int day_of_month = 1; /* Update on the 1st of each month */
          
          while (1) {
              time(&now);
              local_time = localtime(&now);
              
              if (local_time->tm_mday == day_of_month && local_time->tm_hour == 0 && local_time->tm_min == 0) {
                  /* It's midnight on the 1st of the month - update the view */
                  update_monthly_phone_transactions(client, sales);
                  update_monthly_phone_transactions(client, sales);
                  printf("Scheduled update completed at %s", ctime(&now));
                  
                  /* Sleep for an hour to avoid multiple updates */
                  sleep(3600);
              } else {
                  /* Check again in a minute */
                  sleep(60);
              }
          }
          
          /* Clean up */
          mongoc_client_destroy(client);
          mongoc_cleanup();
          
          return 0;
      }

     void update_monthly_phone_transactions(mongoc_client_t *client, mongoc_collection_t *collection)
      {
          bson_t *pipeline;
          mongoc_cursor_t *cursor;
          const bson_t *doc;
          bson_error_t error;
          
          /* Create the aggregation pipeline */
          pipeline = BCON_NEW (
              "pipeline", "[",
                  "{", "$match", "{", "purchaseMethod", BCON_UTF8("Phone"), "}", "}",
                  "{", "$unwind", "{", "path", BCON_UTF8("$items"), "}", "}",
                  "{", "$group", "{",
                      "_id", "{", "$dateToString", "{", 
                          "format", BCON_UTF8("%Y-%m"), 
                          "date", BCON_UTF8("$saleDate"),
                      "}", "}",
                      "sales_quantity", "{", "$sum", BCON_UTF8("$items.quantity"), "}",
                      "sales_price", "{", "$sum", BCON_UTF8("$items.price"), "}",
                  "}", "}",
                  "{", "$set", "{", "sales_price", "{", "$toDouble", BCON_UTF8("$sales_price"), "}", "}", "}",
                  "{", "$merge", "{", "into", BCON_UTF8("monthlyPhoneTransactions"), "whenMatched", BCON_UTF8("replace"), "}", "}",
              "]"
          );
          
          /* Run the aggregation */
          cursor = mongoc_collection_aggregate(
              collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

          while (mongoc_cursor_next(cursor, &doc)) {
           /* Consume the cursor to execute the pipeline */
          }
          
          
          if (mongoc_cursor_error(cursor, &error)) {
              fprintf(stderr, "Aggregation error: %s\n", error.message);
          }
          
          /* Clean up */
          bson_destroy(pipeline);
          mongoc_cursor_destroy(cursor);
          mongoc_collection_destroy(collection);
      }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file to add a new target for the view updater:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (view_updater.out create_update_view.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(view_updater.out mongoc::mongoc)
   ```

3. Run the application.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target view_updater.out
   ./cmake-build/view_updater.out
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       bson_error_t error;
       bson_t cmd;
       bson_t *index_definition;
       bool success;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection (client, "sample_analytics", "accounts");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Create the MongoDB Search index definition. */
       char *cmd_str = bson_strdup_printf (
           BSON_STR ({
               "createSearchIndexes": "%s",
               "indexes": [{
                   "name": "lookup-with-search-tutorial",
                   "definition": {
                       "mappings": {
                           "dynamic": true
                       }
                   }
               }]
           }),
           "accounts"
       );
       bson_init_from_json (&cmd, cmd_str, -1, &error);
       bson_free (cmd_str);

       if (mongoc_collection_command_simple (collection, &cmd, NULL, NULL, &error)) {
           printf ("Index created!\n");
       } else {
           fprintf (stderr, "Failed to create search index: %s", error.message);
       }

       /* Clean up. */
       bson_destroy (&cmd);
       mongoc_collection_destroy (collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (index.out create_index.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(index.out mongoc::mongoc)
   ```

3. Create the index.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target index.out
   ./cmake-build/index.out
   ```

   **Output:**

   ```text
   Index created!
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.c` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```c
   #include <mongoc/mongoc.h>
   #include <stdio.h>

   int main(int argc, char *argv[]) {
       mongoc_client_t *client;
       mongoc_collection_t *collection;
       mongoc_cursor_t *cursor;
       bson_error_t error;
       bson_t *pipeline;
       bson_t *lookup;
       bson_t *lookup_pipeline;
       bson_t *search;
       bson_t *compound;
       bson_t *must_array;
       bson_t *should_array;
       bson_t *query_string;
       bson_t *range;
       bson_t *limit_stage;
       bson_t *project_stage;
       const bson_t *doc;
       char *str;

       /* Initialize the MongoDB C Driver. */
       mongoc_init();

       /* Connect to your MongoDB deployment. */
       client = mongoc_client_new("<connection-string>");
       collection = mongoc_client_get_collection(client, "sample_analytics", "customers");

       if (!client) {
           fprintf(stderr, "Failed to create client.\n");
           return 1;
       }

       /* Build the aggregation pipeline */
       pipeline = bson_new();
       
       /* Create the $lookup stage */
       lookup = BCON_NEW("$lookup", "{",
           "from", BCON_UTF8("accounts"),
           "localField", BCON_UTF8("accounts"),
           "foreignField", BCON_UTF8("account_id"),
           "as", BCON_UTF8("purchases"),
           "pipeline", "[",
               /* First stage in lookup pipeline: $search */
               "{",
                   "$search", "{",
                       "index", BCON_UTF8("lookup-with-search-tutorial"),
                       "compound", "{",
                           "must", "[",
                               "{",
                                   "queryString", "{",
                                       "defaultPath", BCON_UTF8("products"),
                                       "query", BCON_UTF8("products: (CurrencyService AND InvestmentStock)"),
                                   "}",
                               "}",
                           "]",
                           "should", "[",
                               "{",
                                   "range", "{",
                                       "path", BCON_UTF8("limit"),
                                       "gte", BCON_INT64(5000),
                                       "lte", BCON_INT64(10000),
                                   "}",
                               "}",
                           "]",
                       "}",
                   "}",
               "}",
               /* $limit stage in lookup pipeline */
               "{",
                   "$limit", BCON_INT64(5),
               "}",
               /* $project stage in lookup pipeline */
               "{",
                   "$project", "{",
                       "_id", BCON_INT32(0),
                       "address", BCON_INT32(0),
                       "birthdate", BCON_INT32(0),
                       "username", BCON_INT32(0),
                       "tier_and_details", BCON_INT32(0),
                   "}",
               "}",
           "]",
       "}");
       
       bson_append_document(pipeline, "0", 1, lookup);
       
       /* $limit stage */
       limit_stage = BCON_NEW("$limit", BCON_INT64(5));
       bson_append_document(pipeline, "1", 1, limit_stage);
       
       /* $project stage */
       project_stage = BCON_NEW("$project", "{",
           "_id", BCON_INT32(0),
           "address", BCON_INT32(0),
           "birthdate", BCON_INT32(0),
           "username", BCON_INT32(0),
           "tier_and_details", BCON_INT32(0),
       "}");
       bson_append_document(pipeline, "2", 1, project_stage);

       /* Run the aggregation pipeline */
       cursor = mongoc_collection_aggregate(collection, MONGOC_QUERY_NONE, pipeline, NULL, NULL);

       /* Print the results */
       while (mongoc_cursor_next(cursor, &doc)) {
           str = bson_as_canonical_extended_json(doc, NULL);
           printf("%s\n", str);
           bson_free(str);
       }

       if (mongoc_cursor_error(cursor, &error)) {
           fprintf(stderr, "Cursor Failure: %s\n", error.message);
       }

       /* Clean up. */
       bson_destroy(lookup);
       bson_destroy(limit_stage);
       bson_destroy(project_stage);
       bson_destroy(pipeline);
       mongoc_cursor_destroy(cursor);
       mongoc_collection_destroy(collection);
       mongoc_client_destroy(client);
       mongoc_cleanup();

       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Modify your `CMakeLists.txt` file

   Edit the `CMakeLists.txt` file created in the previous section to resemble the following code:

   ```c
   cmake_minimum_required(VERSION 3.11)
   project(search-materialized-view LANGUAGES C)
   add_executable (query.out query.c)
   find_package(mongoc <version> REQUIRED)
   target_link_libraries(query.out mongoc::mongoc)
   ```

3. Run the query.

   In your terminal, run the following commands to build and run this application:

   ```shell
   cmake -S. -Bcmake-build
   cmake --build cmake-build --target query.out
   ./cmake-build/query.out
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` and initialize your project in that directory:

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   dotnet new console

   # Add the MongoDB .NET/C# Driver to your project
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the index.

   Paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Copy and paste the query into the `Program.cs` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Bson.Serialization.Conventions;
   using MongoDB.Driver;
   using MongoDB.Driver.Core;
   using MongoDB.Driver.Search;

   public class LookupWithSearch{

     static void Main(string[] args) {// allow automapping of the camelCase database fields to our MovieDocument
       var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
       ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

       // connect to your MongoDB deployment
       var mongoClient = new MongoClient("<connection-string>");
       
       // define namespace
       var analyticsDatabase = mongoClient.GetDatabase("sample_analytics");
       var accountsCollection = analyticsDatabase.GetCollection<AccountDocument>("accounts");
       var customersCollection = analyticsDatabase.GetCollection<CustomerDocument>("customers");

       // define pipeline stages
       var lookupStage = new BsonDocument("$lookup", new BsonDocument{
         { "from", "accounts" }, { "localField", "accounts" }, { "foreignField", "account_id" }, 
         { "as", "purchases" }, { "pipeline", new BsonArray{
           new BsonDocument("$search", new BsonDocument{
             { "index", "lookup-with-search-tutorial" }, { "compound", new BsonDocument{
               { "must", new BsonArray{
                 new BsonDocument("queryString", new BsonDocument{
                   { "defaultPath", "products" }, { "query", "products: (CurrencyService AND InvestmentStock)" }
                 })
               }}, 
               { "should", new BsonArray{
                 new BsonDocument("range", new BsonDocument{
                   { "path", "limit" }, { "gte", 5000 }, { "lte", 10000 }
                 })
               }}
             }}
           })
         }}
       });
       var projectStage1 = new BsonDocument("$project", new BsonDocument("_id", 0));
       var limitStage = new BsonDocument("$limit", 5);
       var projectStage2 = new BsonDocument("$project", new BsonDocument{
         { "_id", 0 }, { "address", 0 }, { "birthdate", 0 }, { "username", 0 }, { "tier_and_details", 0 }
       });
       var aggregationPipeline = new List<BsonDocument> {lookupStage, projectStage1, limitStage, projectStage2};

       // run pipeline
       var results = customersCollection.Aggregate<BsonDocument>(aggregationPipeline).ToList();

       // print results
       foreach (var acct in results) {
           Console.WriteLine(acct.ToJson());
       }
     }            
   }

   // define fields in the accounts collection
   [BsonIgnoreExtraElements]
   public class AccountDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public string Id { get; set; }

       [BsonElement("account_id")]
       public int AccountId { get; set; }

       [BsonElement("limit")]
       public int Limit { get; set; }
   }

   // define fields in the customers collection
   [BsonIgnoreExtraElements]
   public class CustomerDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public ObjectId Id { get; set; }

       [BsonElement("name")]
       public string Name { get; set; }

       [BsonElement("email")]
       public string Email { get; set; }

       [BsonElement("active")]
       public bool Active { get; set; }

       [BsonElement("accounts")]
       public List<int> Accounts { get; set; }
   }

   // define new array field for matching documents
   public class CustomerLookedUp: CustomerDocument{
       public List<CustomerDocument> Purchases { get; set; }
   }
   ```

2. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Compile and run the `Program.cs` file.

   ```bash
   dotnet run lookup-with-search.csproj
   ```

   **Output:**

   ```javascript
   {
     "name" : "Elizabeth Ray",
     "email" : "arroyocolton@gmail.com",
     "active" : true,
     "accounts" : [371138, 324287, 276528, 332179, 422649,
     387979],
     "purchases" : [
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee58162402"),
          "account_id" : 422649,
          "limit" : 10000,
          "products" : ["CurrencyService", "InvestmentStock"]
        },
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee581623a9"),
          "account_id" : 324287,
          "limit" : 10000,
          "products" : ["Commodity", "CurrencyService", "Derivatives", "InvestmentStock"]
        },
        {
          "_id" : ObjectId("5ca4bbc7a2dd94ee58162400"),
          "account_id" : 332179,
          "limit" : 10000,
          "products" : ["Commodity", "CurrencyService", "InvestmentFund", "Brokerage", "InvestmentStock"]
        }
     ]
   }
   {
     "name" : "Lindsay Cowan",
     "email" : "cooperalexis@hotmail.com",
     "accounts" : [116508],
     "purchases" : []
   }
   {
     "name" : "Katherine David",
     "email" : "timothy78@hotmail.com",
     "accounts" : [462501, 228290, 968786, 515844, 377292],
     "purchases" : [
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623c9"),
         "account_id" : 228290,
         "limit" : 10000,
         "products" : ["CurrencyService", "InvestmentStock", "InvestmentFund", "Brokerage"] },
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623cb"),
         "account_id" : 515844,
         "limit" : 10000,
         "products" : ["Commodity", "CurrencyService", "InvestmentFund", "Brokerage", "InvestmentStock"]
       }
     ]
   }
   {
     "name" : "Leslie Martinez",
     "email" : "tcrawford@gmail.com",
     "accounts" : [170945, 951849],
     "purchases" : []
   }
   {
     "name" : "Brad Cardenas",
     "email" : "dustin37@yahoo.com",
     "accounts" : [721914, 817222, 973067, 260799, 87389],
     "purchases" : [
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623d6"),
         "account_id" : 87389,
         "limit" : 10000,
         "products" : ["CurrencyService", "InvestmentStock"] },
       {
         "_id" : ObjectId("5ca4bbc7a2dd94ee581623d5"),
         "account_id" : 260799,
         "limit" : 10000,
         "products" : ["Brokerage", "InvestmentStock", "Commodity", "CurrencyService"]
       }
     ]
   }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` and initialize your project in that directory:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   dotnet new console
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the index.

   Paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Copy and paste the query into the `Program.cs` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;
   using MongoDB.Driver.Search;

   public class Program
   {
       public static void Main(string[] args)
       {
           // connect to your MongoDB deployment
           string connectionString = "<connection-string>";
           var client = new MongoClient(connectionString);
           
           // define namespace
           var database = client.GetDatabase("sample_training");
           var collection = database.GetCollection<BsonDocument>("companies");
           
           // define pipeline stage
           var searchStage1 = new BsonDocument("$search", new BsonDocument{{ "text", new BsonDocument
               {{ "query", "Mobile" },{ "path", "name" }}
           }});
           var projectStage1 = new BsonDocument("$project", new BsonDocument{
               { "score", new BsonDocument("$meta", "searchScore") },
               { "_id", 0 },{ "number_of_employees", 1 },{ "founded_year", 1 },{ "name", 1 }
           });
           var setStage1 = new BsonDocument("$set", new BsonDocument{{ "source", "companies" }});
           var limitStage1 = new BsonDocument("$limit", 3);
           
           // define subpipeline
           var searchStage2 = new BsonDocument("$search", new BsonDocument{{ "text", new BsonDocument
               {{ "query", "Mobile" },{ "path", "business_name" }}
           }});
           var setStage2 = new BsonDocument("$set", new BsonDocument{ { "source", "inspections" } });
           var projectStage2 = new BsonDocument("$project", new BsonDocument{
               { "score", new BsonDocument("$meta", "searchScore") }, 
               { "source", 1 }, { "_id", 0 }, { "business_name", 1 }, { "address", 1 }
           });
           var limitStage2 = new BsonDocument("$limit", 3);
           var sortStage2 = new BsonDocument("$sort", new BsonDocument{{ "score", -1 }});
           var unionWithPipeline = new List<BsonDocument>{searchStage2, setStage2, projectStage2, limitStage2, sortStage2};
           var unionWithStage = new BsonDocument("$unionWith", new BsonDocument
           {
               { "coll", "inspections" },
               { "pipeline", new BsonArray(unionWithPipeline) }
           });
           var aggregationPipeline = new List<BsonDocument> {searchStage1, projectStage1, setStage1, limitStage1,unionWithStage};
           
           // run pipeline
           var result = collection.Aggregate<BsonDocument>(aggregationPipeline).ToList();
           
           //print results
           foreach (var document in result)
           {
               Console.WriteLine(document);
           }
       }
   }

   ```

2. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Compile and run the `Program.cs` file.

   ### Basic Example

   ```bash
   dotnet run search-with-unionwith.csproj
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : 21, "founded_year" : 2006, "score" : 2.0815043449401855, "source" : "companies" }
   { "name" : "Pulse Mobile", "number_of_employees" : null, "founded_year" : null, "score" : 2.0815043449401855, "source" : "companies" }
   { "name" : "T-Mobile", "number_of_employees" : null, "founded_year" : null, "score" : 2.0815043449401855, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : 11209, "street" : "86TH ST", "number" : 440 }, "source" : "inspections", "score" : 2.9009163379669189 }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : 10458, "street" : "E FORDHAM RD", "number" : 261 }, "source" : "inspections", "score" : 2.9009163379669189 }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : 11419, "street" : "LIBERTY AVE", "number" : 12207 }, "source" : "inspections", "score" : 2.9009163379669189 }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up and initialize the .NET/C# project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` and initialize your project in that directory:

   ```sh
   mkdir search-materialized-view
   cd search-materialized-view
   dotnet new console
   dotnet add package MongoDB.Driver
   ```

   For more detailed installation instructions, see the [MongoDB .NET/C# Driver documentation.](https://www.mongodb.com/docs/drivers/csharp/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Paste the following code into the `Program.cs` file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Driver;
   using System;
   using System.Collections.Generic;

   public class CreateCollection {
       static void Main(string[] args) {
           // Connect to your MongoDB deployment
           var mongoClient = new MongoClient("<connection-string>");
           
           // Define namespace
           var suppliesDatabase = mongoClient.GetDatabase("sample_supplies");
           var purchaseOrdersCollection = suppliesDatabase.GetCollection<BsonDocument>("purchaseOrders");
           
           // Create first document
           var purchaseOrder1 = new BsonDocument
           {
               { "saleDate", new BsonDateTime(new DateTime(2018, 1, 23, 21, 6, 49, 506)) },
               { "items", new BsonArray
                   {
                       new BsonDocument
                       {
                           { "name", "printer paper" },
                           { "tags", new BsonArray { "office", "stationary" } },
                           { "price", 40.01 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 35.29 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "pens" },
                           { "tags", new BsonArray { "writing", "office", "school", "stationary" } },
                           { "price", 56.12 },
                           { "quantity", 5 }
                       },
                       new BsonDocument
                       {
                           { "name", "backpack" },
                           { "tags", new BsonArray { "school", "travel", "kids" } },
                           { "price", 77.71 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 18.47 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 19.95 },
                           { "quantity", 8 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 8.08 },
                           { "quantity", 3 }
                       },
                       new BsonDocument
                       {
                           { "name", "binder" },
                           { "tags", new BsonArray { "school", "general", "organization" } },
                           { "price", 14.16 },
                           { "quantity", 3 }
                       }
                   }
               },
               { "storeLocation", "Denver" },
               { "customer", new BsonDocument
                   {
                       { "gender", "M" },
                       { "age", 42 },
                       { "email", "cauho@witwuta.sv" },
                       { "satisfaction", 4 }
                   }
               },
               { "couponUsed", true },
               { "purchaseMethod", "Phone" }
           };
           
           // Create second document
           var purchaseOrder2 = new BsonDocument
           {
               { "saleDate", new BsonDateTime(new DateTime(2018, 1, 25, 10, 1, 2, 918)) },
               { "items", new BsonArray
                   {
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 8.05 },
                           { "quantity", 10 }
                       },
                       new BsonDocument
                       {
                           { "name", "binder" },
                           { "tags", new BsonArray { "school", "general", "organization" } },
                           { "price", 28.31 },
                           { "quantity", 9 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 20.95 },
                           { "quantity", 3 }
                       },
                       new BsonDocument
                       {
                           { "name", "laptop" },
                           { "tags", new BsonArray { "electronics", "school", "office" } },
                           { "price", 866.5 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "notepad" },
                           { "tags", new BsonArray { "office", "writing", "school" } },
                           { "price", 33.09 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "printer paper" },
                           { "tags", new BsonArray { "office", "stationary" } },
                           { "price", 37.55 },
                           { "quantity", 1 }
                       },
                       new BsonDocument
                       {
                           { "name", "backpack" },
                           { "tags", new BsonArray { "school", "travel", "kids" } },
                           { "price", 83.28 },
                           { "quantity", 2 }
                       },
                       new BsonDocument
                       {
                           { "name", "pens" },
                           { "tags", new BsonArray { "writing", "office", "school", "stationary" } },
                           { "price", 42.9 },
                           { "quantity", 4 }
                       },
                       new BsonDocument
                       {
                           { "name", "envelopes" },
                           { "tags", new BsonArray { "stationary", "office", "general" } },
                           { "price", 16.68 },
                           { "quantity", 2 }
                       }
                   }
               },
               { "storeLocation", "Seattle" },
               { "customer", new BsonDocument
                   {
                       { "gender", "M" },
                       { "age", 50 },
                       { "email", "keecade@hem.uy" },
                       { "satisfaction", 5 }
                   }
               },
               { "couponUsed", false },
               { "purchaseMethod", "Phone" }
           };
           
           // Insert the documents
           purchaseOrdersCollection.InsertOne(purchaseOrder1);
           purchaseOrdersCollection.InsertOne(purchaseOrder2);
           
           Console.WriteLine("Successfully inserted purchase order documents.");
           
           // Query the new collection
           var sort = Builders<BsonDocument>.Sort.Descending("saleDate");
           var findOptions = new FindOptions<BsonDocument> { Sort = sort };
           
           var results = purchaseOrdersCollection.Find(new BsonDocument()).Sort(sort).ToList();
           
           Console.WriteLine("\nQuery results:");
           foreach (var doc in results)
           {
               Console.WriteLine(doc.ToJson());
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { "_id" : ObjectId("..."), "saleDate" : ISODate("2018-01-25T10:01:02.918Z"), "items" : [...], "storeLocation" : "Seattle", "customer" : {...}, "couponUsed" : false, "purchaseMethod" : "Phone" }
   { "_id" : ObjectId("..."), "saleDate" : ISODate("2018-01-23T21:06:49.506Z"), "items" : [...], "storeLocation" : "Denver", "customer" : {...}, "couponUsed" : true, "purchaseMethod" : "Phone" }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Copy and paste the following code into the `Program.cs` file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;
   using System;
   using System.Collections.Generic;
   using System.Threading;
   using System.Threading.Tasks;

   namespace SearchMaterializedView
   {
       class Program
       {
           static async Task Main(string[] args)
           {
               // Connect to MongoDB
               var client = new MongoClient("<connection-string>");
               var database = client.GetDatabase("sample_supplies");
               var sales = database.GetCollection<BsonDocument>("sales");
               var purchaseOrders = database.GetCollection<BsonDocument>("purchaseOrders");
               
               // Update immediately on startup
               await UpdateMonthlyPhoneTransactions(client, sales);
               await UpdateMonthlyPhoneTransactions(client, purchaseOrders);
               Console.WriteLine("Initial update completed. Materialized view is ready.");
               
               // Example of a simple scheduler that updates monthly
               int dayOfMonth = 1; // Update on the 1st of each month
               
               while (true)
               {
                   DateTime now = DateTime.Now;
                   
                   if (now.Day == dayOfMonth && now.Hour == 0 && now.Minute == 0)
                   {
                       // It's midnight on the 1st of the month - update the view
                       await UpdateMonthlyPhoneTransactions(client, sales);
                       await UpdateMonthlyPhoneTransactions(client, purchaseOrders);
                       Console.WriteLine($"Scheduled update completed at {now}");
                       
                       // Sleep for an hour to avoid multiple updates
                       Thread.Sleep(TimeSpan.FromHours(1));
                   }
                   else
                   {
                       // Check again in a minute
                       Thread.Sleep(TimeSpan.FromMinutes(1));
                   }
               }
           }
           
           static async Task UpdateMonthlyPhoneTransactions(MongoClient client, IMongoCollection<BsonDocument> collection)
           {
               // Create the aggregation pipeline
               var pipeline = new BsonDocument[]
               {
                   new BsonDocument("$match", new BsonDocument("purchaseMethod", "Phone")),
                   new BsonDocument("$unwind", new BsonDocument("path", "$items")),
                   new BsonDocument("$group", new BsonDocument
                   {
                       { "_id", new BsonDocument("$dateToString", new BsonDocument
                           {
                               { "format", "%Y-%m" },
                               { "date", "$saleDate" }
                           })
                       },
                       { "sales_quantity", new BsonDocument("$sum", "$items.quantity") },
                       { "sales_price", new BsonDocument("$sum", "$items.price") }
                   }),
                   new BsonDocument("$set", new BsonDocument("sales_price", new BsonDocument("$toDouble", "$sales_price"))),
                   new BsonDocument("$merge", new BsonDocument
                   {
                       { "into", "monthlyPhoneTransactions" },
                       { "whenMatched", "replace" }
                   })
               };

               // Run the aggregation
               await collection.AggregateAsync<BsonDocument>(pipeline);
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following command to build and run this application:

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Paste the following code into the `Program.cs` file:

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Driver;

   // Replace the connection string with your MongoDB deployment's connection string
   var uri = "<connection-string>";

   var client = new MongoClient(uri);

   var db = client.GetDatabase("sample_analytics");
   var collection = db.GetCollection<BsonDocument>("accounts");

   // Create the MongoDB Search index definition
   var index =  new CreateSearchIndexModel(
     "lookup-with-search-tutorial", new BsonDocument
     {
       { "mappings", new BsonDocument
         {
           { "dynamic", true}
         }
       }
     });

   var result = collection.SearchIndexes.CreateOne(index);
   Console.WriteLine($"New index name: {result}");
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Copy and paste the following code into the `Program.cs` file.

   ```csharp
   using MongoDB.Bson;
   using MongoDB.Bson.Serialization.Attributes;
   using MongoDB.Bson.Serialization.Conventions;
   using MongoDB.Driver;
   using MongoDB.Driver.Core;
   using MongoDB.Driver.Search;

   public class MaterializedViewSearchQuery {

     static void Main(string[] args) {
       // allow automapping of the camelCase database fields
       var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
       ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

       // connect to your MongoDB deployment
       var mongoClient = new MongoClient("<connection-string>");
       
       // define namespace
       var suppliesDatabase = mongoClient.GetDatabase("sample_supplies");
       var transactionsCollection = suppliesDatabase.GetCollection<BsonDocument>("monthlyPhoneTransactions");

       // define pipeline stages
       var searchStage = new BsonDocument("$search", new BsonDocument{
         { "index", "monthlySalesIndex" },
         { "range", new BsonDocument{
           { "gt", 10000 },
           { "path", new BsonArray{ "sales_price" } }
         }}
       });
       
       var countStage = new BsonDocument("$count", "months_w_over_10000");
       
       var aggregationPipeline = new List<BsonDocument> { searchStage, countStage };

       // run pipeline
       var results = transactionsCollection.Aggregate<BsonDocument>(aggregationPipeline).ToList();

       // print results
       foreach (var result in results) {
           Console.WriteLine(result.ToJson());
       }
     }            
   }

   // Define a class to represent the materialized view document structure if needed
   [BsonIgnoreExtraElements]
   public class MonthlyTransactionDocument {
       [BsonId]
       [BsonRepresentation(BsonType.ObjectId)]
       [BsonElement("_id")]
       public string Id { get; set; }

       [BsonElement("month")]
       public string Month { get; set; }

       [BsonElement("sales_price")]
       public double SalesPrice { get; set; }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   dotnet run Program.cs
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` for this project:

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the index.

   Create a `CreateIndex.cpp` file in your project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["accounts"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "lookup-with-search-tutorial";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   c++ --std=c++17 CreateIndex.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndex.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.cpp` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.cpp` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["customers"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // Create documents for the lookup pipeline stages
       auto search_doc = make_document(
           kvp("$search", make_document(
               kvp("index", "lookup-with-search-tutorial"),
               kvp("compound", make_document(
                   kvp("must", make_array(
                       make_document(
                           kvp("queryString", make_document(
                               kvp("defaultPath", "products"),
                               kvp("query", "products: (CurrencyService AND InvestmentStock)")
                           ))
                       )
                   )),
                   kvp("should", make_array(
                       make_document(
                           kvp("range", make_document(
                               kvp("path", "limit"),
                               kvp("gte", 5000),
                               kvp("lte", 10000)
                           ))
                       )
                   ))
               ))
           ))
       );
       
       
       auto limit_doc = make_document(kvp("$limit", 5));
       
       auto project_doc = make_document(
           kvp("$project", make_document(
               kvp("_id", 0),
               kvp("address", 0),
               kvp("birthdate", 0),
               kvp("username", 0),
               kvp("tier_and_details", 0)
           ))
       );
       
       auto lookup_pipeline_array = make_array(
           search_doc.view(),
           limit_doc.view(),
           project_doc.view()
       );
       
       pipeline.append_stage(make_document(
           kvp("$lookup", make_document(
               kvp("from", "accounts"),
               kvp("localField", "accounts"),
               kvp("foreignField", "account_id"),
               kvp("as", "purchases"),
               kvp("pipeline", lookup_pipeline_array.view())
           ))
       ));
       
       // Add $limit stage to the main pipeline
       pipeline.limit(5);
       
       // Add $project stage to the main pipeline
       pipeline.project(make_document(
           kvp("_id", 0),
           kvp("address", 0),
           kvp("birthdate", 0),
           kvp("username", 0),
           kvp("tier_and_details", 0)
       ));
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }
   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `lookup-with-search-query.cpp` file.

   ```bash
   c++ --std=c++17 lookup-with-search-query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./o.out
   ./o.out
   ```

   **Output:**

   ```javascript
   { "name" : "Lindsay Cowan", "email" : "cooperalexis@hotmail.com", "accounts" : [ 116508 ], "purchases" : [ ] }
   { "name" : "Dr. Angela Brown", "email" : "michaelespinoza@gmail.com", "accounts" : [ 571880 ], "purchases" : [ ] }
   { "name" : "Brian Flores", "email" : "april04@gmail.com", "accounts" : [ 550665, 321695 ], "purchases" : [ { "account_id" : 321695, "limit" : 10000, "products" : [ "Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock" ] } ] }
   { "name" : "Shirley Rodriguez", "email" : "jonathan95@yahoo.com", "accounts" : [ 784245, 896066, 991412, 951840 ], "purchases" : [ { "account_id" : 991412, "limit" : 10000, "products" : [ "CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock" ] }, { "account_id" : 951840, "limit" : 10000, "products" : [ "InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock" ] }, { "account_id" : 896066, "limit" : 10000, "products" : [ "Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock" ] } ] }
   { "name" : "Clinton Shelton", "email" : "acook@gmail.com", "accounts" : [ 602560, 986196, 51080, 690617, 225602 ], "purchases" : [ ] }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

#### Create the MongoDB Search Index

In this section, you will create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` for this project:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the index.

   Create a `CreateIndexes.cpp` file in your project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_analytics"];
       auto collection = db["accounts"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "lookup-with-search-tutorial";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   c++ --std=c++17 CreateIndexes.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndexes.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.cpp` in your project directory.

2. Copy and paste the query into the `search-with-unionwith-query.cpp` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_training"];
       auto collection = db["companies"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // $search stage
       pipeline.append_stage(make_document(
           kvp("$search", make_document(
               kvp("text", make_document(
                   kvp("query", "Mobile"),
                   kvp("path", "name")
               ))
           ))
       ));
       
       // $project stage
       pipeline.append_stage(make_document(
           kvp("$project", make_document(
               kvp("score", make_document(kvp("$meta", "searchScore"))),
               kvp("_id", 0),
               kvp("number_of_employees", 1),
               kvp("founded_year", 1),
               kvp("name", 1)
           ))
       ));
       
       // $set stage to add source
       pipeline.append_stage(make_document(
           kvp("$set", make_document(
               kvp("source", "companies")
           ))
       ));
       
       // $limit stage
       pipeline.append_stage(make_document(
           kvp("$limit", 3)
       ));
       
       // Create the lookup pipeline for the $unionWith stage
       auto lookup_pipeline = mongocxx::pipeline{};
       
       lookup_pipeline.append_stage(make_document(
           kvp("$search", make_document(
               kvp("text", make_document(
                   kvp("query", "Mobile"),
                   kvp("path", "business_name")
               ))
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$set", make_document(
               kvp("source", "inspections")
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$project", make_document(
               kvp("score", make_document(kvp("$meta", "searchScore"))),
               kvp("source", 1),
               kvp("_id", 0),
               kvp("business_name", 1),
               kvp("address", 1)
           ))
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$limit", 3)
       ));
       
       lookup_pipeline.append_stage(make_document(
           kvp("$sort", make_document(
               kvp("score", -1)
           ))
       ));
       
       // Add $unionWith stage to the main pipeline
       pipeline.append_stage(make_document(
           kvp("$unionWith", make_document(
               kvp("coll", "inspections"),
               kvp("pipeline", lookup_pipeline.view_array())
           ))
       ));
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `search-with-unionwith-query.cpp` file.

   ### Basic Example

   ```bash
   c++ --std=c++17 search-with-unionwith-query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./o.out
   ./o.out
   ```

   **Output:**

   ```javascript
   { "name" : "XLR8 Mobile", "number_of_employees" : 21, "founded_year" : 2006, "score" : 3.33040714263916, "source" : "companies" }
   { "name" : "Pulse Mobile", "score" : 3.33040714263916, "source" : "companies" }
   { "name" : "T-Mobile", "score" : 3.33040714263916, "source" : "companies" }
   { "business_name" : "T. MOBILE", "address" : { "city" : "BROOKLYN", "zip" : 11209, "street" : "86TH ST", "number" : 440 }, "score" : 2.900916337966919, "source" : "inspections" }
   { "business_name" : "BOOST MOBILE", "address" : { "city" : "BRONX", "zip" : 10458, "street" : "E FORDHAM RD", "number" : 261 }, "score" : 2.900916337966919, "source" : "inspections" }
   { "business_name" : "SPRING MOBILE", "address" : { "city" : "SOUTH RICHMOND HILL", "zip" : 11419, "street" : "LIBERTY AVE", "number" : 12207 }, "score" : 2.900916337966919, "source" : "inspections" }
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the C++ project.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` for this project:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   ```

   For more detailed installation instructions, see the [MongoDB C++ Driver documentation.](https://www.mongodb.com/docs/languages/cpp/cpp-driver/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.cpp` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <bsoncxx/types.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/options/find.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;
   using bsoncxx::types::b_date;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["purchaseOrders"];
       
       // Create the first document
       auto doc1 = make_document(
           kvp("saleDate", b_date(std::chrono::milliseconds(1516741609506))), // 2018-01-23T21:06:49.506Z
           kvp("items", make_array(
               make_document(
                   kvp("name", "printer paper"),
                   kvp("tags", make_array("office", "stationary")),
                   kvp("price", 40.01),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 35.29),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "pens"),
                   kvp("tags", make_array("writing", "office", "school", "stationary")),
                   kvp("price", 56.12),
                   kvp("quantity", 5)
               ),
               make_document(
                   kvp("name", "backpack"),
                   kvp("tags", make_array("school", "travel", "kids")),
                   kvp("price", 77.71),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 18.47),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 19.95),
                   kvp("quantity", 8)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 8.08),
                   kvp("quantity", 3)
               ),
               make_document(
                   kvp("name", "binder"),
                   kvp("tags", make_array("school", "general", "organization")),
                   kvp("price", 14.16),
                   kvp("quantity", 3)
               )
           )),
           kvp("storeLocation", "Denver"),
           kvp("customer", make_document(
               kvp("gender", "M"),
               kvp("age", 42),
               kvp("email", "cauho@witwuta.sv"),
               kvp("satisfaction", 4)
           )),
           kvp("couponUsed", true),
           kvp("purchaseMethod", "Phone")
       );
       
       // Create the second document
       auto doc2 = make_document(
           kvp("saleDate", b_date(std::chrono::milliseconds(1516874462918))), // 2018-01-25T10:01:02.918Z
           kvp("items", make_array(
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 8.05),
                   kvp("quantity", 10)
               ),
               make_document(
                   kvp("name", "binder"),
                   kvp("tags", make_array("school", "general", "organization")),
                   kvp("price", 28.31),
                   kvp("quantity", 9)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 20.95),
                   kvp("quantity", 3)
               ),
               make_document(
                   kvp("name", "laptop"),
                   kvp("tags", make_array("electronics", "school", "office")),
                   kvp("price", 866.5),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "notepad"),
                   kvp("tags", make_array("office", "writing", "school")),
                   kvp("price", 33.09),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "printer paper"),
                   kvp("tags", make_array("office", "stationary")),
                   kvp("price", 37.55),
                   kvp("quantity", 1)
               ),
               make_document(
                   kvp("name", "backpack"),
                   kvp("tags", make_array("school", "travel", "kids")),
                   kvp("price", 83.28),
                   kvp("quantity", 2)
               ),
               make_document(
                   kvp("name", "pens"),
                   kvp("tags", make_array("writing", "office", "school", "stationary")),
                   kvp("price", 42.9),
                   kvp("quantity", 4)
               ),
               make_document(
                   kvp("name", "envelopes"),
                   kvp("tags", make_array("stationary", "office", "general")),
                   kvp("price", 16.68),
                   kvp("quantity", 2)
               )
           )),
           kvp("storeLocation", "Seattle"),
           kvp("customer", make_document(
               kvp("gender", "M"),
               kvp("age", 50),
               kvp("email", "keecade@hem.uy"),
               kvp("satisfaction", 5)
           )),
           kvp("couponUsed", false),
           kvp("purchaseMethod", "Phone")
       );
       
       try {
           // Insert the documents
           auto result1 = collection.insert_one(doc1.view());
           auto result2 = collection.insert_one(doc2.view());
           
           std::cout << "Successfully inserted purchase order documents." << std::endl;
           
           // Query the new collection
           mongocxx::options::find options;
           options.sort(make_document(kvp("saleDate", -1)));
           
           auto cursor = collection.find({}, options);
           
           std::cout << "\nQuery results:" << std::endl;
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   c++ --std=c++17 create-collection.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   { _id: ObjectId("..."), saleDate: 2018-01-25T10:01:02.918Z, items: [ ... ], storeLocation: "Seattle", customer: { ... }, couponUsed: false, purchaseMethod: "Phone" }
   { _id: ObjectId("..."), saleDate: 2018-01-23T21:06:49.506Z, items: [ ... ], storeLocation: "Denver", customer: { ... }, couponUsed: true, purchaseMethod: "Phone" }
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 create-collection.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```cpp
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <bsoncxx/builder/stream/document.hpp>
   #include <bsoncxx/builder/stream/helpers.hpp>
   #include <bsoncxx/json.hpp>
   #include <bsoncxx/types.hpp>
   #include <iostream>
   #include <chrono>
   #include <thread>
   #include <ctime>

   using bsoncxx::builder::stream::close_array;
   using bsoncxx::builder::stream::close_document;
   using bsoncxx::builder::stream::document;
   using bsoncxx::builder::stream::finalize;
   using bsoncxx::builder::stream::open_array;
   using bsoncxx::builder::stream::open_document;

   // Function to update the materialized view
   void update_monthly_phone_transactions(mongocxx::client& client, mongocxx::collection collection) {
       // Create the aggregation pipeline
       auto pipeline = mongocxx::pipeline{};
       pipeline.match(document{} << "purchaseMethod" << "Phone" << finalize);
       pipeline.unwind("$items");
       pipeline.group(document{} << "_id" << open_document 
                                 << "$dateToString" << open_document 
                                 << "format" << "%Y-%m" 
                                 << "date" << "$saleDate" 
                                 << close_document << close_document
                                 << "sales_quantity" << open_document 
                                 << "$sum" << "$items.quantity" 
                                 << close_document
                                 << "sales_price" << open_document 
                                 << "$sum" << "$items.price" 
                                 << close_document << finalize);
       pipeline.add_fields(document{} << "sales_price" << open_document 
                                     << "$toDouble" << "$sales_price" 
                                     << close_document << finalize);
       pipeline.merge(document{} << "into" << "monthlyPhoneTransactions" 
                                << "whenMatched" << "replace" << finalize);
       
       // Run the aggregation
       auto cursor = collection.aggregate(pipeline);
       
       for (auto&& doc : cursor) {
           //  Consume the cursor to execute the pipeline
       }
   }

   int main() {
       // Initialize the MongoDB C++ Driver
       mongocxx::instance instance{};
       
       // Connect to MongoDB
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);
       
       auto sales = client["sample_supplies"]["sales"];
       auto purchase_orders = client["sample_supplies"]["purchaseOrders"];
       
       // Update immediately on startup
       update_monthly_phone_transactions(client, sales);
       update_monthly_phone_transactions(client, purchase_orders);
       std::cout << "Initial update completed. Materialized view is ready." << std::endl;
       
       // Example of a simple scheduler that updates monthly
       int day_of_month = 1; // Update on the 1st of each month
       
       while (true) {
           auto now = std::chrono::system_clock::now();
           std::time_t now_c = std::chrono::system_clock::to_time_t(now);
           std::tm* local_time = std::localtime(&now_c);
           
           if (local_time->tm_mday == day_of_month && local_time->tm_hour == 0 && local_time->tm_min == 0) {
               // It's midnight on the 1st of the month - update the view
               update_monthly_phone_transactions(client, sales);
               update_monthly_phone_transactions(client, purchase_orders);
               std::cout << "Scheduled update completed at " << std::ctime(&now_c);
               
               // Sleep for an hour to avoid multiple updates
               std::this_thread::sleep_for(std::chrono::hours(1));
           } else {
               // Check again in a minute
               std::this_thread::sleep_for(std::chrono::minutes(1));
           }
       }
       
       return 0;
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following commands to build and run this application:

   ```shell
   c++ --std=c++17 create_update_view.cpp $(pkg-config --cflags --libs libmongocxx) -o ./view_updater.out
   ./view_updater.out
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 create_update_view.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./view_updater.out
   ./view_updater.out
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["monthlyPhoneTransactions"];
       auto siv = collection.search_indexes();

       // Create the MongoDB Search index definition
       auto name = "monthlySalesIndex";
       auto definition = make_document(
           kvp("mappings", make_document(
               kvp("dynamic", true)
           ))
       );
       auto model = mongocxx::search_index_model(name, definition.view());

       try {
           // Execute the command to create the index
           auto result = siv.create_one(model);
           std::cout << "New index name: " << result << std::endl;
       } catch (const std::exception& e) {
           std::cerr << "Error creating Atlas Search index: " << e.what() << std::endl;
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   c++ --std=c++17 CreateIndex.cpp $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 CreateIndex.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./app.out
   ./app.out
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.cpp` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```cpp
   #include <iostream>

   #include <bsoncxx/builder/basic/document.hpp>
   #include <bsoncxx/builder/basic/array.hpp>
   #include <bsoncxx/json.hpp>
   #include <mongocxx/client.hpp>
   #include <mongocxx/instance.hpp>
   #include <mongocxx/uri.hpp>
   #include <mongocxx/pipeline.hpp>

   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;
   using bsoncxx::builder::basic::make_array;

   int main() {
       // Initialize the MongoDB C++ driver
       mongocxx::instance instance{};
       
       // Connect to your MongoDB deployment
       mongocxx::uri uri("<connection-string>");
       mongocxx::client client(uri);

       auto db = client["sample_supplies"];
       auto collection = db["monthlyPhoneTransactions"];
       
       // Build the aggregation pipeline
       mongocxx::pipeline pipeline{};
       
       // Create the $search stage
       auto search_doc = make_document(
           kvp("$search", make_document(
               kvp("index", "monthlySalesIndex"),
               kvp("range", make_document(
                   kvp("gt", 10000),
                   kvp("path", make_array("sales_price"))
               ))
           ))
       );
       
       // Create the $count stage
       auto count_doc = make_document(
           kvp("$count", "months_w_over_10000")
       );
       
       // Add stages to the pipeline
       pipeline.append_stage(search_doc.view());
       pipeline.append_stage(count_doc.view());
       
       try {
           // Run the aggregation pipeline and print results
           auto cursor = collection.aggregate(pipeline);

           // Process and print the results
           for (auto&& doc : cursor) {
               std::cout << bsoncxx::to_json(doc) << std::endl;
           }
       } catch (const std::exception& e) {
           std::cerr << "Error executing query: " << e.what() << std::endl;
       }
       
       return 0;
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the query.

   ```shell
   c++ --std=c++17 query.cpp $(pkg-config --cflags --libs libmongocxx) -o ./query.out
   ./query.out
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   **Tip: MacOS Error**

   MacOS users might see the following error after running the preceding commands:

   ```sh
   dyld[54430]: Library not loaded: @rpath/libmongocxx._noabi.dylib
   ```

   To resolve this error, use the `-Wl,-rpath` linker option to set the `@rpath`, as shown in the following code:

   ```sh
   c++ --std=c++17 query.cpp -Wl,-rpath,/usr/local/lib/ $(pkg-config --cflags --libs libmongocxx) -o ./query.out
   ./query.out
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `atlas-search-project` and initialize your project in that directory:

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   go mod init atlas-search-project

   # Add the MongoDB Go Driver to your project
   go get go.mongodb.org/mongo-driver/v2/mongo
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the index.

   Create a `create_index.go` file in your project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Create the index.

   ```shell
   go run create_index.go
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.go`

2. Copy and paste the query into the `lookup-with-search-query.go` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	var err error
   	// connect to the MongoDB deployment
   	ctx := context.Background()
   	clOpts := options.Client().ApplyURI("<connection-string>").SetTimeout(5 * time.Second)
   	client, err := mongo.Connect(clOpts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)
   	// set namespace
   	collection := client.Database("sample_analytics").Collection("customers")
   	// define pipeline
   	lookupStage := bson.D{{Key: "$lookup", Value: bson.D{
   		{Key: "from", Value: "accounts"},
   		{Key: "localField", Value: "accounts"},
   		{Key: "foreignField", Value: "account_id"},
   		{Key: "as", Value: "purchases"},
   		{Key: "pipeline", Value: bson.A{
   			bson.D{
   				{Key: "$search", Value: bson.D{
   					{Key: "index", Value: "lookup-with-search-tutorial"},
   					{Key: "compound", Value: bson.D{
   						{Key: "must", Value: bson.A{
   							bson.D{{Key: "queryString", Value: bson.D{
   								{Key: "defaultPath", Value: "products"},
   								{Key: "query", Value: "products: (CurrencyService AND InvestmentStock)"},
   							}}},
   						}},
   						{Key: "should", Value: bson.A{
   							bson.D{{Key: "range", Value: bson.D{
   								{Key: "path", Value: "limit"},
   								{Key: "gte", Value: 5000},
   								{Key: "lte", Value: 10000},
   							}}},
   						}},
   					}},
   				}},
   			},
   			bson.D{{Key: "$project", Value: bson.D{
   				{Key: "_id", Value: 0},
   				{Key: "address", Value: 0},
   				{Key: "birthdate", Value: 0},
   				{Key: "username", Value: 0},
   				{Key: "tier_and_details", Value: 0},
   			}}},
   		}},
   	}}}
   	limitStage := bson.D{{Key: "$limit", Value: 5}}
   	projectStage := bson.D{{Key: "$project", Value: bson.D{
   		{Key: "name", Value: 1},
   		{Key: "email", Value: 1},
   		{Key: "active", Value: 1},
   		{Key: "accounts", Value: 1},
   		{Key: "purchases", Value: 1},
   	}}}

   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{lookupStage, limitStage, projectStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ```shell
   go run lookup-with-search-query.go
   ```

   **Output:**

   ```none
   {"_id":{"$oid":"..."},"name":"Lindsay Cowan","email":"cooperalexis@hotmail.com","accounts":[{"$numberInt":"116508"}],"purchases":[]}
   {"_id":{"$oid":"..."},"name":"Dr. Angela Brown","email":"michaelespinoza@gmail.com","accounts":[{"$numberInt":"571880"}],"purchases":[]}
   {"_id":{"$oid":"..."},"name":"Brian Flores","email":"april04@gmail.com","accounts":[{"$numberInt":"550665"},{"$numberInt":"321695"}],"purchases":[{"account_id":{"$numberInt":"321695"},"limit":{"$numberInt":"10000"},"products":["Derivatives","Commodity","CurrencyService","Brokerage","InvestmentStock"]}]}
   {"_id":{"$oid":"..."},"name":"Shirley Rodriguez","email":"jonathan95@yahoo.com","accounts":[{"$numberInt":"784245"},{"$numberInt":"896066"},{"$numberInt":"991412"},{"$numberInt":"951840"}],"purchases":[{"account_id":{"$numberInt":"991412"},"limit":{"$numberInt":"10000"},"products":["CurrencyService","Commodity","InvestmentFund","InvestmentStock"]},{"account_id":{"$numberInt":"951840"},"limit":{"$numberInt":"10000"},"products":["InvestmentFund","Commodity","CurrencyService","InvestmentStock"]},{"account_id":{"$numberInt":"896066"},"limit":{"$numberInt":"10000"},"products":["Derivatives","InvestmentFund","Brokerage","CurrencyService","InvestmentStock"]}]}
   {"_id":{"$oid":"..."},"name":"Clinton Shelton","email":"acook@gmail.com","accounts":[{"$numberInt":"602560"},{"$numberInt":"986196"},{"$numberInt":"51080"},{"$numberInt":"690617"},{"$numberInt":"225602"}],"purchases":[]}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-with-unionwith` and initialize your project in that directory:

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   go mod init search-with-unionwith
   go get go.mongodb.org/mongo-driver/v2
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the index.

   Create a `create_indexes.go` file in your project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Create the index.

   ```shell
   go run create_indexes.go
   ```

   **Output:**

   ```text
   Index created on companies collection: default
   Index created on inspections collection: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.go`

2. Copy and paste the query into the `search-with-unionwith-query.go` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	// connect to your MongoDB deployment and set a maximum operation time
   	ctx := context.Background()
   	opts := options.Client().
   		SetTimeout(5 * time.Second).
   		ApplyURI("<connection-string>")

   	client, err := mongo.Connect(opts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)

   	// set namespace
   	collection := client.Database("sample_training").Collection("companies")
   	// define pipeline
   	searchStage := bson.D{{"$search", bson.D{
   		{"text", bson.D{
   			{"query", "Mobile"}, {"path", "name"},
   		}},
   	}}}
   	projectStage := bson.D{{"$project", bson.D{
   		{"score", bson.D{{"$meta", "searchScore"}}},
   		{"_id", 0},
   		{"number_of_employees", 1},
   		{"founded_year", 1},
   		{"name", 1},
   	}}}
   	setStage := bson.D{{"$set", bson.D{{"source", "companies"}}}}
   	limitStage := bson.D{{"$limit", 5}}
   	unionWithStage := bson.D{{"$unionWith", bson.D{
   		{"coll", "inspections"},
   		{"pipeline", bson.A{
   			bson.D{{"$search", bson.D{
   				{"text", bson.D{
   					{"query", "Mobile"}, {"path", "business_name"},
   				}},
   			}}},
   			bson.D{{"$set", bson.D{{"source", "inspections"}}}},
   			bson.D{{"$project", bson.D{
   				{"score", bson.D{{"$meta", "searchScore"}}},
   				{"source", 1},
   				{"_id", 0},
   				{"business_name", 1},
   				{"address", 1},
   			}}},
   			bson.D{{"$limit", 3}},
   			bson.D{{"$sort", bson.D{{"score", -1}}}},
   		}},
   	}}}
   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{searchStage, projectStage, setStage, limitStage, unionWithStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ### Basic Example

   ```shell
   go run search-with-unionwith-query.go
   ```

   **Output:**

   ```javascript
   [{name XLR8 Mobile} {number_of_employees 21} {founded_year 2006} {score 3.33040714263916} {source companies} {source_count 52}]
   [{name Pulse Mobile} {number_of_employees <nil>} {founded_year <nil>} {score 3.33040714263916} {source companies} {source_count 52}]
   [{name T-Mobile} {number_of_employees <nil>} {founded_year <nil>} {score 3.33040714263916} {source companies} {source_count 52}]
   [{business_name T. MOBILE} {address [{city BROOKLYN} {zip 11209} {street 86TH ST} {number 440}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   [{business_name BOOST MOBILE} {address [{city BRONX} {zip 10458} {street E FORDHAM RD} {number 261}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   [{business_name SPRING MOBILE} {address [{city SOUTH RICHMOND HILL} {zip 11419} {street LIBERTY AVE} {number 12207}]} {score 2.900916337966919} {source inspections} {source_count 456}]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up and initialize the Go module.

   In your terminal, navigate to where you want to create your application, then run the following command to create a directory called `search-materialized-view` and initialize your project in that directory:

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   go mod init search-materialized-view
   go get go.mongodb.org/mongo-driver/v2/mongo
   ```

   For more detailed installation instructions, see the [MongoDB Go Driver documentation.](https://www.mongodb.com/docs/drivers/go/current/get-started.md#std-label-go-get-started)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.go` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	// Connect to the MongoDB deployment
   	ctx := context.Background()
   	client, err := mongo.Connect(options.Client().ApplyURI("<connection-string>"))
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)

   	// Set namespace
   	collection := client.Database("sample_supplies").Collection("purchaseOrders")

   	// Create first document
   	jan23, _ := time.Parse(time.RFC3339, "2018-01-23T21:06:49.506Z")
   	purchaseOrder1 := bson.M{
   		"saleDate": jan23,
   		"items": []bson.M{
   			{
   				"name":     "printer paper",
   				"tags":     []string{"office", "stationary"},
   				"price":    40.01,
   				"quantity": 2,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    35.29,
   				"quantity": 2,
   			},
   			{
   				"name":     "pens",
   				"tags":     []string{"writing", "office", "school", "stationary"},
   				"price":    56.12,
   				"quantity": 5,
   			},
   			{
   				"name":     "backpack",
   				"tags":     []string{"school", "travel", "kids"},
   				"price":    77.71,
   				"quantity": 2,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    18.47,
   				"quantity": 2,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    19.95,
   				"quantity": 8,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    8.08,
   				"quantity": 3,
   			},
   			{
   				"name":     "binder",
   				"tags":     []string{"school", "general", "organization"},
   				"price":    14.16,
   				"quantity": 3,
   			},
   		},
   		"storeLocation": "Denver",
   		"customer": bson.M{
   			"gender":       "M",
   			"age":          42,
   			"email":        "cauho@witwuta.sv",
   			"satisfaction": 4,
   		},
   		"couponUsed":     true,
   		"purchaseMethod": "Phone",
   	}

   	// Create second document
   	jan25, _ := time.Parse(time.RFC3339, "2018-01-25T10:01:02.918Z")
   	purchaseOrder2 := bson.M{
   		"saleDate": jan25,
   		"items": []bson.M{
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    8.05,
   				"quantity": 10,
   			},
   			{
   				"name":     "binder",
   				"tags":     []string{"school", "general", "organization"},
   				"price":    28.31,
   				"quantity": 9,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    20.95,
   				"quantity": 3,
   			},
   			{
   				"name":     "laptop",
   				"tags":     []string{"electronics", "school", "office"},
   				"price":    866.5,
   				"quantity": 4,
   			},
   			{
   				"name":     "notepad",
   				"tags":     []string{"office", "writing", "school"},
   				"price":    33.09,
   				"quantity": 4,
   			},
   			{
   				"name":     "printer paper",
   				"tags":     []string{"office", "stationary"},
   				"price":    37.55,
   				"quantity": 1,
   			},
   			{
   				"name":     "backpack",
   				"tags":     []string{"school", "travel", "kids"},
   				"price":    83.28,
   				"quantity": 2,
   			},
   			{
   				"name":     "pens",
   				"tags":     []string{"writing", "office", "school", "stationary"},
   				"price":    42.9,
   				"quantity": 4,
   			},
   			{
   				"name":     "envelopes",
   				"tags":     []string{"stationary", "office", "general"},
   				"price":    16.68,
   				"quantity": 2,
   			},
   		},
   		"storeLocation": "Seattle",
   		"customer": bson.M{
   			"gender":       "M",
   			"age":          50,
   			"email":        "keecade@hem.uy",
   			"satisfaction": 5,
   		},
   		"couponUsed":     false,
   		"purchaseMethod": "Phone",
   	}

   	// Insert the documents
   	_, err = collection.InsertOne(ctx, purchaseOrder1)
   	if err != nil {
   		panic(err)
   	}

   	_, err = collection.InsertOne(ctx, purchaseOrder2)
   	if err != nil {
   		panic(err)
   	}

   	fmt.Println("Successfully inserted purchase order documents.")

   	// Query the new collection
   	findOptions := options.Find().SetSort(bson.D{{Key: "saleDate", Value: -1}})
   	cursor, err := collection.Find(ctx, bson.D{}, findOptions)
   	if err != nil {
   		panic(err)
   	}
   	defer cursor.Close(ctx)

   	// Print results
   	var results []bson.M
   	if err = cursor.All(ctx, &results); err != nil {
   		panic(err)
   	}

   	fmt.Println("\nQuery results:")
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   ### Atlas Cluster

   Your connection string should use the following format:

   ```text
   mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
   ```

   IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   go run create-collection.go
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   map[_id:ObjectId("...") couponUsed:false customer:map[age:50 email:keecade@hem.uy gender:M satisfaction:5] items:[map[quantity:10] map[quantity:9] map[quantity:3] map[quantity:4] map[quantity:4] map[quantity:1] map[quantity:2] map[quantity:4]] purchaseMethod:Phone saleDate:2018-01-25 10:01:02.918 +0000 UTC storeLocation:Seattle]
   map[_id:ObjectId("...") couponUsed:true customer:map[age:42 email:cauho@witwuta.sv gender:M satisfaction:4] items:[map[quantity:2] map[quantity:2] map[quantity:5] map[quantity:2] map[quantity:2] map[quantity:8] map[quantity:3]] purchaseMethod:Phone saleDate:2018-01-23 21:06:49.506 +0000 UTC storeLocation:Denver]
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `create_update_view.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func updateMonthlyPhoneTransactions(ctx context.Context, client *mongo.Client, collection *mongo.Collection) error {
   	// Create the aggregation pipeline
   	matchStage := bson.D{bson.E{Key: "$match", Value: bson.D{bson.E{Key: "purchaseMethod", Value: "Phone"}}}}
   	unwindStage := bson.D{bson.E{Key: "$unwind", Value: bson.D{bson.E{Key: "path", Value: "$items"}}}}
   	groupStage := bson.D{bson.E{Key: "$group", Value: bson.D{
   		bson.E{Key: "_id", Value: bson.D{
   			bson.E{Key: "$dateToString", Value: bson.D{
   				bson.E{Key: "format", Value: "%Y-%m"},
   				bson.E{Key: "date", Value: "$saleDate"},
   			}},
   		}},
   		bson.E{Key: "sales_quantity", Value: bson.D{bson.E{Key: "$sum", Value: "$items.quantity"}}},
   		bson.E{Key: "sales_price", Value: bson.D{bson.E{Key: "$sum", Value: "$items.price"}}},
   	}}}
   	setStage := bson.D{bson.E{Key: "$set", Value: bson.D{bson.E{Key: "sales_price", Value: bson.D{bson.E{Key: "$toDouble", Value: "$sales_price"}}}}}}
   	mergeStage := bson.D{bson.E{Key: "$merge", Value: bson.D{
   		bson.E{Key: "into", Value: "monthlyPhoneTransactions"},
   		bson.E{Key: "whenMatched", Value: "replace"},
   	}}}

   	pipeline := mongo.Pipeline{matchStage, unwindStage, groupStage, setStage, mergeStage}

   	// Run the aggregation
   	cursor, err := collection.Aggregate(ctx, pipeline)
   	if err != nil {
   		return err
   	}
   	defer cursor.Close(ctx)
   	return nil
   }

   func main() {
   	// Create a context
   	ctx := context.Background()

   	// Connect to MongoDB
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().ApplyURI(uri))
   	if err != nil {
   		log.Fatal(err)
   	}

   	// Ensure the client will be properly disconnected when main exits
   	defer func() {
   		if err := client.Disconnect(ctx); err != nil {
   			log.Fatal(err)
   		}
   	}()

   	// Ping the MongoDB server to verify the connection
   	if err := client.Ping(ctx, nil); err != nil {
   		log.Fatal(err)
   	}

   	sales := client.Database("sample_supplies").Collection("sales")
   	purchaseOrders := client.Database("sample_supplies").Collection("purchaseOrders")

   	// Update immediately on startup
   	if err := updateMonthlyPhoneTransactions(ctx, client, sales); err != nil {
   		log.Fatal(err)
   	}
   	if err := updateMonthlyPhoneTransactions(ctx, client, purchaseOrders); err != nil {
   		log.Fatal(err)
   	}
   	fmt.Println("Initial update completed. Materialized view is ready.")

   	// Example of a simple scheduler that updates monthly
   	dayOfMonth := 1 // Update on the 1st of each month

   	for {
   		now := time.Now()

   		if now.Day() == dayOfMonth && now.Hour() == 0 && now.Minute() == 0 {
   			// Create a new context for each update
   			updateCtx, cancel := context.WithTimeout(ctx, 30*time.Minute)

   			// It's midnight on the 1st of the month - update the view
   			if err := updateMonthlyPhoneTransactions(updateCtx, client, sales); err != nil {
   				log.Println("Error updating from sales:", err)
   			}
   			if err := updateMonthlyPhoneTransactions(updateCtx, client, purchaseOrders); err != nil {
   				log.Println("Error updating from purchase orders:", err)
   			}

   			// Cancel the context when done
   			cancel()

   			fmt.Printf("Scheduled update completed at %s\n", now.Format(time.RFC1123))

   			// Sleep for an hour to avoid multiple updates
   			time.Sleep(1 * time.Hour)
   		} else {
   			// Check again in a minute
   			time.Sleep(1 * time.Minute)
   		}
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the application.

   In your terminal, run the following command to build and run this application:

   ```shell
   go run create_update_view.go
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"log"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	ctx := context.Background()
   	uri := "<connection-string>"
   	client, err := mongo.Connect(options.Client().
   		ApplyURI(uri))
   	if err != nil {
   		panic(err)
   	}

   	defer func() {
   		if err := client.Disconnect(context.TODO()); err != nil {
   			panic(err)
   		}
   	}()

   	coll := client.Database("sample_analytics").Collection("accounts")
   	const indexName = "lookup-with-search-tutorial"
   	opts := options.SearchIndexes().SetName(indexName).SetType("search")

   	// Define the MongoDB Search index
   	searchIndexModel := mongo.SearchIndexModel{
   		Definition: bson.D{
   			{"mappings", bson.D{
   				{"dynamic", true},
   			}},
   		},
   		Options: opts,
   	}

   	// Create the index
   	searchIndexName, err := coll.SearchIndexes().CreateOne(ctx, searchIndexModel)
   	if err != nil {
   		log.Fatalf("Failed to create the Atlas Search index: %v", err)
   	}

   	fmt.Println("Name of index created: " + searchIndexName)
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   go run create_index.go
   ```

   **Output:**

   ```text
   Name of index created: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.go` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```go
   package main

   import (
   	"context"
   	"fmt"
   	"time"

   	"go.mongodb.org/mongo-driver/v2/bson"
   	"go.mongodb.org/mongo-driver/v2/mongo"
   	"go.mongodb.org/mongo-driver/v2/mongo/options"
   )

   func main() {
   	var err error
   	// connect to the MongoDB deployment
   	ctx := context.Background()
   	clOpts := options.Client().ApplyURI("<connection-string>").SetTimeout(5 * time.Second)
   	client, err := mongo.Connect(clOpts)
   	if err != nil {
   		panic(err)
   	}
   	defer client.Disconnect(ctx)
   	// set namespace
   	collection := client.Database("sample_analytics").Collection("customers")
   	// define pipeline
   	lookupStage := bson.D{{Key: "$lookup", Value: bson.D{
   		{Key: "from", Value: "accounts"},
   		{Key: "localField", Value: "accounts"},
   		{Key: "foreignField", Value: "account_id"},
   		{Key: "as", Value: "purchases"},
   		{Key: "pipeline", Value: bson.A{
   			bson.D{
   				{Key: "$search", Value: bson.D{
   					{Key: "index", Value: "lookup-with-search-tutorial"},
   					{Key: "compound", Value: bson.D{
   						{Key: "must", Value: bson.A{
   							bson.D{{Key: "queryString", Value: bson.D{
   								{Key: "defaultPath", Value: "products"},
   								{Key: "query", Value: "products: (CurrencyService AND InvestmentStock)"},
   							}}},
   						}},
   						{Key: "should", Value: bson.A{
   							bson.D{{Key: "range", Value: bson.D{
   								{Key: "path", Value: "limit"},
   								{Key: "gte", Value: 5000},
   								{Key: "lte", Value: 10000},
   							}}},
   						}},
   					}},
   				}},
   			},
   			bson.D{{Key: "$project", Value: bson.D{
   				{Key: "_id", Value: 0},
   				{Key: "address", Value: 0},
   				{Key: "birthdate", Value: 0},
   				{Key: "username", Value: 0},
   				{Key: "tier_and_details", Value: 0},
   			}}},
   		}},
   	}}}
   	limitStage := bson.D{{Key: "$limit", Value: 5}}
   	projectStage := bson.D{{Key: "$project", Value: bson.D{
   		{Key: "name", Value: 1},
   		{Key: "email", Value: 1},
   		{Key: "active", Value: 1},
   		{Key: "accounts", Value: 1},
   		{Key: "purchases", Value: 1},
   	}}}

   	// run pipeline
   	cursor, err := collection.Aggregate(ctx, mongo.Pipeline{lookupStage, limitStage, projectStage})
   	if err != nil {
   		panic(err)
   	}
   	// print results
   	var results []bson.D
   	if err = cursor.All(context.TODO(), &results); err != nil {
   		panic(err)
   	}
   	for _, result := range results {
   		fmt.Println(result)
   	}
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   go run query.go
   ```

   **Output:**

   ```text
   map[months_w_over_10000:4]
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.java` file and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `LookupWithSearchQuery.java` in your project directory.

2. Copy and paste the query into the `LookupWithSearchQuery.java` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```java
   import static com.mongodb.client.model.Aggregates.limit;
   import static com.mongodb.client.model.Aggregates.project;
   import static com.mongodb.client.model.Projections.*;
   import java.util.Arrays;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;

   public class LookupWithSearchQuery {

     public static void main(String[] args) {
       // connect to your MongoDB deployment
       String uri = "<connection-string>";
           
       try (MongoClient mongoClient = MongoClients.create(uri)) {
         // set namespace
         MongoDatabase database = mongoClient.getDatabase("sample_analytics");
         MongoCollection<Document> collection = database.getCollection("customers");
               
         // define pipeline
         Document agg = new Document("$lookup", 
           new Document("from", "accounts")
             .append("localField", "accounts")
             .append("foreignField", "account_id")
             .append("as", "purchases")
             .append("pipeline", Arrays.asList(new Document("$search", 
               new Document("index", "lookup-with-search-tutorial")
                 .append("compound", 
                 new Document("must", Arrays.asList(new Document("queryString", 
                   new Document("defaultPath", "products")
                     .append("query", "products: (CurrencyService AND InvestmentStock)"))))
                     .append("should", Arrays.asList(new Document("range", 
                       new Document("path", "limit")
                         .append("gte", 5000L)
                         .append("lte", 10000L)
                     )))
                 )
             ), 
             new Document("$limit", 5L),
             new Document("$project", 
             new Document("_id", 0L)
               .append("address", 0L)
               .append("birthdate", 0L)
               .append("username", 0L)
               .append("tier_and_details", 0L)
           )))
         );
         // run pipeline and print results
         collection.aggregate(Arrays.asList(agg,
           limit(5),
           project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
         ))
         .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `LookupWithSearchQuery.java` file.

   ```bash
   javac LookupWithSearchQuery.java
   java LookupWithSearchQuery
   ```

   **Output:**

   ```json
   {"name": "Lindsay Cowan", "email": "cooperalexis@hotmail.com", "accounts": [116508], "purchases": []}
   {"name": "Dr. Angela Brown", "email": "michaelespinoza@gmail.com", "accounts": [571880], "purchases": []}
   {"name": "Brian Flores", "email": "april04@gmail.com", "accounts": [550665, 321695], "purchases": [{"account_id": 321695, "limit": 10000, "products": ["Derivatives", "Commodity", "CurrencyService", "Brokerage", "InvestmentStock"]}]}
   {"name": "Shirley Rodriguez", "email": "jonathan95@yahoo.com", "accounts": [784245, 896066, 991412, 951840], "purchases": [{"account_id": 991412, "limit": 10000, "products": ["CurrencyService", "Commodity", "InvestmentFund", "InvestmentStock"]}, {"account_id": 951840, "limit": 10000, "products": ["InvestmentFund", "Commodity", "CurrencyService", "InvestmentStock"]}, {"account_id": 896066, "limit": 10000, "products": ["Derivatives", "InvestmentFund", "Brokerage", "CurrencyService", "InvestmentStock"]}]}
   {"name": "Clinton Shelton", "email": "acook@gmail.com", "accounts": [602560, 986196, 51080, 690617, 225602], "purchases": []}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.java` file and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `SearchWithUnionwithQuery.java`.

2. Copy and paste the query into the `SearchWithUnionwithQuery.java` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```java
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;
   import java.util.ArrayList;
   import java.util.Arrays;
   import java.util.List;

   public class SearchWithUnionwith {
     public static void main(String[] args) {
     // connect to your MongoDB deployment
       try (MongoClient mongoClient = MongoClients.create("<connection-string>")) {
         // get database name
         MongoDatabase database = mongoClient.getDatabase("sample_training");
         // define pipeline
         List<Document> pipeline1 = Arrays.asList(
           new Document("$search", new Document("text",
             new Document("query", "Mobile")
               .append("path", "name"))),
           new Document("$project", new Document("score",
             new Document("$meta", "searchScore"))
               .append("_id", 0)
               .append("number_of_employees", 1)
               .append("founded_year", 1)
               .append("name", 1)),
           new Document("$set", new Document("source", "companies")),
           new Document("$limit", 3)
         );

         List<Document> pipeline2 = Arrays.asList(
           new Document("$search", new Document("text",
             new Document("query", "Mobile")
               .append("path", "business_name"))),
           new Document("$set", new Document("source", "inspections")),
           new Document("$project", new Document("score",
             new Document("$meta", "searchScore"))
               .append("source", 1)
               .append("_id", 0)
               .append("business_name", 1)
               .append("address", 1)),
           new Document("$limit", 3),
           new Document("$sort", new Document("score", -1))
         );

         List<Document> unionWithStage = new ArrayList<>();
         Document unionWith = new Document("$unionWith", new Document("coll", "inspections")
           .append("pipeline", pipeline2));
         unionWithStage.add(unionWith);

         List<Document> finalPipeline = new ArrayList<>(pipeline1);
         finalPipeline.addAll(unionWithStage);
         // run pipeline and print results
         database.getCollection("companies").aggregate(finalPipeline)
           .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Compile and run the `UinionwithWithSearchQuery.java` file.

   ### Basic Example

   ```bash
   javac SearchWithUnionwithQuery.java
   java SearchWithUnionwithQuery
   ```

   **Output:**

   ```json
   {"name": "XLR8 Mobile", "number_of_employees": 21, "founded_year": 2006, "score": 2.0815043449401855, "source": "companies"}
   {"name": "Pulse Mobile", "number_of_employees": null, "founded_year": null, "score": 2.0815043449401855, "source": "companies"}
   {"name": "T-Mobile", "number_of_employees": null, "founded_year": null, "score": 2.0815043449401855, "source": "companies"}
   {"business_name": "T. MOBILE", "address": {"city": "BROOKLYN", "zip": 11209, "street": "86TH ST", "number": 440}, "source": "inspections", "score": 2.900916337966919}
   {"business_name": "BOOST MOBILE", "address": {"city": "BRONX", "zip": 10458, "street": "E FORDHAM RD", "number": 261}, "source": "inspections", "score": 2.900916337966919}
   {"business_name": "SPRING MOBILE", "address": {"city": "SOUTH RICHMOND HILL", "zip": 11419, "street": "LIBERTY AVE", "number": 12207}, "source": "inspections", "score": 2.900916337966919}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up your Java project with the MongoDB Java driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Java Sync Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>    
         <groupId>org.mongodb</groupId>    
         <artifactId>mongodb-driver-sync</artifactId>    
         <version>4.11.1</version>    
       </dependency>
   </dependencies>

   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Java Driver documentation.](https://www.mongodb.com/docs/drivers/java/sync/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   In your project's base package directory, create a `CreateCollection.java` file and copy and paste the following code into this file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```java
   import java.util.Arrays;
   import java.util.Date;
   import java.util.ArrayList;
   import java.util.List;

   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoCursor;
   import com.mongodb.client.MongoDatabase;
   import com.mongodb.client.model.Sorts;
   import org.bson.Document;

   public class CreateCollection {

       public static void main(String[] args) {
           // Connect to your MongoDB deployment
           String uri = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(uri)) {
               // Set namespace
               MongoDatabase database = mongoClient.getDatabase("sample_supplies");
               MongoCollection<Document> collection = database.getCollection("purchaseOrders");
               
               // Create first document
               Document purchaseOrder1 = new Document("saleDate", new Date(1516741609506L)) // 2018-01-23T21:06:49.506Z
                   .append("items", Arrays.asList(
                       new Document("name", "printer paper")
                           .append("tags", Arrays.asList("office", "stationary"))
                           .append("price", 40.01)
                           .append("quantity", 2),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 35.29)
                           .append("quantity", 2),
                       new Document("name", "pens")
                           .append("tags", Arrays.asList("writing", "office", "school", "stationary"))
                           .append("price", 56.12)
                           .append("quantity", 5),
                       new Document("name", "backpack")
                           .append("tags", Arrays.asList("school", "travel", "kids"))
                           .append("price", 77.71)
                           .append("quantity", 2),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 18.47)
                           .append("quantity", 2),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 19.95)
                           .append("quantity", 8),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 8.08)
                           .append("quantity", 3),
                       new Document("name", "binder")
                           .append("tags", Arrays.asList("school", "general", "organization"))
                           .append("price", 14.16)
                           .append("quantity", 3)
                   ))
                   .append("storeLocation", "Denver")
                   .append("customer", new Document()
                       .append("gender", "M")
                       .append("age", 42)
                       .append("email", "cauho@witwuta.sv")
                       .append("satisfaction", 4)
                   )
                   .append("couponUsed", true)
                   .append("purchaseMethod", "Phone");
               
               // Create second document
               Document purchaseOrder2 = new Document("saleDate", new Date(1516874462918L)) // 2018-01-25T10:01:02.918Z
                   .append("items", Arrays.asList(
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 8.05)
                           .append("quantity", 10),
                       new Document("name", "binder")
                           .append("tags", Arrays.asList("school", "general", "organization"))
                           .append("price", 28.31)
                           .append("quantity", 9),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 20.95)
                           .append("quantity", 3),
                       new Document("name", "laptop")
                           .append("tags", Arrays.asList("electronics", "school", "office"))
                           .append("price", 866.5)
                           .append("quantity", 4),
                       new Document("name", "notepad")
                           .append("tags", Arrays.asList("office", "writing", "school"))
                           .append("price", 33.09)
                           .append("quantity", 4),
                       new Document("name", "printer paper")
                           .append("tags", Arrays.asList("office", "stationary"))
                           .append("price", 37.55)
                           .append("quantity", 1),
                       new Document("name", "backpack")
                           .append("tags", Arrays.asList("school", "travel", "kids"))
                           .append("price", 83.28)
                           .append("quantity", 2),
                       new Document("name", "pens")
                           .append("tags", Arrays.asList("writing", "office", "school", "stationary"))
                           .append("price", 42.9)
                           .append("quantity", 4),
                       new Document("name", "envelopes")
                           .append("tags", Arrays.asList("stationary", "office", "general"))
                           .append("price", 16.68)
                           .append("quantity", 2)
                   ))
                   .append("storeLocation", "Seattle")
                   .append("customer", new Document()
                       .append("gender", "M")
                       .append("age", 50)
                       .append("email", "keecade@hem.uy")
                       .append("satisfaction", 5)
                   )
                   .append("couponUsed", false)
                   .append("purchaseMethod", "Phone");
               
               // Insert the documents
               collection.insertOne(purchaseOrder1);
               collection.insertOne(purchaseOrder2);
               
               System.out.println("Successfully inserted purchase order documents.");
               
               // Query the new collection
               MongoCursor<Document> cursor = collection.find()
                   .sort(Sorts.descending("saleDate"))
                   .iterator();
               
               System.out.println("\nQuery results:");
               try {
                   while (cursor.hasNext()) {
                       System.out.println(cursor.next().toJson());
                   }
               } finally {
                   cursor.close();
               }
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to populate the collection and display its documents.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateCollection.java
   java CreateCollection
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   Document{{_id=ObjectId(...), saleDate=Sun Jan 25 10:01:02 UTC 2018, items=[...], storeLocation=Seattle, customer=Document{{gender=M, age=50, email=keecade@hem.uy, satisfaction=5}}, couponUsed=false, purchaseMethod=Phone}}
   Document{{_id=ObjectId(...), saleDate=Tue Jan 23 21:06:49 UTC 2018, items=[...], storeLocation=Denver, customer=Document{{gender=M, age=42, email=cauho@witwuta.sv, satisfaction=4}}, couponUsed=true, purchaseMethod=Phone}}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `CreateUpdateView.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```java
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import com.mongodb.client.model.ReplaceOptions;
   import com.mongodb.client.AggregateIterable;
   import org.bson.Document;

   import java.text.SimpleDateFormat;
   import java.util.Arrays;
   import java.util.Calendar;
   import java.util.Date;
   import java.util.List;
   import java.util.concurrent.Executors;
   import java.util.concurrent.ScheduledExecutorService;
   import java.util.concurrent.TimeUnit;

   public class CreateUpdateView {

       private static void updateMonthlyPhoneTransactions(MongoClient client, MongoCollection<Document> collection) {
           // Create the aggregation pipeline
           List<Document> pipeline = Arrays.asList(
               new Document("$match", new Document("purchaseMethod", "Phone")),
               new Document("$unwind", new Document("path", "$items")),
               new Document("$group", new Document("_id", new Document("$dateToString",
                   new Document("format", "%Y-%m")
                       .append("date", "$saleDate")))
                   .append("sales_quantity", new Document("$sum", "$items.quantity"))
                   .append("sales_price", new Document("$sum", "$items.price"))
               ),
               new Document("$set", new Document("sales_price", new Document("$toDouble", "$sales_price"))),
               new Document("$merge", new Document("into", "monthlyPhoneTransactions")
                   .append("whenMatched", "replace"))
           );

           // Run the aggregation
           AggregateIterable<Document> results = collection.aggregate(pipeline);
           results.toCollection();
       }

       public static void main(String[] args) {
           // Connect to MongoDB
           MongoClient client = MongoClients.create("<connection-string>");
           MongoDatabase database = client.getDatabase("sample_supplies");
           MongoCollection<Document> sales = database.getCollection("sales");
           MongoCollection<Document> purchaseOrders = database.getCollection("purchaseOrders");

           // Update immediately on startup
           updateMonthlyPhoneTransactions(client, sales);
           updateMonthlyPhoneTransactions(client, purchaseOrders);
           System.out.println("Initial update completed. Materialized view is ready.");

           // Example of a simple scheduler that updates monthly
           final int dayOfMonth = 1; // Update on the 1st of each month
           final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

           // Create a scheduled executor for periodic updates
           ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

           // Schedule a task that checks if it's time to update
           Runnable updateTask = new Runnable() {
               @Override
               public void run() {
                   Calendar calendar = Calendar.getInstance();
                   Date now = calendar.getTime();

                   if (calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth &&
                       calendar.get(Calendar.HOUR_OF_DAY) == 0 &&
                       calendar.get(Calendar.MINUTE) == 0) {

                       // It's midnight on the 1st of the month - update the view
                       updateMonthlyPhoneTransactions(client, sales);
                       updateMonthlyPhoneTransactions(client, purchaseOrders);
                       System.out.println("Scheduled update completed at " + dateFormat.format(now));

                       // Skip the next hour's checks to avoid multiple updates
                       try {
                           Thread.sleep(3600000); // 1 hour in milliseconds
                       } catch (InterruptedException e) {
                           Thread.currentThread().interrupt();
                       }
                   }
               }
           };

           // Run the check every minute
           scheduler.scheduleAtFixedRate(updateTask, 0, 60, TimeUnit.SECONDS);

           // Keep the application running
           try {
               // Run forever until external termination
               Thread.currentThread().join();
           } catch (InterruptedException e) {
               scheduler.shutdown();
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create and update the view.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateUpdateView.java
   java CreateUpdateView
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.*;
   import org.bson.Document;

   public class CreateIndexExample {
       public static void main(String[] args) {
           // Replace the connection string with your MongoDB deployment's connection string
           String connectionString = "<connection-string>";
           
           try (MongoClient mongoClient = MongoClients.create(connectionString)) {
               MongoDatabase database = mongoClient.getDatabase("sample_analytics");
               MongoCollection<Document> collection = database.getCollection("accounts");
               String indexName = "lookup-with-search-tutorial";

               // Create the MongoDB Search index definition for the document field
               Document searchIdx = new Document(
                       "mappings",
                       new Document("dynamic", true)
               );
               collection.createSearchIndex(indexName, searchIdx);
               System.out.println("New index name: " + indexName);
           } catch (Exception e) {
               System.err.println("Error creating Atlas Search index: " + e.getMessage());
           }
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac CreateIndex.java
   java CreateIndex
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `MaterializedViewSearchQuery.java` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```java
   import com.mongodb.client.model.Aggregates;
   import java.util.Arrays;
   import com.mongodb.client.MongoClient;
   import com.mongodb.client.MongoClients;
   import com.mongodb.client.MongoCollection;
   import com.mongodb.client.MongoDatabase;
   import org.bson.Document;

   public class MaterializedViewSearchQuery {

     public static void main(String[] args) {
       // connect to your MongoDB deployment
       String uri = "<connection-string>";
           
       try (MongoClient mongoClient = MongoClients.create(uri)) {
         // set namespace
         MongoDatabase database = mongoClient.getDatabase("sample_supplies");
         MongoCollection<Document> collection = database.getCollection("monthlyPhoneTransactions");
               
         // define pipeline
         Document searchStage = new Document("$search", 
           new Document("index", "monthlySalesIndex")
             .append("range", 
               new Document("gt", 10000)
                 .append("path", Arrays.asList("sales_price"))
             )
         );
         
         Document countStage = new Document("$count", "months_w_over_10000");
         
         // run pipeline and print results
         collection.aggregate(Arrays.asList(searchStage, countStage))
           .forEach(doc -> System.out.println(doc.toJson()));
       }
     }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to execute the query.

   Compile and run your application in your IDE or your shell.

   ```shell
   javac MaterializedViewSearchQuery.java
   java MaterializedViewSearchQuery
   ```

   **Output:**

   ```text
   { "months_w_over_10000" : 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.kt` file and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `LookupWithSearchQuery.kt` in your project directory.

2. Copy and paste the query into the `LookupWithSearchQuery.kt` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```kotlin
   import com.mongodb.client.model.Aggregates.limit
   import com.mongodb.client.model.Aggregates.project
   import com.mongodb.client.model.Projections.*
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.util.*

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_analytics")
       val collection = database.getCollection<Document>("customers")

       runBlocking {
           // define pipeline
           val agg = Document(
               "\$lookup",
               Document("from", "accounts")
                   .append("localField", "accounts")
                   .append("foreignField", "account_id")
                   .append("as", "purchases")
                   .append(
                       "pipeline", Arrays.asList(
                           Document(
                               "\$search",
                               Document("index", "lookup-with-search-tutorial")
                                   .append(
                                       "compound",
                                       Document(
                                           "must", Arrays.asList(
                                               Document(
                                                   "queryString",
                                                   Document("defaultPath", "products")
                                                       .append("query", "products: (CurrencyService AND InvestmentStock)")
                                               )
                                           )
                                       )
                                           .append(
                                               "should", Arrays.asList(
                                                   Document(
                                                       "range",
                                                       Document("path", "limit")
                                                           .append("gte", 5000)
                                                           .append("lte", 10000)
                                                   )
                                               )
                                           )
                                   )
                           ),
                           Document("\$limit", 5),
                           Document(
                               "\$project",
                               Document("_id", 0)
                                   .append("address", 0)
                                   .append("birthdate", 0)
                                   .append("username", 0)
                                   .append("tier_and_details", 0)
                           )
                       )
                   )
           )

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(
               listOf(
                   agg,
                   limit(5),
                   project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
               )
           )
           resultsFlow.collect { println(it) }
       }
       mongoClient.close()
   }


   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the `LookupWithSearchQuery.kt` file.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc LookupWithSearchQuery.kt -include-runtime -d LookupWithSearchQuery.jar
   java -jar LookupWithSearchQuery.jar
   ```

   **Output:**

   ```text
   Document{{name=Lindsay Cowan, email=cooperalexis@hotmail.com, accounts=[116508], purchases=[]}}
   Document{{name=Dr. Angela Brown, email=michaelespinoza@gmail.com, accounts=[571880], purchases=[]}}
   Document{{name=Brian Flores, email=april04@gmail.com, accounts=[550665, 321695], purchases=[Document{{account_id=321695, limit=10000, products=[Derivatives, Commodity, CurrencyService, Brokerage, InvestmentStock]}}]}}
   Document{{name=Shirley Rodriguez, email=jonathan95@yahoo.com, accounts=[784245, 896066, 991412, 951840], purchases=[Document{{account_id=991412, limit=10000, products=[CurrencyService, Commodity, InvestmentFund, InvestmentStock]}}, Document{{account_id=951840, limit=10000, products=[InvestmentFund, Commodity, CurrencyService, InvestmentStock]}}, Document{{account_id=896066, limit=10000, products=[Derivatives, InvestmentFund, Brokerage, CurrencyService, InvestmentStock]}}]}}
   Document{{name=Clinton Shelton, email=acook@gmail.com, accounts=[602560, 986196, 51080, 690617, 225602], purchases=[]}}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the index.

   In your project's base package directory, create a `CreateIndex.kt` file and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `SearchWithUnionwithQuery.kt`.

2. Copy and paste the query into the `SearchWithUnionwithQuery.kt` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_training")
       val collection = database.getCollection<Document>("companies")

       runBlocking {
           // define pipeline
           val pipeline1 = listOf(
               Document("\$search", Document("text",
                   Document("query", "Mobile")
                       .append("path", "name"))), Document("\$project", Document("score",
                   Document("\$meta", "searchScore"))
                   .append("_id", 0)
                   .append("number_of_employees", 1)
                   .append("founded_year", 1)
                   .append("name", 1)), Document("\$set", Document("source", "companies")),
               Document("\$limit", 3)
           )

           val pipeline2 = listOf(
               Document(
                   "\$search", Document(
                       "text",
                       Document("query", "Mobile")
                           .append("path", "business_name")
                   )
               ),
               Document("\$set", Document("source", "inspections")),
               Document(
                   "\$project", Document(
                       "score",
                       Document("\$meta", "searchScore")
                   )
                       .append("source", 1)
                       .append("_id", 0)
                       .append("business_name", 1)
                       .append("address", 1)
               ),
               Document("\$limit", 3),
               Document("\$sort", Document("score", -1))
           )

           val unionWithStage: MutableList<Document> = ArrayList()
           val unionWith = Document(
               "\$unionWith", Document("coll", "inspections")
                   .append("pipeline", pipeline2)
           )
           unionWithStage.add(unionWith)
           val finalPipeline: MutableList<Document> = ArrayList(pipeline1)
           finalPipeline.addAll(unionWithStage)

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(finalPipeline)
           resultsFlow.collect { println(it) }

       }
       mongoClient.close()
   }

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the `SearchWithUnionwithQuery.kt` file.

   ### Basic Example

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc SearchWithUnionwithQuery.kt -include-runtime -d SearchWithUnionwithQuery.jar
   java -jar SearchWithUnionwithQuery.jar
   ```

   **Output:**

   ```text
   Document{{name=XLR8 Mobile, number_of_employees=21, founded_year=2006, score=2.0815043449401855, source=companies}}
   Document{{name=Pulse Mobile, number_of_employees=null, founded_year=null, score=2.0815043449401855, source=companies}}
   Document{{name=Mobile Trend, number_of_employees=null, founded_year=2003, score=2.0815043449401855, source=companies}}
   Document{{business_name=T-MOBILE, address=Document{{city=BROOKLYN, zip=11229, street=AVENUE U, number=1616}}, source=inspections, score=2.900916337966919}}
   Document{{business_name=BOOST MOBILE, address=Document{{city=BRONX, zip=10458, street=E FORDHAM RD, number=261}}, source=inspections, score=2.900916337966919}}
   Document{{business_name=SPRING MOBILE, address=Document{{city=SOUTH RICHMOND HILL, zip=11419, street=LIBERTY AVE, number=12207}}, source=inspections, score=2.900916337966919}}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up your Kotlin project with the MongoDB Kotlin Coroutine driver.

   In your IDE, create a new [Maven](https://maven.apache.org/) or [Gradle](https://gradle.org/) project. Add the Bill of Materials (BOM) for MongoDB JVM artifacts to your project to organize dependency versions. The BOM simplifies dependency management by ensuring that you maintain consistent and compatible versions of dependencies, such as between the Java driver and the core driver library. Use the BOM to avoid version conflicts and simplify upgrades.

   Select from the following **Maven** and **Gradle** tabs to view instructions for adding the BOM for each dependency manager:

   ### Maven

   Add the following code to the `dependencyManagement` list in your `pom.xml` file:

   ```xml
   <dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.mongodb</groupId>
               <artifactId>mongodb-driver-bom</artifactId>
               <version>5.5.1</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
       </dependencies>
   </dependencyManagement>
   ```

   To view a list of dependencies that the BOM manages, see the [mongodb-driver-bom dependency listing](https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-bom/5.51) on the Maven Repository website.

   After adding the BOM, select from the following **Maven** and **Gradle** tabs to view instructions for adding the MongoDB Kotlin Coroutine Driver as a dependency in your project:

   ### Maven

   Add the following code to the `dependencies` array in your Maven project's `pom.xml` file:

   ```xml
   <dependencies>
       <dependency>
           <groupId>org.mongodb</groupId>
           <artifactId>mongodb-driver-kotlin-coroutine</artifactId>
       </dependency>
   </dependencies>
   ```

   Run your package manager to install the dependencies to your project.

   For more detailed installation instructions and version compatibility, see the [MongoDB Kotlin Coroutine Driver documentation.](https://www.mongodb.com/docs/drivers/kotlin/coroutine/current/quick-start/)

2. Define the `purchaseOrders` collection documents.

   In your project's base package directory, create a `CreateCollection.kt` file and copy and paste the following code into this file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.flow.toList
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.text.SimpleDateFormat
   import java.util.*

   fun main() = runBlocking {
       // Connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)
       
       try {
           // Set namespace
           val database = mongoClient.getDatabase("sample_supplies")
           val collection = database.getCollection<Document>("purchaseOrders")
           
           // Parse dates
           val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
           dateFormat.timeZone = TimeZone.getTimeZone("UTC")
           val date1 = dateFormat.parse("2018-01-23T21:06:49.506Z")
           val date2 = dateFormat.parse("2018-01-25T10:01:02.918Z")
           
           // Create first document
           val purchaseOrder1 = Document("saleDate", date1)
               .append("items", listOf(
                   Document()
                       .append("name", "printer paper")
                       .append("tags", listOf("office", "stationary"))
                       .append("price", 40.01)
                       .append("quantity", 2),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 35.29)
                       .append("quantity", 2),
                   Document()
                       .append("name", "pens")
                       .append("tags", listOf("writing", "office", "school", "stationary"))
                       .append("price", 56.12)
                       .append("quantity", 5),
                   Document()
                       .append("name", "backpack")
                       .append("tags", listOf("school", "travel", "kids"))
                       .append("price", 77.71)
                       .append("quantity", 2),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 18.47)
                       .append("quantity", 2),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 19.95)
                       .append("quantity", 8),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 8.08)
                       .append("quantity", 3),
                   Document()
                       .append("name", "binder")
                       .append("tags", listOf("school", "general", "organization"))
                       .append("price", 14.16)
                       .append("quantity", 3)
               ))
               .append("storeLocation", "Denver")
               .append("customer", Document()
                   .append("gender", "M")
                   .append("age", 42)
                   .append("email", "cauho@witwuta.sv")
                   .append("satisfaction", 4)
               )
               .append("couponUsed", true)
               .append("purchaseMethod", "Phone")
           
           // Create second document
           val purchaseOrder2 = Document("saleDate", date2)
               .append("items", listOf(
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 8.05)
                       .append("quantity", 10),
                   Document()
                       .append("name", "binder")
                       .append("tags", listOf("school", "general", "organization"))
                       .append("price", 28.31)
                       .append("quantity", 9),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 20.95)
                       .append("quantity", 3),
                   Document()
                       .append("name", "laptop")
                       .append("tags", listOf("electronics", "school", "office"))
                       .append("price", 866.5)
                       .append("quantity", 4),
                   Document()
                       .append("name", "notepad")
                       .append("tags", listOf("office", "writing", "school"))
                       .append("price", 33.09)
                       .append("quantity", 4),
                   Document()
                       .append("name", "printer paper")
                       .append("tags", listOf("office", "stationary"))
                       .append("price", 37.55)
                       .append("quantity", 1),
                   Document()
                       .append("name", "backpack")
                       .append("tags", listOf("school", "travel", "kids"))
                       .append("price", 83.28)
                       .append("quantity", 2),
                   Document()
                       .append("name", "pens")
                       .append("tags", listOf("writing", "office", "school", "stationary"))
                       .append("price", 42.9)
                       .append("quantity", 4),
                   Document()
                       .append("name", "envelopes")
                       .append("tags", listOf("stationary", "office", "general"))
                       .append("price", 16.68)
                       .append("quantity", 2)
               ))
               .append("storeLocation", "Seattle")
               .append("customer", Document()
                   .append("gender", "M")
                   .append("age", 50)
                   .append("email", "keecade@hem.uy")
                   .append("satisfaction", 5)
               )
               .append("couponUsed", false)
               .append("purchaseMethod", "Phone")
           
           // Insert the documents
           collection.insertOne(purchaseOrder1)
           collection.insertOne(purchaseOrder2)
           
           println("Successfully inserted purchase order documents.")
           
           // Query the new collection
           val sort = Document("saleDate", -1)
           val results = collection.find().sort(sort).toList()
           
           println("\nQuery results:")
           results.forEach { println(it) }
       } finally {
           mongoClient.close()
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Compile and run the file to populate the collection and display its documents.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateCollection.kt -include-runtime -d CreateCollection.jar
   java -jar CreateCollection.jar
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   Document{{_id=ObjectId(...), saleDate=Thu Jan 25 10:01:02 UTC 2018, items=[...], storeLocation=Seattle, customer=Document{{gender=M, age=50, email=keecade@hem.uy, satisfaction=5}}, couponUsed=false, purchaseMethod=Phone}}
   Document{{_id=ObjectId(...), saleDate=Tue Jan 23 21:06:49 UTC 2018, items=[...], storeLocation=Denver, customer=Document{{gender=M, age=42, email=cauho@witwuta.sv, satisfaction=4}}, couponUsed=true, purchaseMethod=Phone}}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Define the materialized view and its updater.

   Create a `CreateUpdateView.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import com.mongodb.kotlin.client.coroutine.MongoCollection
   import kotlinx.coroutines.*
   import org.bson.Document
   import java.text.SimpleDateFormat
   import java.util.*
   import com.mongodb.client.model.ReplaceOptions

   class CreateUpdateView {

       companion object {
           private suspend fun updateMonthlyPhoneTransactions(client: MongoClient, collection: MongoCollection<Document>) {
               // Create the aggregation pipeline
               val pipeline = listOf(
                   Document("\$match", Document("purchaseMethod", "Phone")),
                   Document("\$unwind", Document("path", "\$items")),
                   Document("\$group", Document("_id", Document("\$dateToString",
                       Document("format", "%Y-%m")
                           .append("date", "\$saleDate")))
                       .append("sales_quantity", Document("\$sum", "\$items.quantity"))
                       .append("sales_price", Document("\$sum", "\$items.price"))
                   ),
                   Document("\$set", Document("sales_price", Document("\$toDouble", "\$sales_price"))),
                   Document("\$merge", Document("into", "monthlyPhoneTransactions")
                       .append("whenMatched", "replace"))
               )

               // Run the aggregation
               val results = collection.aggregate<Document>(pipeline)
               results.toCollection()
           }

           @JvmStatic
           fun main(args: Array<String>) = runBlocking {
               // Connect to MongoDB
               val client = MongoClient.create("<connection-string>")
               val database = client.getDatabase("sample_supplies")
               val sales = database.getCollection<Document>("sales")
               val purchaseOrders = database.getCollection<Document>("purchaseOrders")

               // Update immediately on startup
               updateMonthlyPhoneTransactions(client, sales)
               updateMonthlyPhoneTransactions(client, purchaseOrders)
               println("Initial update completed. Materialized view is ready.")

               // Example of a simple scheduler that updates monthly
               val dayOfMonth = 1 // Update on the 1st of each month
               val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

               // Create a coroutine scope for our periodic updates
               val scope = CoroutineScope(Dispatchers.Default)

               // Launch a coroutine for periodic updates
               scope.launch {
                   while (isActive) {
                       val calendar = Calendar.getInstance()
                       val now = calendar.time

                       if (calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth &&
                           calendar.get(Calendar.HOUR_OF_DAY) == 0 &&
                           calendar.get(Calendar.MINUTE) == 0) {

                           // It's midnight on the 1st of the month - update the view
                           updateMonthlyPhoneTransactions(client, sales)
                           updateMonthlyPhoneTransactions(client, purchaseOrders)
                           println("Scheduled update completed at ${dateFormat.format(now)}")

                           // Delay for an hour to avoid multiple updates
                           delay(3600000) // 1 hour in milliseconds
                       } else {
                           // Check again in a minute
                           delay(60000) // 60000ms = 1 minute
                       }
                   }
               }

               // Keep the application running
               while(true) {
                   delay(1000)
               }
           }
       }
   }

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create and update the view.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateUpdateView.kt -include-runtime -d CreateUpdateView.jar
   java -jar CreateUpdateView.jar
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `CreateIndex.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document

   fun main() = runBlocking {
       // Replace the connection string with your MongoDB deployment's connection string
       val uri = "<connection-string>"
       
       val mongoClient = MongoClient.create(uri)
       try {
           // set namespace
           val database = mongoClient.getDatabase("sample_analytics")
           val collection = database.getCollection<Document>("accounts")
           val indexName = "lookup-with-search-tutorial"
           
           val index = Document("mappings", Document("dynamic", true))
           
           val result = collection.createSearchIndex(indexName, index)
           println("New index name: $result")
       } finally {
           mongoClient.close()
       }
   }
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to create the index.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc CreateIndex.kt -include-runtime -d CreateIndex.jar
   java -jar CreateIndex.jar
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.kt` file in your `search-materialized-view` project directory, and copy and paste the following code into this file.

   ```kotlin
   import com.mongodb.client.model.Aggregates.limit
   import com.mongodb.client.model.Aggregates.project
   import com.mongodb.client.model.Projections.*
   import com.mongodb.kotlin.client.coroutine.MongoClient
   import kotlinx.coroutines.runBlocking
   import org.bson.Document
   import java.util.*

   fun main() {
       // connect to your MongoDB deployment
       val uri = "<connection-string>"
       val mongoClient = MongoClient.create(uri)

       // set namespace
       val database = mongoClient.getDatabase("sample_analytics")
       val collection = database.getCollection<Document>("customers")

       runBlocking {
           // define pipeline
           val agg = Document(
               "\$lookup",
               Document("from", "accounts")
                   .append("localField", "accounts")
                   .append("foreignField", "account_id")
                   .append("as", "purchases")
                   .append(
                       "pipeline", Arrays.asList(
                           Document(
                               "\$search",
                               Document("index", "lookup-with-search-tutorial")
                                   .append(
                                       "compound",
                                       Document(
                                           "must", Arrays.asList(
                                               Document(
                                                   "queryString",
                                                   Document("defaultPath", "products")
                                                       .append("query", "products: (CurrencyService AND InvestmentStock)")
                                               )
                                           )
                                       )
                                           .append(
                                               "should", Arrays.asList(
                                                   Document(
                                                       "range",
                                                       Document("path", "limit")
                                                           .append("gte", 5000)
                                                           .append("lte", 10000)
                                                   )
                                               )
                                           )
                                   )
                           ),
                           Document("\$limit", 5),
                           Document(
                               "\$project",
                               Document("_id", 0)
                                   .append("address", 0)
                                   .append("birthdate", 0)
                                   .append("username", 0)
                                   .append("tier_and_details", 0)
                           )
                       )
                   )
           )

           // run pipeline and print results
           val resultsFlow = collection.aggregate<Document>(
               listOf(
                   agg,
                   limit(5),
                   project(fields(excludeId(), include("name", "email", "active", "accounts", "purchases")))
               )
           )
           resultsFlow.collect { println(it) }
       }
       mongoClient.close()
   }


   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Compile and run the file to execute the query.

   Compile and run your application in your IDE or your shell.

   ```shell
   kotlinc query.kt -include-runtime -d query.jar
   java -jar query.jar
   ```

   **Output:**

   ```text
   Document{{months_w_over_10000=4}}
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Initialize your Node.js project.

   ```shell
   # Create a new directory and initialize the project
   mkdir atlas-search-project && cd atlas-search-project
   npm init -y

   # Add the MongoDB Node.js Driver to your project
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the index.

   Create a `create-index.js` file in your project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.js` in your project directory.

2. Copy and paste the sample query into the `lookup-with-search-query.js` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```go
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$lookup': {
           'from': 'accounts', 
           'localField': 'accounts', 
           'foreignField': 'account_id', 
           'as': 'purchases', 
           'pipeline': [
             {
               '$search': {
                 'index': 'lookup-with-search-tutorial', 
                 'compound': {
                   'must': [
                     {
                       'queryString': {
                         'defaultPath': 'products', 
                         'query': 'products: (CurrencyService AND InvestmentStock)'
                       }
                     }
                   ], 
                   'should': [
                     {
                       'range': {
                         'path': 'limit', 
                         'gte': 5000, 
                         'lte': 10000
                       }
                     }
                   ]
                 }
               }
             }, {
               '$project': {
                 '_id': 0
               }
             }
           ]
         }
       }, {
         '$limit': 5
       }, {
         '$project': {
           '_id': 0, 
           'address': 0, 
           'birthdate': 0, 
           'username': 0, 
           'tier_and_details': 0
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_analytics").collection("customers");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Query your collection.

   Run the following command to query your collection:

   ```bash
   node lookup-with-search-query.js
   ```

   **Output:**

   ```js
   {
     name: 'Lindsay Cowan',
     email: 'cooperalexis@hotmail.com',
     accounts: [ 116508 ],
     purchases: []
   }
   {
     name: 'Dr. Angela Brown',
     email: 'michaelespinoza@gmail.com',
     accounts: [ 571880 ],
     purchases: []
   }
   {
     name: 'Brian Flores',
     email: 'april04@gmail.com',
     accounts: [ 550665, 321695 ],
     purchases: [
       { account_id: 321695, limit: 10000, products: [Array] }
     ]
   }
   {
     name: 'Shirley Rodriguez',
     email: 'jonathan95@yahoo.com',
     accounts: [ 784245, 896066, 991412, 951840 ],
     purchases: [
       { account_id: 991412, limit: 10000, products: [Array] },
       { account_id: 951840, limit: 10000, products: [Array] },
       { account_id: 896066, limit: 10000, products: [Array] }
     ]
   }
   {
     name: 'Clinton Shelton',
     email: 'acook@gmail.com',
     accounts: [ 602560, 986196, 51080, 690617, 225602 ],
     purchases: []
   }
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Initialize your Node.js project.

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   npm init -y
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the index.

   Create a `create-index.js` file in your project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.js`.

2. Copy and paste the sample query into the `search-with-unionwith-query.js` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$search': {
           'text': { 'query': 'Mobile', 'path': 'name'  }
         }
       }, {
         '$project': {
           'score': { '$meta': 'searchScore' }, 
           '_id': 0, 'number_of_employees': 1, 'founded_year': 1, 'name': 1
         }
       }, {
         '$set': { 'source': 'companies' }
       }, {
         '$limit': 3
       }, {
         '$unionWith': {
           'coll': 'inspections', 
           'pipeline': [
             {
               '$search': {
                 'text': { 'query': 'Mobile', 'path': 'business_name' }
               }
             }, {
               '$set': { 'source': 'inspections' }
             }, {
               '$project': {
                 'score': { '$meta': 'searchScore' }, 
                 'source': 1, '_id': 0, 'business_name': 1, 'address': 1
               }
             }, {
               '$limit': 3
             }, {
               '$sort': { 'score': -1 }
             }
           ]
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_training").collection("companies");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Query your collection.

   Run the following command to query your collection:

   ### Basic Example

   ```bash
   node unionwith-with-search-query.js
   ```

   **Output:**

   ```js
   [
     {
       "name": "XLR8 Mobile",
       "number_of_employees": 21,
       "founded_year": 2006,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "name": "Pulse Mobile",
       "number_of_employees": null,
       "founded_year": null,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "name": "T-Mobile",
       "number_of_employees": null,
       "founded_year": null,
       "score": 2.0815043449401855,
       "source": "companies"
     },
     {
       "business_name": "T. MOBILE",
       "address": {
         "city": "BROOKLYN",
         "zip": 11209,
         "street": "86TH ST",
         "number": 440
       },
       "source": "inspections",
       "score": 2.900916337966919
     },
     {
       "business_name": "BOOST MOBILE",
       "address": {
         "city": "BRONX",
         "zip": 10458,
         "street": "E FORDHAM RD",
         "number": 261
       },
       "source": "inspections",
       "score": 2.900916337966919
     },
     {
       "business_name": "SPRING MOBILE",
       "address": {
         "city": "SOUTH RICHMOND HILL",
         "zip": 11419,
         "street": "LIBERTY AVE",
         "number": 12207
       },
       "source": "inspections",
       "score": 2.900916337966919
     }
   ]
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Initialize your Node.js project.

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   npm init -y
   npm install mongodb
   ```

   For detailed installation instructions, see the [MongoDB Node Driver documentation.](https://www.mongodb.com/docs/drivers/node/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create-collection.js` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```javascript
   const { MongoClient } = require("mongodb");
   const assert = require("assert");

   async function run() {
     // Connect to your MongoDB deployment
     const client = new MongoClient("<connection-string>");

     try {
       await client.connect();
       
       // Set namespace
       const database = client.db("sample_supplies");
       const collection = database.collection("purchaseOrders");
       
       // Create the documents
       const purchaseOrder1 = {
         saleDate: new Date("2018-01-23T21:06:49.506Z"),
         items: [
           {
             name: 'printer paper',
             tags: ['office', 'stationary'],
             price: 40.01,
             quantity: 2
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 35.29,
             quantity: 2
           },
           {
             name: 'pens',
             tags: ['writing', 'office', 'school', 'stationary'],
             price: 56.12,
             quantity: 5
           },
           {
             name: 'backpack',
             tags: ['school', 'travel', 'kids'],
             price: 77.71,
             quantity: 2
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 18.47,
             quantity: 2
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 19.95,
             quantity: 8
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 8.08,
             quantity: 3
           },
           {
             name: 'binder',
             tags: ['school', 'general', 'organization'],
             price: 14.16,
             quantity: 3
           }
         ],
         storeLocation: "Denver",
         customer: {
           gender: "M",
           age: 42,
           email: "cauho@witwuta.sv",
           satisfaction: 4
         },
         couponUsed: true,
         purchaseMethod: "Phone"
       };
       
       const purchaseOrder2 = {
         saleDate: new Date("2018-01-25T10:01:02.918Z"),
         items: [
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 8.05,
             quantity: 10
           },
           {
             name: 'binder',
             tags: ['school', 'general', 'organization'],
             price: 28.31,
             quantity: 9
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 20.95,
             quantity: 3
           },
           {
             name: 'laptop',
             tags: ['electronics', 'school', 'office'],
             price: 866.5,
             quantity: 4
           },
           {
             name: 'notepad',
             tags: ['office', 'writing', 'school'],
             price: 33.09,
             quantity: 4
           },
           {
             name: 'printer paper',
             tags: ['office', 'stationary'],
             price: 37.55,
             quantity: 1
           },
           {
             name: 'backpack',
             tags: ['school', 'travel', 'kids'],
             price: 83.28,
             quantity: 2
           },
           {
             name: 'pens',
             tags: ['writing', 'office', 'school', 'stationary'],
             price: 42.9,
             quantity: 4
           },
           {
             name: 'envelopes',
             tags: ['stationary', 'office', 'general'],
             price: 16.68,
             quantity: 2
           }
         ],
         storeLocation: "Seattle",
         customer: {
           gender: "M",
           age: 50,
           email: "keecade@hem.uy",
           satisfaction: 5
         },
         couponUsed: false,
         purchaseMethod: "Phone"
       };
       
       // Insert the documents
       await collection.insertMany([purchaseOrder1, purchaseOrder2]);
       
       console.log("Successfully inserted purchase order documents.");
       
       // Query the new collection
       const cursor = collection.find().sort({ saleDate: -1 });
       
       console.log("\nQuery results:");
       await cursor.forEach(doc => console.log(doc));
       
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   node create-collection.js
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   {
     _id: new ObjectId("..."),
     saleDate: 2018-01-25T10:01:02.918Z,
     items: [
       { quantity: 10 },
       { quantity: 9 },
       { quantity: 3 },
       { quantity: 4 },
       { quantity: 4 },
       { quantity: 1 },
       { quantity: 2 },
       { quantity: 4 }
     ],
     storeLocation: 'Seattle',
     customer: {
       gender: 'M',
       age: 50,
       email: 'keecade@hem.uy',
       satisfaction: 5
     },
     couponUsed: false,
     purchaseMethod: 'Phone'
   }
   {
     _id: new ObjectId("..."),
     saleDate: 2018-01-23T21:06:49.506Z,
     items: [
       { quantity: 2 },
       { quantity: 2 },
       { quantity: 5 },
       { quantity: 2 },
       { quantity: 2 },
       { quantity: 8 },
       { quantity: 3 }
     ],
     storeLocation: 'Denver',
     customer: {
       gender: 'M',
       age: 42,
       email: 'cauho@witwuta.sv',
       satisfaction: 4
     },
     couponUsed: true,
     purchaseMethod: 'Phone'
   }
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Install the required package.

   Before running the application, install the `node-schedule` package in your `search-materialized-view` project directory. This package is used to schedule the periodic updates of the materialized view.

   ```shell
   npm install node-schedule
   ```

2. Define the materialized view and its updater.

   Create a `create-update-view.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```javascript
   const { MongoClient } = require('mongodb');
   const schedule = require('node-schedule');

   // Function to update the materialized view
   async function updateMonthlyPhoneTransactions(client, collection) {
       // Create the aggregation pipeline
       const pipeline = [
           { $match: { purchaseMethod: 'Phone' } },
           { $unwind: { path: '$items' } },
           { 
               $group: {
                   _id: { 
                       $dateToString: {
                           format: '%Y-%m',
                           date: '$saleDate'
                       }
                   },
                   sales_quantity: { $sum: '$items.quantity' },
                   sales_price: { $sum: '$items.price' }
               }
           },
           { $set: { sales_price: { $toDouble: '$sales_price' } } },
           { 
               $merge: {
                   into: 'monthlyPhoneTransactions',
                   whenMatched: 'replace'
               }
           }
       ];
       
       // Run the aggregation
       const cursor = collection.aggregate(pipeline);
       
       for await (const doc of cursor) {
           // Consume the cursor to execute the pipeline
       }
   }

   async function main() {
       // Connect to MongoDB
       const uri = '<connection-string>';
       const client = new MongoClient(uri);
       
       try {
           await client.connect();
           const database = client.db('sample_supplies');
           const sales = database.collection('sales');
           const purchaseOrders = database.collection('purchaseOrders');
           
           // Update immediately on startup
           await updateMonthlyPhoneTransactions(client, sales);
           await updateMonthlyPhoneTransactions(client, purchaseOrders);
           console.log('Initial update completed. Materialized view is ready.');
           
           // Example of a simple scheduler that updates monthly
           // This schedules the job to run at midnight on the 1st of each month
           const job = schedule.scheduleJob('0 0 1 * *', async function() {
               try {
                   await updateMonthlyPhoneTransactions(client, sales);
                   await updateMonthlyPhoneTransactions(client, purchaseOrders);
                   console.log(`Scheduled update completed at ${new Date().toISOString()}`);
               } catch (err) {
                   console.error('Error during scheduled update:', err);
               }
           });
           
           console.log('Scheduler is running. Press Ctrl+C to exit.');
           
           // Keep the process running
           process.stdin.resume();
           
       } catch (err) {
           console.error('Error:', err);
       } finally {
           await client.close();
       }
   }

   main().catch(console.error);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create and update the view.

   ```shell
   node create-update-view.js
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   Scheduler is running. Press Ctrl+C to exit.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create-index.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   // Replace the connection string with your MongoDB deployment's connection string
   const uri =  "<connection-string>";

   const client = new MongoClient(uri);

   async function run() {
     try {
       const database = client.db("sample_analytics");
       const collection = database.collection("accounts");

       // Create the MongoDB Search index definition
       const index = {
           name: "lookup-with-search-tutorial",
           definition: {
               "mappings": {
                   "dynamic": true
               }
           }
       }

       // Create the index
       const result = await collection.createSearchIndex(index);
       console.log("New index name: " + result);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   node create-index.js
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.js` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```javascript
   const { MongoClient } = require("mongodb");

   const agg = [
       {
         '$lookup': {
           'from': 'accounts', 
           'localField': 'accounts', 
           'foreignField': 'account_id', 
           'as': 'purchases', 
           'pipeline': [
             {
               '$search': {
                 'index': 'lookup-with-search-tutorial', 
                 'compound': {
                   'must': [
                     {
                       'queryString': {
                         'defaultPath': 'products', 
                         'query': 'products: (CurrencyService AND InvestmentStock)'
                       }
                     }
                   ], 
                   'should': [
                     {
                       'range': {
                         'path': 'limit', 
                         'gte': 5000, 
                         'lte': 10000
                       }
                     }
                   ]
                 }
               }
             }, {
               '$project': {
                 '_id': 0
               }
             }
           ]
         }
       }, {
         '$limit': 5
       }, {
         '$project': {
           '_id': 0, 
           'address': 0, 
           'birthdate': 0, 
           'username': 0, 
           'tier_and_details': 0
         }
       }
     ];

   async function run() {
     const client = new MongoClient("<connection-string>");
     
     try {
       await client.connect();
       const coll = client.db("sample_analytics").collection("customers");
       const cursor = await coll.aggregate(agg);
       const results = await cursor.toArray();
       console.log(JSON.stringify(results, null, 2));
     } catch (err) {
       console.error("Error:", err);
     } finally {
       await client.close();
     }
   }

   run().catch(console.dir);

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   node query.js
   ```

   **Output:**

   ```text
   { months_w_over_10000: 4 }
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

This page contains tutorials that demonstrate the different ways you can run MongoDB Search queries across multiple collections:

- To join collections and search across them, use [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To combine search results from multiple collections, use [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`.](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search)

- To consolidate multiple collections so you can index and search them together, use materialized views.

Before you begin, ensure that your cluster meets the requirements described in the [Prerequisites.](https://www.mongodb.com/docs/search/about/design-patterns.md#std-label-fts-design-patterns-prereqs)

## Search Joined Collections

Starting in v6.0, the MongoDB [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) `pipeline` option. Using [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup), you can join multiple collections in the same database at query-time and run a [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) query to further narrow down your search.

This tutorial demonstrates how to run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `accounts` and `customers` collections in the `sample_analytics` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `accounts` collection in the `sample_analytics` database.

2. Run a [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to find customers from the `customers` collection whose accounts have purchased both `CurrencyService` and `InvestmentStock` products in the `accounts` collection.

### Considerations

To run [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or later. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

[`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) queries are less performant because MongoDB Search does a full document lookup on the database for each document in the collection.

To learn how to improve the performance of `$lookup` queries, see [embeddedDocuments.](https://www.mongodb.com/docs/search/indexes/field-types/embedded-documents-type.md#std-label-embedded-documents-arrays-anti-pattern)

### Create the MongoDB Search Index

In this section, you can learn how to create a MongoDB Search index named `lookup-with-search-tutorial` on all the fields in the `sample_analytics.accounts` collection.

1. Set up the Python project.

   ```shell
   # Create a new directory for the project
   mkdir atlas-search-project && cd atlas-search-project

   # Install PyMongo
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the index.

   Create a `create_index.py` file in your project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name: lookup-with-search-tutorial
   ```

### Run `$lookup` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_analytics` database.

1. Create a file named `lookup-with-search-query.py` in your project directory.

2. Copy and paste the query into the `lookup-with-search-query.py` file.

   The following query uses the following stages:

   - [`$lookup`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup.md#mongodb-pipeline-pipe.-lookup) to do the following:

     - Join `customers` and `accounts` collections in the `sample_analytics` database based on the account ID of the customers and return the matching documents from the `accounts` collection in an array field named `purchases`.

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for customer accounts that `must` have purchased both `CurrencyService` and `InvestmentStock` with preference for an order limit between `5000` to `10000`.

   - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `5` results.

   - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to exclude the specified fields in the results.

   ```python
   import pymongo

   # connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # define pipeline
   pipeline = [
     {
       '$lookup': {
         'from': 'accounts', 
         'localField': 'accounts', 
         'foreignField': 'account_id', 
         'as': 'purchases', 
         'pipeline': [
           {
             '$search': {
               'index': 'lookup-with-search-tutorial', 
               'compound': {
                 'must': [
                   {
                     'queryString': {
                       'defaultPath': 'products', 
                       'query': 'products: (CurrencyService AND InvestmentStock)'
                     }
                   }
                 ], 
                 'should': [
                   {
                     'range': {
                       'path': 'limit', 
                       'gte': 5000, 
                       'lte': 10000
                     }
                   }
                 ]
               }
             }
           }, 
           { '$project': { '_id': 0 }  }
         ]
       }
     }, 
     { '$limit': 5 }, 
     {
       '$project': {
         '_id': 0, 
         'address': 0, 
         'birthdate': 0, 
         'username': 0, 
         'tier_and_details': 0
       }
     }
   ]

   # run pipeline
   result = client['sample_analytics']['customers'].aggregate(pipeline)

   # print results
   for i in result:
       print(i)

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ```shell
   python lookup-with-search-query.py
   ```

   **Output:**

   ```json
   {'name': 'Lindsay Cowan', 'email': 'cooperalexis@hotmail.com', 'accounts': [116508], 'purchases': []}
   {'name': 'Dr. Angela Brown', 'email': 'michaelespinoza@gmail.com', 'accounts': [571880], 'purchases': []}
   {'name': 'Brian Flores', 'email': 'april04@gmail.com', 'accounts': [550665, 321695], 'purchases': [{'account_id': 321695, 'limit': 10000, 'products': ['Derivatives', 'Commodity', 'CurrencyService', 'Brokerage', 'InvestmentStock']}]}
   {'name': 'Shirley Rodriguez', 'email': 'jonathan95@yahoo.com', 'accounts': [784245, 896066, 991412, 951840], 'purchases': [{'account_id': 991412, 'limit': 10000, 'products': ['CurrencyService', 'Commodity', 'InvestmentFund', 'InvestmentStock']}, {'account_id': 951840, 'limit': 10000, 'products': ['InvestmentFund', 'Commodity', 'CurrencyService', 'InvestmentStock']}, {'account_id': 896066, 'limit': 10000, 'products': ['Derivatives', 'InvestmentFund', 'Brokerage', 'CurrencyService', 'InvestmentStock']}]}
   {'name': 'Clinton Shelton', 'email': 'acook@gmail.com', 'accounts': [602560, 986196, 51080, 690617, 225602], 'purchases': []}
   ```

## Combine Query Results

Starting in v6.0, the MongoDB [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) aggregation stage supports [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) inside the [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) `pipeline` option. Using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith), you can combine [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) results from multiple collections in the same database in the result set.

**Warning:**

Queries using [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) in the sub-pipeline might fail during an upgrade from a replica set to a sharded cluster running MongoDB v7.0 if the collection is not spread across multiple shards. To mitigate this, deploy more than one shard on your cluster and shard the collection such that multiple chunks are spread across multiple shards.

This tutorial demonstrates how to run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) against the `companies` and `inspections` collections in the `sample_training` database. It takes you through the following steps:

1. Create a default MongoDB Search index on the `companies` and `inspections` collections in the `sample_training` database.

2. Run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to perform a union of companies with `mobile` in their name from both the `companies` and `inspections` collections.

**Note:**

To run a [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) query with [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search), your cluster must run MongoDB v6.0 or higher. To upgrade your MongoDB version, see [Upgrade Major MongoDB Version for a Cluster.](https://www.mongodb.com/docs/atlas/tutorial/major-version-change.md#std-label-upgrade-major-MongoDB-version)

### Create the MongoDB Search Indexes

In this section, you can learn how to create a MongoDB Search index named `default` on all the fields in the `companies` collection in the `sample_training` database. You will create another MongoDB Search index named `default` on all the fields in the `inspections` collection in the `sample_training` database.

1. Set up the Python project.

   ```shell
   mkdir search-with-unionwith
   cd search-with-unionwith
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the index.

   Create a `create_index.py` file in your project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name for companies: default
   New index name for inspections: default
   ```

### Run `$unionWith` with `$search` to Search the Collections

In this section, you can learn how to connect to your deployment and run the sample query against the indexed collections in the `sample_training` database.

1. Create a file named `search-with-unionwith-query.py`.

2. Copy and paste the query into the `search-with-unionwith-query.py` file.

   The following query searches both the `companies` and `inspections` collections for the term `mobile`. The query searches the `name` field in `companies` and the `business_name` field in `inspections`.

   ### Basic Example

   This query uses the following stages:

   - [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) to search for companies that include `mobile` in the name.

   - [`$unionWith`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith.md#mongodb-pipeline-pipe.-unionWith) to do the following:

     - Use [`$search`](https://www.mongodb.com/docs/search/query/aggregation-stages/search.md#mongodb-pipeline-pipe.-search) stage in the sub-pipeline to search for inspections of companies that include `mobile` in the name.

     - Perform a union of documents from the `companies` and documents from the `inspections` collections.

   - [`$set`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set.md#mongodb-pipeline-pipe.-set) stage to add a new field named `source` that identifies the collection of the output documents.

     - [`$limit`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit.md#mongodb-pipeline-pipe.-limit) stage to limit the output to `3` results from each collection.

     - [`$project`](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project.md#mongodb-pipeline-pipe.-project) stage to:

       - Include only the specified fields in the results.

       - Add a field named `score`.

   ```go
   import pymongo
   import dns

   client = pymongo.MongoClient('<connection-string>')
   result = client['sample_training']['companies'].aggregate([
     {
       '$search': {
         'text': { 'query': 'Mobile', 'path': 'name' }
       }
     }, {
       '$project': {
         'score': { '$meta': 'searchScore' }, '_id': 0, 'number_of_employees': 1, 'founded_year': 1, 'name': 1
       }
     }, {
       '$set': { 'source': 'companies' }
     }, {
       '$limit': 3
     }, {
       '$unionWith': {
         'coll': 'inspections', 
         'pipeline': [
           {
             '$search': {
               'text': { 'query': 'Mobile', 'path': 'business_name' }
             }
           }, {
             '$set': { 'source': 'inspections' }
           }, {
             '$project': {
               'score': { '$meta': 'searchScore' }, 'source': 1, '_id': 0, 'business_name': 1, 'address': 1
             }
           }, {
             '$limit': 3
           }, {
             '$sort': { 'score': -1 }
           }
         ]
       }
     }
   ])

   for i in result:
       print(i)

   ```

3. Replace the `<connection-string>` in the query and then save the file.

   Ensure that your connection string includes your database user's credentials. To learn more, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

4. Run the command to query your collection.

   ### Basic Example

   ```shell
   python search-with-unionwith-query.py
   ```

   **Output:**

   ```json
   {'name': 'XLR8 Mobile', 'number_of_employees': 21, 'founded_year': 2006, 'score': 2.0815043449401855, 'source': 'companies'}
   {'name': 'Pulse Mobile', 'number_of_employees': None, 'founded_year': None, 'score': 2.0815043449401855, 'source': 'companies'}
   {'name': 'T-Mobile', 'number_of_employees': None, 'founded_year': None, 'score': 2.0815043449401855, 'source': 'companies'}
   {'business_name': 'T. MOBILE', 'address': {'city': 'BROOKLYN', 'zip': 11209, 'street': '86TH ST', 'number': 440}, 'source': 'inspections', 'score': 2.900916337966919}
   {'business_name': 'BOOST MOBILE', 'address': {'city': 'BRONX', 'zip': 10458, 'street': 'E FORDHAM RD', 'number': 261}, 'source': 'inspections', 'score': 2.900916337966919}
   {'business_name': 'SPRING MOBILE', 'address': {'city': 'SOUTH RICHMOND HILL', 'zip': 11419, 'street': 'LIBERTY AVE', 'number': 12207}, 'source': 'inspections', 'score': 2.900916337966919}
   ```

## Run MongoDB Search Queries Using Materialized Views

This tutorial describes how to create an index and run queries against the  `sample_supplies.sales` collection from the [sample dataset](https://www.mongodb.com/docs/atlas/import/load-sample-data.md#std-label-sample-data) and a new `sample_supplies.purchaseOrders`.

An on-demand materialized view is a collection that you create and update using a `$merge` aggregation pipeline stage. You can create an MongoDB Search index on the materialized view and then run queries on the materialized view using the `$search` aggregation pipeline stage.

This tutorial takes you through the following steps:

1. Create a collection named `purchaseOrders` in the `sample_supplies` database.

2. Create a materialized view named `monthlyPhoneTransactions` that retrieves data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.

3. Create a MongoDB Search index on the `monthlyPhoneTransactions` materialized view.

4. Run a query on the `monthlyPhoneTransactions` materialized view.

### Create the `purchaseOrders` Collection

1. Set up the Python project.

   ```shell
   mkdir search-materialized-view
   cd search-materialized-view
   pip install pymongo
   ```

   For detailed installation instructions, see the [MongoDB PyMongo Driver documentation.](https://www.mongodb.com/docs/drivers/python/pymongo-driver/current/get-started/)

2. Define the `purchaseOrders` collection documents.

   Create a `create_collection.py` file in your project directory, and copy and paste the following code into the file. This code performs the following actions:

   - Inserts documents into a new `purchaseOrders` collection in the `sample_supplies` database.

   - Runs a simple query to display the documents in the `purchaseOrders` collection, sorted by the `saleDate` field in descending order.

   ```python
   import pymongo
   from datetime import datetime

   # Connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # Set namespace
   database = client['sample_supplies']
   collection = database['purchaseOrders']

   # Create first document
   purchase_order1 = {
       'saleDate': datetime(2018, 1, 23, 21, 6, 49, 506000),
       'items': [
           {
               'name': 'printer paper',
               'tags': ['office', 'stationary'],
               'price': 40.01,
               'quantity': 2
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 35.29,
               'quantity': 2
           },
           {
               'name': 'pens',
               'tags': ['writing', 'office', 'school', 'stationary'],
               'price': 56.12,
               'quantity': 5
           },
           {
               'name': 'backpack',
               'tags': ['school', 'travel', 'kids'],
               'price': 77.71,
               'quantity': 2
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 18.47,
               'quantity': 2
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 19.95,
               'quantity': 8
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 8.08,
               'quantity': 3
           },
           {
               'name': 'binder',
               'tags': ['school', 'general', 'organization'],
               'price': 14.16,
               'quantity': 3
           }
       ],
       'storeLocation': 'Denver',
       'customer': {
           'gender': 'M',
           'age': 42,
           'email': 'cauho@witwuta.sv',
           'satisfaction': 4
       },
       'couponUsed': True,
       'purchaseMethod': 'Phone'
   }

   # Create second document
   purchase_order2 = {
       'saleDate': datetime(2018, 1, 25, 10, 1, 2, 918000),
       'items': [
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 8.05,
               'quantity': 10
           },
           {
               'name': 'binder',
               'tags': ['school', 'general', 'organization'],
               'price': 28.31,
               'quantity': 9
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 20.95,
               'quantity': 3
           },
           {
               'name': 'laptop',
               'tags': ['electronics', 'school', 'office'],
               'price': 866.5,
               'quantity': 4
           },
           {
               'name': 'notepad',
               'tags': ['office', 'writing', 'school'],
               'price': 33.09,
               'quantity': 4
           },
           {
               'name': 'printer paper',
               'tags': ['office', 'stationary'],
               'price': 37.55,
               'quantity': 1
           },
           {
               'name': 'backpack',
               'tags': ['school', 'travel', 'kids'],
               'price': 83.28,
               'quantity': 2
           },
           {
               'name': 'pens',
               'tags': ['writing', 'office', 'school', 'stationary'],
               'price': 42.9,
               'quantity': 4
           },
           {
               'name': 'envelopes',
               'tags': ['stationary', 'office', 'general'],
               'price': 16.68,
               'quantity': 2
           }
       ],
       'storeLocation': 'Seattle',
       'customer': {
           'gender': 'M',
           'age': 50,
           'email': 'keecade@hem.uy',
           'satisfaction': 5
       },
       'couponUsed': False,
       'purchaseMethod': 'Phone'
   }

   # Insert the documents
   collection.insert_many([purchase_order1, purchase_order2])

   print("Successfully inserted purchase order documents.")

   # Query the new collection
   results = collection.find().sort('saleDate', pymongo.DESCENDING)

   print("\nQuery results:")
   for doc in results:
       print(doc)

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Populate the `purchaseOrders` collection and display its documents.

   ```shell
   python create_collection.py
   ```

   **Output:**

   ```text
   Successfully inserted purchase order documents.

   Query results:
   {'_id': ObjectId('...'), 'saleDate': datetime.datetime(2018, 1, 25, 10, 1, 2, 918000), 'items': [{'quantity': 10}, {'quantity': 9}, {'quantity': 3}, {'quantity': 4}, {'quantity': 4}, {'quantity': 1}, {'quantity': 2}, {'quantity': 4}], 'storeLocation': 'Seattle', 'customer': {'gender': 'M', 'age': 50, 'email': 'keecade@hem.uy', 'satisfaction': 5}, 'couponUsed': False, 'purchaseMethod': 'Phone'}
   {'_id': ObjectId('...'), 'saleDate': datetime.datetime(2018, 1, 23, 21, 6, 49, 506000), 'items': [{'quantity': 2}, {'quantity': 2}, {'quantity': 5}, {'quantity': 2}, {'quantity': 2}, {'quantity': 8}, {'quantity': 3}], 'storeLocation': 'Denver', 'customer': {'gender': 'M', 'age': 42, 'email': 'cauho@witwuta.sv', 'satisfaction': 4}, 'couponUsed': True, 'purchaseMethod': 'Phone'}
   ```

### Create and Update the Materialized View

In this section, you can learn how to create a materialized view that stores cumulative sales data for each month. This view uses data from the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections. Then, create a function to update the materialized view each month.

1. Install the required package.

   Before running the application, install the `schedule` package. This package allows you to schedule the materialized view updates to run at a specific time each month.

   Run the following command in your `search-materialized-view` project directory:

   ```shell
   pip install schedule
   ```

2. Define the materialized view and its updater.

   Create a `create_update_view.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file. This file performs the following actions:

   1. Connects to your MongoDB deployment using the connection string

   2. Immediately updates the `monthlyPhoneTransactions` materialized view by:

      - Filtering transactions with purchase method of `"Phone"`

      - Unwinding the items array to process each item

      - Grouping transactions by month and year

      - Calculating total sales quantity and price for each month

   3. Sets up a scheduler that runs on the first day of each month at midnight

   4. Continues running in the background, automatically refreshing the view on schedule

   The materialized view stores aggregated data about phone transactions by month, which can significantly improve query performance for search operations.

   ```python
   import pymongo
   import time
   import datetime
   import schedule

   def update_monthly_phone_transactions(client, collection):
       """Update the materialized view of monthly phone transactions."""
       # Create the aggregation pipeline
       pipeline = [
           {"$match": {"purchaseMethod": "Phone"}},
           {"$unwind": {"path": "$items"}},
           {"$group": {
               "_id": {
                   "$dateToString": {
                       "format": "%Y-%m",
                       "date": "$saleDate"
                   }
               },
               "sales_quantity": {"$sum": "$items.quantity"},
               "sales_price": {"$sum": "$items.price"}
           }},
           {"$set": {"sales_price": {"$toDouble": "$sales_price"}}},
           {"$merge": {
               "into": "monthlyPhoneTransactions",
               "whenMatched": "replace"
           }}
       ]
       
       # Run the aggregation
       collection.aggregate(pipeline)

   def scheduled_update(client, sales, purchase_orders):
       """Run the monthly update task."""
       update_monthly_phone_transactions(client, sales)
       update_monthly_phone_transactions(client, purchase_orders)
       print(f"Scheduled update completed at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

   def main():
       # Connect to MongoDB
       client = pymongo.MongoClient("<connection-string>")
       sales = client.sample_supplies.sales
       purchase_orders = client.sample_supplies.purchaseOrders
       
       # Update immediately on startup
       update_monthly_phone_transactions(client, sales)
       update_monthly_phone_transactions(client, purchase_orders)
       print("Initial update completed. Materialized view is ready.")
       
       # Schedule update to run at midnight on the 1st of each month
       schedule.every().day.at("00:00").do(
           lambda: scheduled_update(client, sales, purchase_orders) if datetime.datetime.now().day == 1 else None
       )
       
       print("Scheduler is running. Press Ctrl+C to exit.")
       
       # Keep the process running and execute scheduled tasks
       try:
           while True:
               schedule.run_pending()
               time.sleep(60)  # Check every minute
       except KeyboardInterrupt:
           print("\nShutting down...")
       finally:
           client.close()

   if __name__ == "__main__":
       main()

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

3. Create and update the view.

   ```shell
   python create_update_view.py
   ```

   **Output:**

   ```text
   Initial update completed. Materialized view is ready.
   Scheduler is running. Press Ctrl+C to exit.
   ```

### Create a MongoDB Search Index on the Materialized View

In this section, you can learn how to create a MongoDB Search index on the `monthlyPhoneTransactions` collection.

1. Define the index.

   Create a `create_index.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```python
   from pymongo import MongoClient
   from pymongo.operations import SearchIndexModel

   # Connect to your Atlas deployment
   uri = "<connection-string>"
   client = MongoClient(uri)

   database = client["sample_analytics"]
   collection = database["accounts"]

   # Create the MongoDB Search index definition for the document field
   search_index_model = SearchIndexModel(
       definition={
           "mappings": {
               "dynamic": true
           },
       },
       name="lookup-with-search-tutorial",
   )

   # Create the index
   result = collection.create_search_index(model=search_index_model)
   print(f"New index name: {result}")
   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Create the index.

   ```shell
   python create_index.py
   ```

   **Output:**

   ```text
   New index name: monthlySalesIndex
   ```

### Run a Query on the Materialized View

In this section, you can learn how to run a query against the newly updated and indexed `monthlyPhoneTransactions` collection.

1. Specify a simple MongoDB Search query on the `sample_supplies.monthlyPhoneTransactions` collection.

   Create a `query.py` file in your `search-materialized-view` project directory, and copy and paste the following code into the file.

   ```python
   import pymongo

   # connect to your MongoDB deployment
   client = pymongo.MongoClient('<connection-string>')

   # define pipeline
   pipeline = [
     {
       '$lookup': {
         'from': 'accounts', 
         'localField': 'accounts', 
         'foreignField': 'account_id', 
         'as': 'purchases', 
         'pipeline': [
           {
             '$search': {
               'index': 'lookup-with-search-tutorial', 
               'compound': {
                 'must': [
                   {
                     'queryString': {
                       'defaultPath': 'products', 
                       'query': 'products: (CurrencyService AND InvestmentStock)'
                     }
                   }
                 ], 
                 'should': [
                   {
                     'range': {
                       'path': 'limit', 
                       'gte': 5000, 
                       'lte': 10000
                     }
                   }
                 ]
               }
             }
           }, 
           { '$project': { '_id': 0 }  }
         ]
       }
     }, 
     { '$limit': 5 }, 
     {
       '$project': {
         '_id': 0, 
         'address': 0, 
         'birthdate': 0, 
         'username': 0, 
         'tier_and_details': 0
       }
     }
   ]

   # run pipeline
   result = client['sample_analytics']['customers'].aggregate(pipeline)

   # print results
   for i in result:
       print(i)

   ```

   Replace `<connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.

   To learn more about finding your connection string, see [Connect to a Cluster via Client Libraries.](https://www.mongodb.com/docs/atlas/driver-connection.md#std-label-connect-via-driver)

   **Note:**

   For a local or self-managed deployment, your connection string should use the following format:

   ```text
   mongodb://localhost:<port-number>/?directConnection=true
   ```

   To learn more, see [Connection Strings.](https://www.mongodb.com/docs/manual/reference/connection-string.md#std-label-mongodb-uri)

2. Run the query.

   ```shell
   python query.py
   ```

   **Output:**

   ```text
   {'months_w_over_10000': 4}
   ```

   The above query returns `4`, indicating that only 4 months out of all the months in the `monthlyPhoneTransactions` materialized view had total sales greater than or equal to 10000 dollars. This result reflects data from both the `sample_supplies.sales` and `sample_supplies.purchaseOrders` collections.
