Explore Developer Center's New Chatbot! MongoDB AI Chatbot can be accessed at the top of your navigation to answer all your MongoDB questions.

Join us at AWS re:Invent 2024! Learn how to use MongoDB for AI use cases.
MongoDB Developer
MongoDB
plus
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right
Productschevron-right
MongoDBchevron-right

Java vs Kotlin: Different Syntax, Same Possibilities

AM
Ashni Mehta5 min read • Published Nov 25, 2024 • Updated Nov 25, 2024
Change StreamsKotlinJavaMongoDB
Facebook Icontwitter iconlinkedin icon
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty
Ever since Java was introduced in 1995, millions of developers have used it to build applications across a variety of industries and problem spaces. Other languages have emerged, too, building on top of the solid foundation that Java has provided over the past 30 years. Apache Groovy, first released in 2007, is a Java syntax-compatible language for the JVM. Scala, intended to address some of the criticisms of Java, also can run on the JVM and is interoperable with Java.
Kotlin is the newest popular contender in the world of JVM languages. Released in 2011, it has been gaining steady popularity—first in the Android application development world, and now, in the server-side JVM space. Kotlin is interoperable with Java and mainly targets the JVM. However, it is more concise than Java, with null safety and coroutine support.
In this article, we’ll cover how to use some MongoDB features, and compare and contrast how they look and feel in Kotlin and Java.

Change streams

Suppose you want to track changes to your data in MongoDB. These can be changes to a collection, database, or deployment. You can monitor real-time changes through change streams, a feature in the MongoDB server. This feature allows applications to watch for changes to data and react to them.
Change streams have been available for several MongoDB server releases, but in more recent releases, the ability to split a large change stream event (an event that exceeds 16MB) was introduced, with $changeStreamSplitLargeEvent. The ability to contain or omit a pre-image (a document that represents the version of the document before the operation) and a post-image (the same, but for after the operation) was also introduced, with ChangeStreamPreAndPostImagesOptions.
In Java, you can open a change stream on a collection and print events as they occur, as follows:
1ChangeStreamIterable<Document> changeStream = collection.watch();
2changeStream.forEach(event -> System.out.println("Received a change: " + event));
The watch() method is how you can open a change stream on a MongoCollection, MongoDatabase, or MongoClient. Note that depending on the object that you call watch() on, the scope of events that the change stream listens for will change (ex: if you call watch() on a MongoDatabase, all collections in that database are monitored).
The method is similar in Kotlin, but note that in the Kotlin Coroutine driver, you can leverage the power of flows, as the watch() method will return an instance of ChangeStreamFlow, which is a Flow implementation for change streams:
1val changeStream = collection.watch()
2changeStream.collect {
3 println("Received a change: $it")
4}

Builders

In MongoDB, queries can be constructed in a few ways. Before I dive into specifics, let’s start with an example. Let us assume that we want to find all patients in a patients collection who match the following eligibility criteria for a new healthcare trial:
  • Patients whose gender is “male”
  • Patients who are younger than “30”
We want to return only their addresses so that we can send them a notice in the mail about this new trial.
If we were to use the MongoDB shell, our query would look like this:
1collection.find({ "gender": "male", "age" : { "$lt": 30 }}, { "_id": 0, "address": 1 })
The results of the above query would be a set of documents containing only the address field (the _id field has been suppressed by being projected out). If we were to use the Java driver, without builders, our query would resemble the following:
1Bson filter = new Document().append("gender", "male").append("age", new Document().append("$lt", 30));
2Bson projection = new Document().append("_id", 0).append("address", 1);
3
4collection.find(filter).projection(projection);
The above would yield the addresses for patients who match the earlier criteria. However, the driver also provides methods to simplify the process of using CRUD operations and the Aggregation API to build queries more efficiently. These methods are called builders.
If we were to use builders, instead of the above code, our code in Java might resemble the following:
1import static com.mongodb.client.model.Filters.*;
2import static com.mongodb.client.model.Projections.*;
3
4Bson filter = and(eq("gender", "male"), lt("age", 30));
5Bson projection = fields(excludeId(), include("address"));
6
7collection.find(filter).projection(projection);
There are several builders that are available in the Java driver today:
In the above example, we use both the filters and projections. Filters are operations MongoDB uses to limit results to what you want to see, and projections allow you to specify which fields to include and exclude in query results. Projections are particularly useful when you want to limit the amount of data MongoDB is sending to applications, to optimize performance or reduce storage costs.

The same builders are available in Kotlin and can be used with slightly different syntax. If we consider the non-builder case in Kotlin, our code might look like the following:
1val filter = Document().append("gender", "male").append("age", Document().append("\$lt", 30))
2val projection = Document().append("_id", 0).append("address", 1)
3val results = collection.find<Results>(filter).projection(projection)
Builders simplify this quite a bit, with the resulting code resembling the following:
1val filter = Filters.and(Filters.eq(User::gender.name, "male"), Filters.lt(User::age.name, 30))
2val projection = Projections.fields(Projections.excludeId(), Projections.include("address"))
3val results = collection.find<Results>(filter).projection(projection)

Queryable Encryption

MongoDB supports encryption in several ways:
(1) In transit through TLS/SSL, to ensure that MongoDB network traffic is only readable by the intended client
(2) At rest, to help ensure compliance with security and privacy standards like HIPAA, PCI-DSS, and FERPA
(3) In-use, to encrypt data when transmitted, stored, and processed
Queryable Encryption is a form of in-use encryption that allows you to encrypt sensitive data from the client-side, store sensitive fields as fully randomized encrypted data on the server-side, and run expressive queries on the encrypted data. It is an industry-first, fast, searchable encryption scheme that allows you to perform expressive queries on encrypted fields without the need for deterministic encryption or hardware enclaves. Although Queryable Encryption was launched last year, with support for equality queries, with MongoDB 8.0 comes the ability to run range queries, as well. Range queries can be used to filter documents with specific value ranges, like dates or numerical intervals.
To use the new range support, you’ll need to first: \
  1. Optional: If you’re using explicit encryption (as opposed to automatic encryption), you will also need to create a Data Encryption Key for your encrypted field(s).
Once you have your application, you’ll need to:
  1. Specify which fields you’d like to encrypt by adding them to the encryption schema, with the queries property. Similar to specifying a queryType of “equality” for equality queries, for range queries, you’d want to specify a queryType of “range”.
  2. Instantiate ClientEncryption to access the API for encryption helper methods (you can use ClientEncryptions#create to do so).
  3. Create the collection (note that it is recommended to explicitly create the collection so that an index is created on the encrypted fields).
  4. Insert at least one document with encrypted fields.
For a more in-depth guide to the above steps, we have code snippets throughout our documentation.
Once a field has been designated as encrypted, and at least one document has been inserted into your collection with one of those encrypted fields, you can query to find matching documents. In Java, a range query to find a patient whose bill amount is between 1000 and 2000 might resemble the following:
1Document filter = new Document("patientRecord.billAmount", new Document("$gt", 1000).append("$lt", 2000));
2Patient findResult = collection.find(filter).first();
Kotlin is not so different, syntax-wise, with the resulting range query resembling the following:
1val filter = Document("patientRecord.billAmount", Document("\$gt", 1000).append("\$lt", 2000))
2val findResult: Patient? = collection.find(filter).first()
To explore Range Queries (and Queryable Encryption) in greater detail, check out our tutorial, Java Meets Queryable Encryption! It’ll take you step by step through the process of setting up a project, configuring automatic encryption, the encryption classes, and more.

Summary

In the above article, we touched on key features of MongoDB and how to use them in two different, but complementary, languages. Java and Kotlin are two distinct languages that target the JVM, but with MongoDB support for both, you can rest assured that you can interact with your data in the language of your choice. Plus, because Java and Kotlin are interoperable, you can leverage the power of the long-standing Java ecosystem and the libraries and utilities within it, to build new Kotlin applications.
Top Comments in Forums
There are no comments on this article yet.
Start the Conversation

Facebook Icontwitter iconlinkedin icon
Rate this article
star-empty
star-empty
star-empty
star-empty
star-empty
Related
Article

Bloated Documents


Oct 01, 2024 | 6 min read
Article

The Six Principles for Building Robust Yet Flexible Shared Data Applications


Sep 23, 2022 | 3 min read
Article

Case-Insensitive Queries Without Case-Insensitive Indexes


Oct 01, 2024 | 8 min read
Quickstart

Complex Aggregation Pipelines with Vanilla PHP and MongoDB


Sep 05, 2024 | 10 min read
Table of Contents
  • Change streams