21

I have the following documents:

{
  "dates": [
    1399518702000,
    1399126333000,
    1399209192000,
    1399027545000
  ],
  "dress_number": "4",
  "name": "J. Evans",
  "numbers": [
    "5982",
    "5983",
    "5984",
    "5985"
  ]
}

Is it possible unwind data from multiple arrays and get only paired elements from arrays:

{
    "dates": "1399518702000",
    "numbers": "5982"
},
{
    "dates": "1399126333000",
    "numbers": "5983"
},
{
    "dates": "1399209192000",
    "numbers": "5984"
},
{
    "dates": "1399027545000",
    "numbers": "5985"
}
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
corry
  • 1,457
  • 7
  • 32
  • 63

2 Answers2

27

From version 3.2 you can do it with $unwind on both of the arrays, $cmp the indexes, and $match only the equal indexes.

This solution will populate what you wrote in case you have only the example document. If you have more documents I don't know what you expect to get in the output, but it's solvable by grouping by _id of the document.

db.test.aggregate([
    {
        $unwind: {
            path: '$dates',
            includeArrayIndex: 'dates_index',
        }
    },
    {
        $unwind: {
            path: '$numbers',
            includeArrayIndex: 'numbers_index',
        }
    },
    {
        $project: {
            dates: 1,
            numbers: 1,
            compare: {
                $cmp: ['$dates_index', '$numbers_index']
            }
        }
    },
    {
        $match: {
            compare: 0
        }
    },
    {
        $project: {
            _id: 0,
            dates: 1,
            numbers: 1
        }
    }
])
TomG
  • 2,409
  • 4
  • 23
  • 40
  • If I understood well, it's possible to unwind a maximum of two arrays? – corry Sep 09 '16 at 14:28
  • 2
    @corry how did you understand that? you can unwind as many as you want. The problem is that each element will become an document, so for many unwinds, the result will be enormous db. it will be very hard to handle and they question is what you want to do with them. – TomG Sep 09 '16 at 19:08
  • I think it couldn't be possible to unwind as many as I want because of $cmp operator. I put a new question regarding this problem http://stackoverflow.com/questions/39426022/mongodb-show-children-items-in-one-to-many-relationship – corry Sep 10 '16 at 12:21
4

Starting in Mongo 3.4, you can use $zip to pair your array elements:

// { values: [23, 2, 14], items: ["aa", "bb", "cc"] }
db.collection.aggregate([

  { $project: { x: { $zip: { inputs: ["$values", "$items"] } } } },
  // { x: [[23, "aa"], [2, "bb"], [14, "cc"]] }

  { $unwind: "$x" },
  // { x: [23, "aa"] }
  // { x: [2,  "bb"] }
  // { x: [14, "cc"] }

  { $project: { value: { $first: "$x" }, item: { $last: "$x" } } }
])
// { value: 23, item: "aa" }
// { value: 2,  item: "bb" }
// { value: 14, item: "cc" }

Once your arrays have been $zipped, it's just a matter of $unwinding them before creating documents out of arrays ({ value: { $first: "$x" }, item: { $last: "$x" } }).

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190