0

Inspired by another question I was looking for a common way to couple items in a nested array, so the 1st item will be coupled with the 2nd item, and the 3rd item will be coupled with the 4th item.

Assuming my document looks like:

{
    _id: ObjectId("5a934e000102030405000000"),
    events: [
      {
        status: 0,
        timestamp: ISODate("2022-05-29T13:26:00Z")
      },
      {
        status: 8,
        timestamp: ISODate("2022-05-29T14:41:00Z")
      },
      {
        status: 4,
        timestamp: ISODate("2022-05-31T10:13:00Z")
      },
      {
        status: 3,
        timestamp: ISODate("2022-05-31T10:18:00Z")
      }
    ]
  }

And I want to couple the items:

  {
    _id: ObjectId("5a934e000102030405000000"),
    couples: [
      [
        {
          mod: 0,
          status: 0,
          timestamp: ISODate("2022-05-29T13:26:00Z")
        },
        {
          mod: 1,
          status: 8,
          timestamp: ISODate("2022-05-29T14:41:00Z")
        }
      ],
      [
        {
          mod: 0,
          status: 4,
          timestamp: ISODate("2022-05-31T10:13:00Z")
        },
        {
          mod: 1,
          status: 3,
          timestamp: ISODate("2022-05-31T10:18:00Z")
        }
      ]
    ]
  }
nimrod serok
  • 14,151
  • 2
  • 11
  • 33

1 Answers1

0

Since mongoDB version 4.4*, One option is to use an aggregation pipeline with $reduce, $mod, $filter and $zip:

  1. $reduce with $mod to add a new mod field to each item, with value 0 to each odd index (1, 3, 5,...) and value 1 to each even index (2, 4, 6,...)
  2. $fiter into two arrays according to the mod value
  3. $zip these two arrays to one array of couples
db.collection.aggregate([
  {
    $project: {
      events: {
        $reduce: {
          input: "$events",
          initialValue: [],
          in: {$concatArrays: [
              "$$value",
              [
                {
                  timestamp: "$$this.timestamp",
                  status: "$$this.status",
                  mod: {$mod: [{$size: "$$value"}, 2]}
                }
              ]
            ]
          }
        }
      }
    }
  },
  {
    $project: {
      firstEvent: {$filter: {input: "$events", cond: {$eq: ["$$this.mod", 0]}}},
      secondEvent: {$filter: {input: "$events", cond: {$eq: ["$$this.mod", 1]}}}
    }
  },
  {$project: {couples: {$zip: {inputs: ["$firstEvent", "$secondEvent"]}}}}
])

See how it works on the playground example

*With older mongoDB versions, 3.4 or higher, the $mod can be replaces with a "manual" mod calculation.

nimrod serok
  • 14,151
  • 2
  • 11
  • 33