On some occasions, you just don’t have all the info you need to find a document in a MongoDb instance. That’s why you need to do pattern searching.
Pattern searching, if you’re unfamiliar with it, is similar to “like” searching on a traditional, relational database.
Consider the following query:
select * from momawork where title like '%Plate%';
That query will find any row in the table where the contents of the title column include the word “Plate” anywhere.
In other words, if the title is “Hi, I’m a Plate” or “Plate This!”, the query will match and the rows will be returned.
But MongoDb instances don’t use tables or SQL.
So how do you do something similar to a “like” search in a MongoDb instance?
Moreover, how do you do that kind of search if you’re using Spring Boot with a MongoDB instance?
Those are the questions that we’ll answer in this article.
As always, feel free to go straight to the source code in GitHub if you’re one of those TL;DR types. We won’t be offended.
The Use Case
You’re playing Tetris in your office when your boss, Smithers, walks in.
He tells you he’s got an issue with the app that lets people browse Metropolitan Museum of Art (MOMA) pieces online. Apparently, some people want to search for art by entering only a portion of the title name!
He can’t believe that people would want to do such a thing. But he’s sure that you can make those folks happy.
Smithers leaves your office, telling you to deploy a Spring Boot app that handles pattern searching on the MOMA MongoDb instance.
You get to work.
The Tech Stack
It’s a microservices environment. People browse the MOMA works on an Angular app.
That app sends HTTP requests to the Spring Boot application, which in turn interacts with the MongoDb instance.
Spring Boot returns the requested data to the Angular app in JSON format.
We won’t cover the intricacies of an Angular app here. That’s a subject for a different day.
We will, however, go over the implementation on the Spring Boot side.
Getting the Ball Rolling
Within Eclipse, create a new Maven project. That’s the project that you’ll use to build your Spring Boot app.
First things first: update the
pom.xml file with the necessary dependencies. Here’s what it looks like:
Pay particular attention to the last dependency:
That tells Spring Boot you’re ready to do business with a MongoDb instance.
You also need to get your
application.properties file up to snuff. Here’s what it will look like:
The most important piece of info there is the second line. That’s the URI you’ll use to access the MongoDb instance.
As you can see, that line includes the user name and password as well as the host name, port number, and database name.
As you can also see, you’ll need to replace that info with data specific to your MongoDb instance.
The Application Class
Next, create a class that launches a Spring Boot app.
Nothing fancy there. It’s all the usual stuff.
Create a Java bean that reflects the MOMA work you persist in the MongoDb instance.
That’s easier than many people think because Mongo databases are document-driven. They allow you to add whatever fields you want on-the-fly without defining anything like a schema.
So all the fields you define in the Java class will automagically map to fields in MongoDb documents.
An instance of the Java class represents a single document in the collection.
However, the collection itself will have the same name as the Java class.
To some folks, that’s confusing. But it makes sense if you think about it.
Just as a Java class can have many instances, a MongoDb collection can have many documents.
Anyhoo, here’s what the MomaWork class looks like.
It’s a standard Java bean that defines a bunch of fields specific to works of art.
The Ingestion Has Happened
Before we go on with the code, let’s make one thing abundantly clear: for the purposes of this tutorial, the database is already populated with info retrieved from the MOMA API.
So you won’t need to persist anything. All you need to do is handle the search feature.
If you want real data to test against, feel free to use that API to populate your own MongoDb collection.
And now we get to where the magic happens: the repository.
Here’s what the code for that interface looks like:
The class declaration is pretty standard for a Spring Boot repository. In this case, though, it extends
MongoRepository instead of the usual
Additionally, the ID is defined as a
String object instead of a
Long object as is often the case.
Why is that?
Because Mongo databases store IDs in a format that looks like this: “5bdb14a486bcaf40bc2593dc”. That’s hexadecimal and requires a String instead of a number.
The first method in the class is easy to understand. It returns all documents with pagination.
The second method is the one you want to focus on. Take a look at the
Believe it or not, that annotation is handling pattern searching. Let’s go through it in pieces.
The first part (
'title') tells the MongoDb instance that you’re looking for matches on the title field. The rest of the query tells the MongoDb instance the kind of matches that you’re looking for.
'$regex' bit tells the database that you’re performing a regular expression search. That’s cool, because regex is well-suited to a pattern search.
And what about
'$options'? In this case that “i” you see tells the database to run a search that’s case-insensitive. In other words “Hey” and “hey” would return the same results.
You might also notice that the whole query looks like a JSON object. That’s understandable because MongoDb uses BSON, binary encoding strikingly similar to JSON.
We also shouldn’t ignore the
'?0' in the room. What’s that?
That’s telling Spring Boot to substitute the value of the first parameter with that
?0 in the query.
So if the value of name in is “finkelstein,” then the database will perform the following query:
What does the zero have to do with anything? The system is using 0-based indexing. The first parameter is #0, the second parameter is #1, and so on.
Next, let’s run a few tests directly against the database to see if this query structure works.
Mucking About in MongoDb
As we’ve seen, there’s already plenty of MOMA data in the MongoDb instance. Now it’s time to take a look at some of it.
Please note: you won’t be able to do this in your database unless you’ve ingested Metropolitan Museum of Art data. The purpose of this section is to ensure that the query is structured correctly.
The name of the database that holds the MOMA data is unimaginatively named “moma.” You can access that with the following command from within the Mongo shell:
The name of the collection should match the name of the Java class. View all collections with this command:
That results in this:
Bingo! There’s a collection called
momaWork. Now, it’s time to take a peek at its contents.
View all documents with this command:
The empty braces within the parentheses mean that there’s no filter criteria. The query will return everything.
Well, not quite everything. It paginates the results.
Here’s what the results look like:
Again, MongoDb documents look like JSON objects. That makes this result pretty easy to read.
If you look at the last result at the bottom, you’ll see it has the title: “Plate (one of a set of twelve).”
Now, put the query structure to the test. Try to find anything with the word “plate” in its title. This should do the trick:
And here are the results:
Looks good! All the results have the word “Plate” in the title. So you know the query works.
Now it’s time to put a controller in place so a client app can perform searches. Here’s what that class looks like:
The controller is annotated with
@RestController so it accepts REST requests and returns JSON data.
Next, the code injects an instance of the
MomaRepository interface so it can access the database.
The only public method is
search(). It’s annotated with
@GetMapping because it accepts HTTP GET requests.
The URL path is defined in the parentheses following the
There are two parameters defined in
search(). One of them is required, the other isn’t.
The match parameter is required. It’s the string pattern to search for.
In the example above, the pattern was “plate.”
The next parameter in the
search() method isn’t required. It’s the page number. It will default to the first page if no page number is specified.
Note that both parameters are annotated with
@RequestParam. That means they’ll get passed in the URL as request parameters.
A search for “plate” would look like this:
The body of the method itself does three things:
- It creates a Pageable object
- It fetches the results that match the pattern
- It returns the paginated results
That’s really all there is to it.
Testing It Out
It’s time to test.
Right-click on the
PatternSearchApplication class within Eclipse.
Give it a few seconds to start up.
Once it’s started, it’s easy to run a test. You can do it from a browser since you’re doing a GET request.
Fire up Google Chrome and visit the following URL:
Remember, all demos on this site use port 8090. If your Spring Application is using a different port, adjust the URL accordingly.
If you have MOMA data loaded in your MongoDB instance, you’ll see something like the following:
Results are paginated so you’ll only see the first 10 artworks.
The test is a success. Pattern searching works with MongoDb!
Wrapping It Up
Again, there’s fully functioning code on GitHub if you want to grab it. Feel free to fork it an mess around some more.