Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

The need for fast and efficient text searches often arises when developing database applications. They should also support full and partial text matches to make these searches even more user-friendly. For this purpose, MongoDB provides a couple of approaches to finding relevant documents using text searches.

In this tutorial, we’ll explore text search in MongoDB, its features, how to use it, and how to get the most out of it.

2. Text Search in MongoDB

While text search queries are a powerful tool, they require a specific setup. To achieve this, we’ll need to create a text index on the collection.

Indexes are like special folders that keep just a little bit of info from each document in a collection. In other words, they’re separate from the actual documents themselves. Moreover, MongoDB lets users make different kinds of indexes.

Fortunately, MongoDB provides text indexes designed to facilitate searches within string content. These indexes are flexible and can encompass multiple fields, allowing for comprehensive searches. Additionally, these indexes help the database search through collections much faster.

In the first place, let’s create a DB client by specifying the connection string, DB name, and collection name:

@Before
public static void setup() {
    if (mongoClient == null) {
        mongoClient = MongoClients.create("mongodb://localhost:27017");
        database = mongoClient.getDatabase("baeldung");
        collection = database.getCollection("user");
    }
}

Let’s also create a text index on the field of the collection:

void createTextIndex(String field) {
    IndexOptions indexOptions = new IndexOptions();
    indexOptions.name("textIndex").unique(false).background(true);
    collection.createIndex(Indexes.text(field), indexOptions);
}

There’s a limitation: a collection can only have one dedicated text search index.

Simple full-text searches are pretty straightforward. We can input keywords or phrases, and then the system locates documents containing those exact terms.

Besides simple full-text, there are several ways to perform a full-text search. Each has its own advantages and is tailored for specific use cases. Some common ways, among others, include boolean full-text search, phrase search, and proximity search.

Let’s create a method to perform text search query:

List<Document> searchUser(String query) {
    Document result = new Document("$text", new Document("$search", name));
    return collection.find(result).into(new ArrayList<>());
}

$text performs a text search on the content of the fields indexed with a text index, whereas $search defines the target text to be searched for. Additionally, $text tokenizes the search string using whitespace and performs a logical OR of all such tokens in the search string. This means that when searching for “Java Spring”, we would find all the documents with either “Java” or “Spring” or even both.

Let’s create some records to explore full-text search functionality:

@Test
void whenSearchingUserWithFullText_thenCorrectCountReturned() {
    // WHEN
    insertUser("Leonel", "Java Spring");
    insertUser("John", "Java Spring MongoDB");
    insertUser("Smith", "Java");
    createTextIndex("description");

    // THEN
    assertEquals("All users with term 'Java' or 'Spring'", 3, searchUser("Java Spring").size());
}

We can also exclude a word by prepending a “-” character in the search query:

assertEquals("All users with term 'Java' or 'Spring' but not 'MongoDB'", 2, searchUser("Java Spring -MongoDB").size());

Similarly, we can also search for exact phrases by enclosing them in double quotes:

assertEquals("All users with term Java only", 1, searchUser("\"Java\"").size());

MongoDB doesn’t natively support partial searches. Unlike the full-text search, the partial, fuzzy, or substring search is not straightforward. The search feature applies the language-specific rules for stopwords and stemming.

Stemming rules for supported languages are based on standard algorithms, which address common verbs and nouns and are often unaware of proper nouns. 

Let’s try to search for a user with a partial search:

@Test
void whenSearchingUserWithPartialText_thenCorrectCountReturned() {
    // WHEN
    insertUser("LEONEL", "Java Spring");
    createTextIndex("name");

    // THEN
    assertEquals("Search with capital case", 1, mongoDBClient.searchUser("LEONEL").size());
    assertEquals("Search with lower case", 1, mongoDBClient.searchUser("leonel").size());
    assertEquals("Partial search", 1, mongoDBClient.searchUser("LEONE").size());
}

On the contrary, the system cannot locate ‘L’, ‘LEO’, or ‘NEL’ due to the stemming rules:

assertEquals("Partial search with L", 0, searchUser("L").size());
assertEquals("Partial search with LEO", 0, searchUser("LEO").size());
assertEquals("Partial search with NEL", 0, searchUser("NEL").size());

One of the workaround for the partial search is to use $regex. For this cause, we will not need the text index, which may slow the search operation if the collection is very large. 

5. Conclusion

In this quick tutorial, we’ve examined full and partial text searches in MongoDB. We learned about using search queries to find exactly what we are looking for and to exclude certain terms from our search results. We also discovered that prefix and postfix do not match in a document for partial searches and identified a workaround.

As always, all the code snippets can be found over on GitHub.

Course – LSD (cat=Persistence)

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
res – Persistence (eBook) (cat=Persistence)
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments