简体   繁体   中英

Aggregating an array of arrays in MongoDB/Mongoose

I am trying to aggregrate a collection into an array of documents. This is what I want it to look like.

[
  {
    _id: ObjectId,
    points: [
      [longitude, latitude],
      [longitude, latitude],
      [longitude, latitude]
    ]
  },
  {
    _id: DifferentObjectId,
    points: [
      [longitude, latitude],
      [longitude, latitude],
      [longitude, latitude]
    ]
  }
]

Here is my model.

const mongoose = require('mongoose');

const workSchema = new mongoose.Schema({
  fieldId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Field'
  },
  dayId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Day'
  },
  workerId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Worker'
  },
  contractorId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Contractor'
  },
  ownerId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Owner'
  },
  points: [{
    point: {
      type: {
        type: String,
        default: 'Point'
      },
      // longitude, latitude
      coordinates: [Number]
    },
    category: {
      type: String,
      enum: ['entry', 'exit', 'sos', 'in', 'out']
    },
    createdAt: {
      type: Date,
      default: Date.now
    }
  }]
},
{
  timestamps: true
});

workSchema.index({ 'points.point': '2dsphere' });

module.exports = mongoose.model('Work', workSchema);

Here is my aggregation logic.

const work = await Work.aggregate([
    {
      $match: {
        contractorId: req.user.contractorId
      }
    },
    {
      $group: {
        _id: '$fieldId',
        points: {
          $push: '$points.point.coordinates'
        }
      }
    }
  ]);

This is what I end up getting. MAKE NOTE OF THE EXTRA LAYER OF ARRAY NESTING.

    [
  {
    _id: ObjectId,
    points: [
      [
        [longitude, latitude],
        [longitude, latitude],
        [longitude, latitude]
      ]
    ]
  },
  {
    _id: DifferentObjectId,
    points: [
      [
        [longitude, latitude],
        [longitude, latitude],
        [longitude, latitude]
      ]
    ]
  }
]

I have tested grouping other fields, such as ownerId, and it works exactly as expected. I assume it has something to do with the fact that I am grouping from a field present on an array of embedded objects, but I have no clue how to fix it. Any help would be greatly appreciated!

First, you need to unwind array.

const work = await Work.aggregate([{
        $match: {
            contractorId: req.user.contractorId
        }
    },
    {
        $unwind: '$points'
    },
    {
        $group: {
            _id: '$fieldId',
            points: {
                $push: '$points.point.coordinates'
            }
        }
    }
]);

Output will be something like

[{
        _id: ObjectId,
        points: [
            [longitude, latitude],
            [longitude, latitude],
            [longitude, latitude]
        ]
    },
    {
        _id: DifferentObjectId,
        points: [
            [longitude, latitude],
            [longitude, latitude],
            [longitude, latitude]
        ]
    }
]

What you tried is correct. You can just add one more stage to flatten array of array( points )

...
{
  $addFields: {
    'points': {
      $reduce: {
        input: '$points',
        initialValue: [],
        in: { $concatArrays: ["$$value", "$$this"] }
      }
    }
  }
}

See example here to flatten array, https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/#computing-a-single-reduction

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM