Getting Started with MongoDB and Java: Part II
Join the DZone community and get the full member experience.
Join For FreeIn the last article, we covered the basics of installing and connecting to MongoDB via a Java application. In this post, I’ll give an introduction to CRUD (Create, Read, Update, Delete) operations using the Java driver. As in the previous article, if you want to follow along and code as we go, you can use these tips to get the tests in the Getting Started project to go green.
Creating documents
In the last article, we introduced documents and how to create them from Java and insert them into MongoDB, so I’m not going to repeat that here. But if you want a reminder, or simply want to skip to playing with the code, you can take a look at Exercise3InsertTest.
Querying
Putting stuff in the database is all well and good, but you’ll probably want to query the database to get data from it.
In the last article we covered some basics on using find() to get data from the database. We also showed an example in Exercise4RetrieveTest. But MongoDB supports more than simply getting a single document by ID or getting all the documents in a collection. As I mentioned, you can query by example, building up a query document that looks a similar shape to the one you want.
For the following examples I’m going to assume a document which looks something like this:
person = {
_id: "anId",
name: "A Name",
address: {
street: "Street Address",
city: "City",
phone: "12345"
}
books: [ 27464, 747854, ...]
}
Find a document by ID
To recap, you can easily get a document back from the database using the unique ID:
DBCursor cursor = collection.find(new BasicDBObject("_id", "theId"));
DBObject
) using a
Map
-like syntax:
(String) cursor.one().get("name")
cursor.one()
to get it.
Find all documents matching some criteria
In the real world, you won’t always know the ID of the document you want. You could be looking for all the people with a particular name, for example.
In this case, you can create a query document that has the criteria you want:
DBCursor results = collection.find(new BasicDBObject("name", "The name I want to find"));
results.size();
for (DBObject result : results) {
// do something with each result
}
The cursor will fetch results in batches from the database, so if you run a query that matches a lot of documents, you don’t have to worry that every document is loaded into memory immediately. For most queries, the first batch returned will be 101 documents. But as you iterate over the cursor, the driver will automatically fetch further batches from the server. So you don’t have to worry about managing batching in your application. But you do need to be aware that if you iterate over the whole of the cursor (for example to put it into a List
), you will end up fetching all the results and putting them in memory.
You can get started with Exercise5SimpleQueryTest.
Selecting Fields
Generally speaking, you will read entire documents from MongoDB most of the time. However, you can choose to return just the fields that you care about (for example, you might have a large document and not need all the values). You do this by passing a second parameter into the find
method that’s another DBObject
defining the fields you want to return. In this example, we’ll search for people called “Smith”, and return only the name
field. To do this we pass in a DBObject
representing {name: 1}
:
DBCursor results = collection.find(new BasicDBObject("name", "SomeName"),
new BasicDBObject("name", 1));
address
. We do this by passing in a zero for this field name, i.e.
{address: 0}
:
DBCursor results = collection.find(new BasicDBObject("name", "SomeName"),
new BasicDBObject("address", 0));
Query Operators
As I mentioned in the previous article, your fields can be one of a number of types, including numeric. This means that you can do queries for numeric values as well. Let’s assume, for example, that our person has a numberOfOrders
field, and we wanted to find everyone who had ordered more than, let’s say, 10 items. You can do this using the $gt operator:
DBCursor results = collection.find(new BasicDBObject("numberOfOrders", new BasicDBObject("$gt", 10)));
$gt
condition to use this operator. All of the query operators
are documented
, and work in a similar way to this example.
You might be wondering what terrible things could happen if you try to perform some sort of numeric comparison on a field that is a String, since the database supports any type of value in any of the fields (and in Java the values are Objects so you don’t get the benefit of type safety). So, what happens if you do this?
DBCursor results = collection.find(new BasicDBObject("name", new BasicDBObject("$gt", 10)));
You can use this technique to get the test in Exercise7QueryOperatorsTest to go green - it’s a bit of a daft example, but you get the idea.
Querying Subdocuments
So far we’ve assumed that we only want to query values in our top-level fields. However, we might want to query for values in a subdocument - for example, with our person document, we might want to find everyone who lives in the same city. We can use dot notation like this:
DBObject findLondoners = new BasicDBObject("address.city", "London");
collection.find(findLondoners));
Familiar methods
I mentioned earlier that you can iterate over a cursor, and that the driver will fetch results in batches. However, you can also use the familiar-looking skip() and limit() methods. You can use these to fix up the test in Exercise8SkipAndLimitTest.
A last note on querying: Indexes
Like a traditional database, you can add indexes onto the database to improve the speed of regular queries. There’s extensive documentation on indexes which you can read at your own leisure. However, it is worth pointing out that, if necessary, you can programmatically create indexes via the Java driver, using createIndexes
. For example:
collection.createIndex(new BasicDBObject("fieldToIndex", 1));
Updating values
Now you can insert into and read from the database. But your data is probably not static, especially as one of the benefits of MongoDB is a flexible schema that can evolve with your needs over time.
In order to update values in the database, you’ll have to define the query criteria that states which document(s) you want to update, and you’ll have to pass in the document that represents the updates you want to make.
There are a few things to be aware of when you’re updating documents in MongoDB, once you understand these it’s as simple as everything else we’ve seen so far.
Firstly, by default only the first document that matches the query criteria is updated.
Secondly, if you pass in a document as the value to update to, this new document will replace the whole existing document. If you think about it, the common use-case will be: you retrieve something from the database; you modify it based on some criteria from your application or the user; then you save the updated document to the database.
I’ll show the various types of updates (and point you to the code in the test class) to walk you through these different cases.
Simple Update: Find a document and replace it with an updated one
We’ll carry on using our simple Person document for our examples. Let’s assume we’ve got a document in our database that looks like:
person = {
_id: "jo",
name: "Jo Bloggs",
address: {
street: "123 Fake St",
city: "Faketon",
phone: "5559991234"
}
books: [ 27464, 747854, ...]
}
Maybe Jo goes into witness protection and needs to change his/her name. Assuming we’ve got jo
populated in a DBObject
, we can make the appropriate changes to the document and save it into the database:
DBObject jo = // get the document representing jo
jo.put("name", "Jo In Disguise"); // replace the old name with the new one
collection.update(new BasicDBObject("_id", "jo"), // find jo by ID
jo); // set the document in the DB to the new document for Jo
Update Operators: Change a field
But sometimes you won’t have the whole document to replace the old one, sometimes you just want to update a single field in whichever document matched your criteria.
Let’s imagine that we only want to change Jo’s phone number, and we don’t have aDBObject
with all of Jo’s details but we do have the ID of the document. If we use the $setoperator, we’ll replace only the field we want to change:
collection.update(new BasicDBObject("_id", "jo"),
new BasicDBObject("$set", new BasicDBObject("phone", "5559874321")));
Now you can do Exercise11UpdateAFieldTest
Update Multiple
As I mentioned earlier, by default the update
operation updates the first document it finds and no more. You can, however, set the multi
flag on update
to update everything.
So maybe we want to update everyone in the database to have a country field, and for now we’re going to assume all the current people are in the UK:
collection.update(new BasicDBObject(),
new BasicDBObject("$set", new BasicDBObject("country", "UK")), false, true);
true
) is the flag that says to update all the values which were found.
Now we’ve learnt enough to complete the two tests inExercise12UpdateMultipleDocumentsTest
Upsert
Finally, the last thing to mention when updating documents is Upsert (Update-or-Insert). This will search for a document matching the criteria and either: update it if it’s there; or insert it into the database if it wasn’t.
Like “update multiple”, you define an upsert operation with a magic boolean. It shouldn’t come as a surprise to find it’s the first boolean param in the update statement (since “multi” was the second):
collection.update(query, personDocument, true, false);
Now you know everything you need to complete the test in Exercise13UpsertTest
Removing from the database
Finally the D in CRUD - Delete. The syntax of a remove should look familiar now we’ve got this far, you pass a document that represents your selection criteria into the remove method. So if we wanted to delete jo
from our database, we’d do:
collection.remove(new BasicDBObject("_id", "jo"));
update
, if the query matches more than one document, all those documents will be deleted (something to be aware of!). If we wanted to remove everyone who lives in London, we’d need to do:
collection.remove(new BasicDBObject("address.city", "London"));
Conclusion
Unlike traditional databases, you don’t create SQL queries in MongoDB to perform CRUD operations. Instead, operations are done by constructing documents both to query the database, and to define the operations to perform.
While we’ve covered what the basics look like in Java, there’s loads more documentation on all the core concepts in the MongoDB documentation:
If you enjoyed this article and want to learn more about MongoDB, check out this collection of tutorials and articles on all things MongoDB.
Published at DZone with permission of Trisha Gee, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Test Data Tutorial: A Comprehensive Guide With Examples and Best Practices
-
Payments Architecture - An Introduction
-
Building a Robust Data Engineering Pipeline in the Streaming Media Industry: An Insider’s Perspective
-
What Is Test Pyramid: Getting Started With Test Automation Pyramid
Comments