This post will show you how to do a full-text search using text indexes on a MongoDB collection. I will use be using Nodejs code snippets in the examples. For other client libraries and mongo shell commands please refer to the documentation.


Creating a Text Index

To create a text index use createIndex() function,

await client
    .db("<database-name>")
    .collection("<books>")
    .createIndex({
      name: "text",
      description: "text",
    });

The above code creates an index from the name and description fields.

Since MongoDB has a dynamic schema this not an ideal way to create an index. Alternatively, you can use Wildcard Fields indexes to automatically index all the text records in your collection.

await client
    .db("<database-name>")
    .collection("<collection-name>")
    .createIndex({
      "$**": "text",
    });

NOTE: MongoDB supports only one text index per collection as of MongoDB 3.2

You can configure a specific language too.

await client
  .db("<database-name>")
  .collection("<collection-name>")
  .createIndex(
   { "$**": "text", },
   { default_language: "english", }
);

Searching in an index

After creating an index you can search through the index using the find() function.

await client
    .db("<database-name>")
    .collection("<collection-name>")
    .find({
      $text: {
        $search: "<your-search-query>",
      },
    })
    .toArray();

By default, these search results are not sorted according to relevance.

To sort results in the order of relevance, we need to project the text score. (i.e.) add text score field in the search result object, and then sort with respect to that field.

await client
    .db("<database-name>")
    .collection("<collection-name>")
    .find( { $text: { $search: "<your-search-query>" } } )
    .project( { score: { $meta: "textScore" } } )
    .sort( { score: { $meta: "textScore" } } )
    .toArray();

You can also use next() and hasNext() for traversing through the search result cursor.

const search = client
    .db("<database-name>")
    .collection("<collection-name>")
    .find({
      $text: {
        $search: "<your-search-query>",
      },
    })
    .sort({ score: { $meta: "textScore" } })
    .project({ score: { $meta: "textScore" } });
while (await search.hasNext()) {
  const next = await search.next();
}

You can search for a exact phrase by wrapping the phrase inside a " " .

"\"the hunger\" games"

You can also negate word in search results by adding a - before the word.

"the hunger -games"

This would return search results not containing the word “games”.

For sorting and grouping your search result, you can use Aggregation Pipelines ,

await client
    .db("<database-name>")
    .collection("<collection-name>")
    .aggregate([
      { $match: { $text: { $search: "games" } } },
      { $group: { _id: null,  ratings: { $sum: "$ratings_count" } } },
    ]).toArray();

Conclusion

That’s a wrap! We covered some basic search features that you could use in your application.

If you want to level up your searching by using features like typo tolerance, faceting and instant-search, you can consider indexing your MongoDB records into Typesense.