1

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!

2 Answers 2

2

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]
        ]
    }
]
Sign up to request clarification or add additional context in comments.

1 Comment

Both solutions work, but this one is simpler, which is why I chose it as the answer. Thanks!
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.