It's often the case that documents in a MongoDB collection include arrays of child elements. And sometimes you want to "unwind" those arrays to create one parent document for each child element.

Fortunately, there's a MongoDB aggregation pipeline operation that makes it easy for you to do that. It's intuitively named "unwind."

And you can use it with MongoTemplate in your Spring Boot application.

In fact, I'll show you how to do just that in this guide.

You can also go straight to the source on GitHub if that's how you roll.

The Business Requirements

Your boss Smithers walks into your office while sipping his coffee.

"Hey there," he says as he sits down. "Need some new stuff."

You're not sure where he's going with this as he never uses the word "stuff."

"Regarding that CRM app you're working on, I need you to create a new service request. I need all individual sales listed. Complete info."

He gets up to walk away but just before he gets out the door he turns back and says: "Oh, and it needs to be sorted by date."

He walks on.

You're in Good Shape

You've already done quite a bit of work with grabbing data from the contacts database using aggregation pipelines. So this shouldn't be too difficult.

However, if you're new to using MongoTemplate with aggregations, I suggest starting at my first guide on the subject. That will help you immensely.

Then come back here.

Now let's assume that your contacts DB is populated with data that looks like this:

[ {
  "id" : "5fde12d60ab013769b67cf02",
  "firstName" : "Chew",
  "lastName" : "Bacca",
  "email" : "chewie@xmail.com",
  "source" : "WEBSITE_FORM",
  "status" : "ACTIVE",
  "statusChange" : 0,
  "linesOfBusiness" : [ "ANGULAR" ],
  "company" : "Working for Han",
  "title" : "Wookie",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09393",
    "title" : "Angular UI Refactoring",
    "value" : 100000,
    "date" : 1609650000000,
    "lineOfBusiness" : "ANGULAR"
  }, {
    "purchaseOrderNumber" : "09394",
    "title" : "Revamp CRM UI",
    "value" : 150000,
    "date" : 1610254800000,
    "lineOfBusiness" : "ANGULAR"
  }, {
    "purchaseOrderNumber" : "09395",
    "title" : "Angular tutorial",
    "value" : 20000,
    "date" : 1610427600000,
    "lineOfBusiness" : "ANGULAR"
  } ]
}, {
  "id" : "5fde1028792009283c603929",
  "firstName" : "JarJar",
  "lastName" : "Binks",
  "email" : "jarjar@xmail.com",
  "addresses" : [ {
    "street1" : "1400 Plum Way",
    "city" : "Onisius",
    "state" : "NM",
    "zip" : "80909",
    "addressType" : "HOME"
  } ],
  "source" : "WALKIN",
  "status" : "CONTACTED",
  "statusChange" : 0,
  "linesOfBusiness" : [ "FULL_STACK" ],
  "company" : "None",
  "title" : "Comic Relief",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09396",
    "title" : "Spring Boot API for In-House REST Service",
    "value" : 310000,
    "date" : 1609736400000
  }, {
    "purchaseOrderNumber" : "09396",
    "title" : "Angular Optimization",
    "value" : 200000,
    "date" : 1610859600000,
    "lineOfBusiness" : "ANGULAR"
  }, {
    "purchaseOrderNumber" : "09397",
    "title" : "Full Stack Work",
    "value" : 110000,
    "date" : 1610946000000,
    "lineOfBusiness" : "FULL_STACK"
  } ]
}, {
  "id" : "5fde117edd79e20e3ff6528c",
  "firstName" : "Lando",
  "lastName" : "Calrissian",
  "email" : "lando@xmail.com",
  "phones" : [ {
    "phone" : "(555) 555-5555",
    "phoneType" : "WORK",
    "countryCode" : "us"
  } ],
  "source" : "INBOUND_SALES_CALL",
  "status" : "CONTACTED",
  "statusChange" : 0,
  "linesOfBusiness" : [ "ANGULAR", "DEV_OPS" ],
  "company" : "Cloud City",
  "title" : "Friend",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09398",
    "title" : "Harness Training",
    "value" : 150000,
    "date" : 1610082000000,
    "lineOfBusiness" : "DEV_OPS"
  }, {
    "purchaseOrderNumber" : "09399",
    "title" : "Jenkins Training",
    "value" : 150000,
    "date" : 1610254800000,
    "lineOfBusiness" : "DEV_OPS"
  } ]
}, {
  "id" : "5fde1ac084dad94dbb7f82ae",
  "firstName" : "R2D2",
  "lastName" : "Droid",
  "email" : "r2d2@xmail.com",
  "source" : "EMAIL",
  "status" : "ACTIVE",
  "statusChange" : 0,
  "linesOfBusiness" : [ "JAVA_ENTERPRISE" ],
  "company" : "For Luke",
  "title" : "Droid",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  }
}, {
  "id" : "5fdd0af34e9d6806f369abf0",
  "firstName" : "Boba",
  "lastName" : "Fett",
  "email" : "boba@xmail.com",
  "phones" : [ {
    "phone" : "(555) 555-5555",
    "phoneType" : "HOME",
    "countryCode" : "us"
  } ],
  "addresses" : [ {
    "street1" : "1222 Galaxy Way",
    "city" : "Alterion",
    "state" : "AR",
    "zip" : "22222",
    "country" : "US",
    "addressType" : "HOME"
  } ],
  "source" : "INBOUND_SALES_CALL",
  "status" : "CONTACTED",
  "statusChange" : 0,
  "linesOfBusiness" : [ "DEV_OPS" ],
  "company" : "Empire",
  "title" : "Bounty Hunter",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09400",
    "title" : "Jenkins Training",
    "value" : 150000,
    "date" : 1610341200000,
    "lineOfBusiness" : "DEV_OPS"
  }, {
    "purchaseOrderNumber" : "09401",
    "title" : "Kubernetes Training",
    "value" : 200000,
    "date" : 1610686800000,
    "lineOfBusiness" : "DEV_OPS"
  } ]
}, {
  "id" : "5fdd0cedaac5f75d62564ee7",
  "firstName" : "Jabba",
  "lastName" : "Hutt",
  "email" : "jabba@xmail.com",
  "source" : "EMAIL",
  "status" : "NEW",
  "statusChange" : 0,
  "linesOfBusiness" : [ "ANGULAR" ],
  "company" : "Sandz",
  "title" : "Large",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09403",
    "title" : "Update UI to Match Wireframes",
    "value" : 200000,
    "date" : 1609822800000,
    "lineOfBusiness" : "ANGULAR"
  }, {
    "purchaseOrderNumber" : "09403",
    "title" : "Improve UI Responsiveness",
    "value" : 150000,
    "date" : 1610254800000,
    "lineOfBusiness" : "ANGULAR"
  } ]
}, {
  "id" : "5fdd0e7c870ef4713e179384",
  "firstName" : "Princess",
  "lastName" : "Leia",
  "email" : "leia@xmail.com",
  "phones" : [ {
    "phone" : "(555) 555-5555",
    "phoneType" : "WORK",
    "countryCode" : "us"
  } ],
  "source" : "WALKIN",
  "status" : "INTERESTED",
  "statusChange" : 0,
  "linesOfBusiness" : [ "JAVA_ENTERPRISE", "ANGULAR" ],
  "company" : "Republic",
  "title" : "Princess",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09404",
    "title" : "Upgrade Spring Boot application to Java 11",
    "value" : 150000,
    "date" : 1610341200000,
    "lineOfBusiness" : "JAVA_ENTERPRISE"
  }, {
    "purchaseOrderNumber" : "09406",
    "title" : "Improve Exception Handling in Spring Boot application",
    "value" : 250000,
    "date" : 1610600400000,
    "lineOfBusiness" : "JAVA_ENTERPRISE"
  }, {
    "purchaseOrderNumber" : "09407",
    "title" : "Support Additional External REST services in Spring Boot",
    "value" : 350000,
    "date" : 1611118800000,
    "lineOfBusiness" : "JAVA_ENTERPRISE"
  }, {
    "purchaseOrderNumber" : "09408",
    "title" : "Enhance UI for Accounting App",
    "value" : 150000,
    "date" : 1611205200000,
    "lineOfBusiness" : "ANGULAR"
  } ]
}, {
  "id" : "5fd5f37bde602d3bacef69db",
  "firstName" : "Luke",
  "lastName" : "Skywalker",
  "email" : "luke@tat2.com",
  "source" : "EMAIL",
  "sourceDetails" : "He emailed me",
  "status" : "NEW",
  "statusChange" : 0,
  "linesOfBusiness" : [ "ANGULAR" ],
  "company" : "International Business",
  "title" : "President",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  }
}, {
  "id" : "5fdd0f2aea599836ca3ddbf1",
  "firstName" : "Han",
  "lastName" : "Solo",
  "email" : "han@xmail.com",
  "addresses" : [ {
    "street1" : "111 Millennium Way",
    "city" : "Nessy",
    "state" : "CO",
    "addressType" : "HOME"
  } ],
  "source" : "EMAIL",
  "status" : "ACTIVE",
  "statusChange" : 0,
  "title" : "Pirate",
  "authority" : false,
  "salesOwner" : {
    "id" : "5f78d8fbc1d3246ab4303f2b",
    "firstName" : "Darth",
    "lastName" : "Vader",
    "email" : "darth@xmail.com",
    "username" : "darth",
    "phoneNumber" : "474-555-1212"
  }
}, {
  "id" : "6005e87163660b7b2e6a16df",
  "firstName" : "Governor",
  "lastName" : "Tarkin",
  "email" : "tarkin@xmail.com",
  "phones" : [ {
    "phone" : "(555) 555-5555",
    "phoneType" : "CELL"
  } ],
  "addresses" : [ {
    "city" : "Home City",
    "state" : "MN",
    "addressType" : "HOME"
  } ],
  "source" : "EMAIL",
  "status" : "ACTIVE",
  "linesOfBusiness" : [ "FULL_STACK" ],
  "company" : "No Moon",
  "title" : "Governor",
  "authority" : true,
  "salesOwner" : {
    "id" : "6005e76bac127e0f5d9a6560",
    "firstName" : "The",
    "lastName" : "Emperor",
    "email" : "theemperor@xmail.com",
    "username" : "theemperor",
    "phoneNumber" : "(555) 555-5555"
  },
  "sales" : [ {
    "purchaseOrderNumber" : "09409",
    "title" : "Refactor Spring Boot application",
    "value" : 150000,
    "date" : 1610341200000,
    "lineOfBusiness" : "JAVA_ENTERPRISE"
  }, {
    "purchaseOrderNumber" : "09410",
    "title" : "Full stack refactoring",
    "value" : 250000,
    "date" : 1610600400000,
    "lineOfBusiness" : "FULL_STACK"
  } ]
} ]

As you can see, many of the contacts have sales. You need to list those sales one by one.

Ordered by date.

The "sales" field for each contact is an array. It's an array of sales documents that each look like the Sale class on the Java side of this business.

The "value" field in the sale document reflects the dollar value of the deal. But it's in cents because it's easier to keep things in whole numbers.

And the "date" field is the date represented as a long number. It's the same thing you'd get if you called getTime() on a Java Date object.

The problem here, of course, is that sales is a child object of the parent document that represents the contact.  But you don't need the contact info here. All you need is the sales info. So how do you get it?

You unwind it.

What the Heck Is Unwind Anyway?

The unwind operation in a MongoDB aggregation pipeline creates a new parent element for each element in an array. So if there are three elements in the array, the pipeline stage will create three parent documents: one for each of the elements.

Take the contacts data above. If you unwind it, you'll end up with something that looks like this:

{
    "_id": ObjectId("5fde12d60ab013769b67cf02"),
    "firstName": "Chew",
    "lastName": "Bacca",
    "email": "chewie@xmail.com",
    "phones": [],
    "addresses": [],
    "source": "WEBSITE_FORM",
    "status": "ACTIVE",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR"
    ],
    "company": "Working for Han",
    "title": "Wookie",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09393",
        "title": "Angular UI Refactoring",
        "value": 100000,
        "date": NumberLong("1609650000000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde1028792009283c603929"),
    "firstName": "JarJar",
    "lastName": "Binks",
    "email": "jarjar@xmail.com",
    "phones": [],
    "addresses": [
        {
            "street1": "1400 Plum Way",
            "city": "Onisius",
            "state": "NM",
            "zip": "80909",
            "addressType": "HOME"
        }
    ],
    "source": "WALKIN",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "FULL_STACK"
    ],
    "company": "None",
    "title": "Comic Relief",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09396",
        "title": "Spring Boot API for In-House REST Service",
        "value": 310000,
        "date": NumberLong("1609736400000")
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0cedaac5f75d62564ee7"),
    "firstName": "Jabba",
    "lastName": "Hutt",
    "email": "jabba@xmail.com",
    "phones": [],
    "addresses": [],
    "source": "EMAIL",
    "status": "NEW",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR"
    ],
    "company": "Sandz",
    "title": "Large",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09403",
        "title": "Update UI to Match Wireframes",
        "value": 200000,
        "date": NumberLong("1609822800000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde117edd79e20e3ff6528c"),
    "firstName": "Lando",
    "lastName": "Calrissian",
    "email": "lando@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "INBOUND_SALES_CALL",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR",
        "DEV_OPS"
    ],
    "company": "Cloud City",
    "title": "Friend",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09398",
        "title": "Harness Training",
        "value": 150000,
        "date": NumberLong("1610082000000"),
        "lineOfBusiness": "DEV_OPS"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0cedaac5f75d62564ee7"),
    "firstName": "Jabba",
    "lastName": "Hutt",
    "email": "jabba@xmail.com",
    "phones": [],
    "addresses": [],
    "source": "EMAIL",
    "status": "NEW",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR"
    ],
    "company": "Sandz",
    "title": "Large",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09403",
        "title": "Improve UI Responsiveness",
        "value": 150000,
        "date": NumberLong("1610254800000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde117edd79e20e3ff6528c"),
    "firstName": "Lando",
    "lastName": "Calrissian",
    "email": "lando@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "INBOUND_SALES_CALL",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR",
        "DEV_OPS"
    ],
    "company": "Cloud City",
    "title": "Friend",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09399",
        "title": "Jenkins Training",
        "value": 150000,
        "date": NumberLong("1610254800000"),
        "lineOfBusiness": "DEV_OPS"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde12d60ab013769b67cf02"),
    "firstName": "Chew",
    "lastName": "Bacca",
    "email": "chewie@xmail.com",
    "phones": [],
    "addresses": [],
    "source": "WEBSITE_FORM",
    "status": "ACTIVE",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR"
    ],
    "company": "Working for Han",
    "title": "Wookie",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09394",
        "title": "Revamp CRM UI",
        "value": 150000,
        "date": NumberLong("1610254800000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0af34e9d6806f369abf0"),
    "firstName": "Boba",
    "lastName": "Fett",
    "email": "boba@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "HOME",
            "countryCode": "us"
        }
    ],
    "addresses": [
        {
            "street1": "1222 Galaxy Way",
            "city": "Alterion",
            "state": "AR",
            "zip": "22222",
            "country": "US",
            "addressType": "HOME"
        }
    ],
    "source": "INBOUND_SALES_CALL",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "DEV_OPS"
    ],
    "company": "Empire",
    "title": "Bounty Hunter",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09400",
        "title": "Jenkins Training",
        "value": 150000,
        "date": NumberLong("1610341200000"),
        "lineOfBusiness": "DEV_OPS"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0e7c870ef4713e179384"),
    "firstName": "Princess",
    "lastName": "Leia",
    "email": "leia@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "WALKIN",
    "status": "INTERESTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "JAVA_ENTERPRISE",
        "ANGULAR"
    ],
    "company": "Republic",
    "title": "Princess",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09404",
        "title": "Upgrade Spring Boot application to Java 11",
        "value": 150000,
        "date": NumberLong("1610341200000"),
        "lineOfBusiness": "JAVA_ENTERPRISE"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("6005e87163660b7b2e6a16df"),
    "firstName": "Governor",
    "lastName": "Tarkin",
    "email": "tarkin@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "CELL"
        }
    ],
    "addresses": [
        {
            "city": "Home City",
            "state": "MN",
            "addressType": "HOME"
        }
    ],
    "source": "EMAIL",
    "status": "ACTIVE",
    "linesOfBusiness": [
        "FULL_STACK"
    ],
    "company": "No Moon",
    "title": "Governor",
    "authority": true,
    "salesOwner": {
        "_id": ObjectId("6005e76bac127e0f5d9a6560"),
        "firstName": "The",
        "lastName": "Emperor",
        "email": "theemperor@xmail.com",
        "username": "theemperor",
        "phoneNumber": "(555) 555-5555"
    },
    "sales": {
        "purchaseOrderNumber": "09409",
        "title": "Refactor Spring Boot application",
        "value": 150000,
        "date": NumberLong("1610341200000"),
        "lineOfBusiness": "JAVA_ENTERPRISE"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde12d60ab013769b67cf02"),
    "firstName": "Chew",
    "lastName": "Bacca",
    "email": "chewie@xmail.com",
    "phones": [],
    "addresses": [],
    "source": "WEBSITE_FORM",
    "status": "ACTIVE",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "ANGULAR"
    ],
    "company": "Working for Han",
    "title": "Wookie",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09395",
        "title": "Angular tutorial",
        "value": 20000,
        "date": NumberLong("1610427600000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0e7c870ef4713e179384"),
    "firstName": "Princess",
    "lastName": "Leia",
    "email": "leia@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "WALKIN",
    "status": "INTERESTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "JAVA_ENTERPRISE",
        "ANGULAR"
    ],
    "company": "Republic",
    "title": "Princess",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09406",
        "title": "Improve Exception Handling in Spring Boot application",
        "value": 250000,
        "date": NumberLong("1610600400000"),
        "lineOfBusiness": "JAVA_ENTERPRISE"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("6005e87163660b7b2e6a16df"),
    "firstName": "Governor",
    "lastName": "Tarkin",
    "email": "tarkin@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "CELL"
        }
    ],
    "addresses": [
        {
            "city": "Home City",
            "state": "MN",
            "addressType": "HOME"
        }
    ],
    "source": "EMAIL",
    "status": "ACTIVE",
    "linesOfBusiness": [
        "FULL_STACK"
    ],
    "company": "No Moon",
    "title": "Governor",
    "authority": true,
    "salesOwner": {
        "_id": ObjectId("6005e76bac127e0f5d9a6560"),
        "firstName": "The",
        "lastName": "Emperor",
        "email": "theemperor@xmail.com",
        "username": "theemperor",
        "phoneNumber": "(555) 555-5555"
    },
    "sales": {
        "purchaseOrderNumber": "09410",
        "title": "Full stack refactoring",
        "value": 250000,
        "date": NumberLong("1610600400000"),
        "lineOfBusiness": "FULL_STACK"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0af34e9d6806f369abf0"),
    "firstName": "Boba",
    "lastName": "Fett",
    "email": "boba@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "HOME",
            "countryCode": "us"
        }
    ],
    "addresses": [
        {
            "street1": "1222 Galaxy Way",
            "city": "Alterion",
            "state": "AR",
            "zip": "22222",
            "country": "US",
            "addressType": "HOME"
        }
    ],
    "source": "INBOUND_SALES_CALL",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "DEV_OPS"
    ],
    "company": "Empire",
    "title": "Bounty Hunter",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09401",
        "title": "Kubernetes Training",
        "value": 200000,
        "date": NumberLong("1610686800000"),
        "lineOfBusiness": "DEV_OPS"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde1028792009283c603929"),
    "firstName": "JarJar",
    "lastName": "Binks",
    "email": "jarjar@xmail.com",
    "phones": [],
    "addresses": [
        {
            "street1": "1400 Plum Way",
            "city": "Onisius",
            "state": "NM",
            "zip": "80909",
            "addressType": "HOME"
        }
    ],
    "source": "WALKIN",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "FULL_STACK"
    ],
    "company": "None",
    "title": "Comic Relief",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09396",
        "title": "Angular Optimization",
        "value": 200000,
        "date": NumberLong("1610859600000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fde1028792009283c603929"),
    "firstName": "JarJar",
    "lastName": "Binks",
    "email": "jarjar@xmail.com",
    "phones": [],
    "addresses": [
        {
            "street1": "1400 Plum Way",
            "city": "Onisius",
            "state": "NM",
            "zip": "80909",
            "addressType": "HOME"
        }
    ],
    "source": "WALKIN",
    "status": "CONTACTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "FULL_STACK"
    ],
    "company": "None",
    "title": "Comic Relief",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09397",
        "title": "Full Stack Work",
        "value": 110000,
        "date": NumberLong("1610946000000"),
        "lineOfBusiness": "FULL_STACK"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0e7c870ef4713e179384"),
    "firstName": "Princess",
    "lastName": "Leia",
    "email": "leia@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "WALKIN",
    "status": "INTERESTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "JAVA_ENTERPRISE",
        "ANGULAR"
    ],
    "company": "Republic",
    "title": "Princess",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09407",
        "title": "Support Additional External REST services in Spring Boot",
        "value": 350000,
        "date": NumberLong("1611118800000"),
        "lineOfBusiness": "JAVA_ENTERPRISE"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}
{
    "_id": ObjectId("5fdd0e7c870ef4713e179384"),
    "firstName": "Princess",
    "lastName": "Leia",
    "email": "leia@xmail.com",
    "phones": [
        {
            "phone": "(555) 555-5555",
            "phoneType": "WORK",
            "countryCode": "us"
        }
    ],
    "addresses": [],
    "source": "WALKIN",
    "status": "INTERESTED",
    "statusChange": NumberLong(0),
    "linesOfBusiness": [
        "JAVA_ENTERPRISE",
        "ANGULAR"
    ],
    "company": "Republic",
    "title": "Princess",
    "authority": false,
    "salesOwner": {
        "_id": ObjectId("5f78d8fbc1d3246ab4303f2b"),
        "firstName": "Darth",
        "lastName": "Vader",
        "email": "darth@xmail.com",
        "username": "darth",
        "phoneNumber": "474-555-1212"
    },
    "sales": {
        "purchaseOrderNumber": "09408",
        "title": "Enhance UI for Accounting App",
        "value": 150000,
        "date": NumberLong("1611205200000"),
        "lineOfBusiness": "ANGULAR"
    },
    "_class": "com.careydevelopment.contact.model.Contact"
}

Pay particularly close attention to the "sales" field now. It's no longer an array!

It's just got one element. But now you'll see the same contact name multiple times in the list. Again, you'll see a new parent document for each child element in the array.

Look just above here and you'll see Princess Leia a couple of times. But for each time she's listed, the "sales" field is different.

That's unwind. That's what it does.

Now let's make Smithers happy.

Unwinding Without Relaxing

It's time to add a new method to ContactService. Make it look like this:

	public List<Sale> listContactSales() {
		AggregationOperation match = Aggregation.match(Criteria.where("sales").exists(true));
		AggregationOperation unwind = Aggregation.unwind("sales");
		AggregationOperation sort = Aggregation.sort(Direction.ASC, "sales.date");
		AggregationOperation replaceRoot = Aggregation.replaceRoot("sales");
		
		Aggregation aggregation = Aggregation.newAggregation(match, unwind, sort, replaceRoot);

		List<Sale> sales = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Contact.class), Sale.class).getMappedResults();
		
		return sales;
	}

That pipeline has four stages. I'll cover each of them in detail.

The first stage (match) eliminates contacts with no sales. We don't need them here.

The second stage (unwind) handles the unwinding. That's what I just explained above.

The third stage (sort) sorts the unwound data by sales date. Remember: Smithers wants the data sorted by date.

The fourth stage (replaceRoot) gets rid of the parent documents and just leaves us with sales info. I've got a whole guide on replaceRoot if you need more info about how that works.

And please note that the mongoTemplate.aggregate() method is returning a list of Sale objects. Because that's what's left after replaceRoot.

By the way, here's what that aggregation pipeline looks like in your MongoDB client:

db.contacts.aggregate([
   {
      "$match":{
         "sales":{
            "$exists":true
         }
      }
   },
   {
      "$unwind":"$sales"
   },
   {
      "$sort":{
         "sales.date":1
      }
   },
   {
      "$replaceRoot":{
         "newRoot":"$sales"
      }
   }
])

Test With the Best

Now it's time to test your work. Do that with some initialization code:

@Component
public class ApplicationListenerInitialize implements ApplicationListener<ApplicationReadyEvent>  {
	
	@Autowired
	private ContactService contactService;
	
    public void onApplicationEvent(ApplicationReadyEvent event) {        	
    	List<Sale> sales = contactService.listContactSales();
    	
    	try {
	    	ObjectMapper objectMapper = new ObjectMapper();
	    	objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
	    	objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
	    	System.err.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(sales));
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
}

Save that file and restart your Spring Boot application. Once everything is loaded, you should see this output in red:

[ {
  "purchaseOrderNumber" : "09393",
  "title" : "Angular UI Refactoring",
  "value" : 100000,
  "date" : 1609650000000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09396",
  "title" : "Spring Boot API for In-House REST Service",
  "value" : 310000,
  "date" : 1609736400000
}, {
  "purchaseOrderNumber" : "09403",
  "title" : "Update UI to Match Wireframes",
  "value" : 200000,
  "date" : 1609822800000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09398",
  "title" : "Harness Training",
  "value" : 150000,
  "date" : 1610082000000,
  "lineOfBusiness" : "DEV_OPS"
}, {
  "purchaseOrderNumber" : "09403",
  "title" : "Improve UI Responsiveness",
  "value" : 150000,
  "date" : 1610254800000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09399",
  "title" : "Jenkins Training",
  "value" : 150000,
  "date" : 1610254800000,
  "lineOfBusiness" : "DEV_OPS"
}, {
  "purchaseOrderNumber" : "09394",
  "title" : "Revamp CRM UI",
  "value" : 150000,
  "date" : 1610254800000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09400",
  "title" : "Jenkins Training",
  "value" : 150000,
  "date" : 1610341200000,
  "lineOfBusiness" : "DEV_OPS"
}, {
  "purchaseOrderNumber" : "09404",
  "title" : "Upgrade Spring Boot application to Java 11",
  "value" : 150000,
  "date" : 1610341200000,
  "lineOfBusiness" : "JAVA_ENTERPRISE"
}, {
  "purchaseOrderNumber" : "09409",
  "title" : "Refactor Spring Boot application",
  "value" : 150000,
  "date" : 1610341200000,
  "lineOfBusiness" : "JAVA_ENTERPRISE"
}, {
  "purchaseOrderNumber" : "09395",
  "title" : "Angular tutorial",
  "value" : 20000,
  "date" : 1610427600000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09406",
  "title" : "Improve Exception Handling in Spring Boot application",
  "value" : 250000,
  "date" : 1610600400000,
  "lineOfBusiness" : "JAVA_ENTERPRISE"
}, {
  "purchaseOrderNumber" : "09410",
  "title" : "Full stack refactoring",
  "value" : 250000,
  "date" : 1610600400000,
  "lineOfBusiness" : "FULL_STACK"
}, {
  "purchaseOrderNumber" : "09401",
  "title" : "Kubernetes Training",
  "value" : 200000,
  "date" : 1610686800000,
  "lineOfBusiness" : "DEV_OPS"
}, {
  "purchaseOrderNumber" : "09396",
  "title" : "Angular Optimization",
  "value" : 200000,
  "date" : 1610859600000,
  "lineOfBusiness" : "ANGULAR"
}, {
  "purchaseOrderNumber" : "09397",
  "title" : "Full Stack Work",
  "value" : 110000,
  "date" : 1610946000000,
  "lineOfBusiness" : "FULL_STACK"
}, {
  "purchaseOrderNumber" : "09407",
  "title" : "Support Additional External REST services in Spring Boot",
  "value" : 350000,
  "date" : 1611118800000,
  "lineOfBusiness" : "JAVA_ENTERPRISE"
}, {
  "purchaseOrderNumber" : "09408",
  "title" : "Enhance UI for Accounting App",
  "value" : 150000,
  "date" : 1611205200000,
  "lineOfBusiness" : "ANGULAR"
} ]

And there you have it. All sales sorted by date in ascending order.

Smithers will be happy.

Wrapping It Up

That's a fairly straightforward use of unwind. But you can use it for much more sophisticated purposes.

Why don't you try it with grouping? Make it an exercise so you learn more about how to use aggregation pipelines with MongoTemplate.

And feel free to take a look at the source code when you get a chance.

Have fun!

Photo by Riccardo Bresciani from Pexels