Creating MongoDB Lookup Views Programmatically in Java Spring Applications

Learn how to create a MongoDB lookup view that combines data from multiple collections using Java code and the Spring Data MongoDB library.

Before You Start

For the rest of this post, I will assume that you understand how MongoDB views work. If you don't, you can check the following articles before reading this one:

It is also important to know that I will use standard (in-memory) MongoDB views.

⚠️The current Spring Data MongoDB Starter Project version at the time this article was written is 3.1.12 

Creating the Initial Collections

Let's go ahead and create two collections that will be the initial data sources for a MongoDB view:

  • An aircraft collection

  • A flights collection

Flights have a reference to an aircraft by using the aircraftId field.

// Create and populate aircraft collection
db.createCollection("aircraft");
db.aircraft.insertMany([
    { _id: 1, model: "Boeing 737-700", nbPassengers: 200, range: 5000, operator: "Austrian" },
    { _id: 2, model: "Airbus A321", nbPassengers: 220, range: 6800, operator: "Air France" },
    { _id: 3, model: "Embraer E175-E2", nbPassengers: 80, range: 3000, operator: "LOT" },
]);
// Create and populate flights (link via aircraftid)
db.createCollection("flights");
db.flights.insertMany([
    { from: "Vienna", to: "Amsterdam", aircraftId: 1 },
    { from: "Paris", to: "Lyon", aircraftId: 2 }
])

Creating a $lookup View in MongoDB

With the collections in place, it's time to create a MongoDB view to merge flight information with aircraft information.

db.createView("detailedFlights", "flights", [
    {
        $lookup: {
            from: "aircraft", localField: "aircraftId", foreignField: "_id", as: "lookup"
        }
    },
    {
        $unwind: {
            path: "$lookup"
        }
    },
    {
        $project:{
            _id: 0,
            from: 1,
            to: 1,
            aircraft: "$lookup.model",
            operatedBy: "$lookup.operator"
        }
    }
])

The code above is written in Mongo query language. We want to generate the same result using Java and Spring. Let's see how to do that.

Set Up a Spring Boot Project

We'll start with a simple, empty Java application. First, let's add the Spring Boot Starter Data MongoDB Dependency.

<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    <version>3.1.2</version>
</dependency>
## Gradle
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb:3.1.2'

Creating a Lookup MongoDB View in Java

For this example, I will create a new class and implement the @CommandLineRunner interface because I want to create the view as soon as the application starts. This is not mandatory; it's just my preference. You can certainly use this code in other ways.

import com.mongodb.client.MongoClients;
import org.bson.Document;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component
public class MongoViewBuilder implements CommandLineRunner {
    String connectionString = "mongodb://localhost:27017";
    String databaseName = "<your-db-name>";

    public MongoViewBuilder() {
    }

    @Override
    public void run(String... args) throws Exception {
        try (var client = MongoClients.create(this.connectionString)) {
            var lookupStage = new Document("$lookup",
                    new Document("from", "aircraft")
                            .append("localField", "aircraftId")
                            .append("foreignField", "_id")
                            .append("as", "lookup"));

            var unwindStage = new Document("$unwind",
                    new Document("path", "$lookup")
                            .append("preserveNullAndEmptyArrays", true)
            );

            var aircraftFieldStage = new Document("$addFields",
                    new Document("aircraft", new Document("$ifNull", Arrays.asList("$lookup.model", "YYY")))
            );

            var operatedByStage = new Document("$addFields",
                    new Document("operatedBy", new Document("$ifNull", Arrays.asList("$lookup.operator", "XXX")))
            );

            var projectStage = new Document("$project",
                    new Document("_id", 0)
                            .append("from", 1)
                            .append("to", 1)
                            .append("aircraft", 1)
                            .append("operatedBy", 1)
            );

            var pipeline = List.of(lookupStage, unwindStage, aircraftFieldStage, operatedByStage, projectStage);

            // connect to database
            var database = client.getDatabase(this.databaseName);

            // if view already exists, delete it
            boolean viewExists = database.listCollectionNames()
                    .into(new ArrayList<>())
                    .contains("detailedFlights");

            if (viewExists) {
                var view = database.getCollection("detailedFlights");
                view.drop();
                System.out.println("Removed view.");
            }

            // create view
            database.createView("detailedFlights", "flights", pipeline);
        }
    }
}

Explanation:

This Java code creates a MongoDB view using the official MongoDB Java driver. A MongoDB view is a read-only object that presents the data in a collection in a specific way by applying aggregation pipelines and transformations. Let's break down the code step by step:

  • The MongoViewBuilder class implements the CommandLineRunner interface. This means that when the Spring Boot application starts, the run method defined in this class will be executed.

  • In the run method, the MongoDB connection string and the database name are defined. The connectionString specifies the URL and port to connect to the MongoDB instance. The databaseName variable holds the name of the database in which the view will be created.

  • The run method starts by creating a MongoDB client using the provided connection string.

  • The view creation process involves defining an aggregation pipeline, a sequence of stages that process and transform the data. Each stage is defined as a Document containing the MongoDB aggregation operator and its parameters.

    • lookupStage: This stage performs a left outer join with the "aircraft" collection. It matches documents based on the "aircraftId" field in the current collection with the "_id" field in the "aircraft" collection. The result is stored in the "lookup" field.

    • unwindStage: This stage "unwinds" the array created by the $lookup stage, effectively creating multiple documents for each element in the "lookup" array.

    • aircraftFieldStage: This stage adds a new field "aircraft" to each document. If the "lookup.model" field is not null, it uses its value; otherwise, it uses the value "YYY".

    • operatedByStage: Similar to the previous stage, this one adds an "operatedBy" field. If the "lookup.operator" field is not null, it uses its value; otherwise, it uses the value "XXX".

    • projectStage: This stage projects the final fields to be included in the view, including "from," "to," "aircraft," and "operatedBy." The "_id" field is excluded from the view.

  • The stages are collected into a list called pipeline.

  • Finally, the code creates a new view named "detailedFlights" in the database. The view is based on the "flights" collection, and the aggregation pipeline defined in the pipeline list is applied to transform the data before presenting it in the view.

Final Thoughts

Creating MongoDB views in Java is more verbose. The code seems intimidating, but if you take a step-by-step approach, defining each pipeline stage separately, the puzzle pieces come together.

Next
Next

Querying MongoDB Views in Java Spring Boot Applications