skip to Main Content

I have documents that contains a tags fields. It’s a simple array with tag names inside, no object nor _id inside.
Just plain tags like this ["Protocol", "Access", "Leverage", "Capability"].

And in my group pipeline I tried something like 'selectedTags': { $addToSet: '$tags' } but then I end up with an array containing arrays of tags. And I get the same with $push.

I tried to use $each or $pushAll but they are not supported as grouping operator as my shell tell me.

Can someone help me on this one please ?

Thank you

Edit:

Sample docs:

{
    "_id" : "HWEdDGsq86x4ikDSQ",
    "teamId" : "AdLizGnPuqbWNsFHe",
    "ownerId" : "Qb5EigWjqn2t3bfxD",
    "type" : "meeting",
    "topic" : "Grass-roots hybrid knowledge user",
    "fullname" : "Guidouil",
    "startDate" : ISODate("2017-07-30T09:00:05.513Z"),
    "shareResults" : true,
    "open" : true,
    "language" : "fr",
    "tags" : [
        "Protocol",
        "Challenge",
        "Artificial Intelligence",
        "Capability"
    ],
    "isDemo" : true,
    "createdAt" : ISODate("2017-11-15T19:24:05.513Z"),
    "participantsCount" : 10,
    "ratersCount" : 10,
    "averageRating" : 3.4,
    "hasAnswers" : true,
    "updatedAt" : ISODate("2017-11-15T19:24:05.562Z")
}
{
    "_id" : "rXvkFndpXwJ6KAvNo",
    "teamId" : "AdLizGnPuqbWNsFHe",
    "ownerId" : "Qb5EigWjqn2t3bfxD",
    "type" : "meeting",
    "topic" : "Profit-focused modular system engine",
    "fullname" : "Guidouil",
    "startDate" : ISODate("2017-07-24T12:00:05.564Z"),
    "shareResults" : true,
    "open" : true,
    "language" : "fr",
    "tags" : [
        "Initiative",
        "Artificial Intelligence",
        "Protocol",
        "Utilisation"
    ],
    "isDemo" : true,
    "createdAt" : ISODate("2017-11-15T19:24:05.564Z"),
    "participantsCount" : 33,
    "ratersCount" : 33,
    "averageRating" : 2.9393939393939394,
    "hasAnswers" : true,
    "updatedAt" : ISODate("2017-11-15T19:24:05.753Z")
}

Aggregation:

db.surveys.aggregate(
  { $match: query },
  {
    $group: {
      '_id': {
        'year': { $year: '$startDate' },
        'day': { $dayOfYear: '$startDate' },
      },
      'participants': { $sum: '$ratersCount' },
      'rating': { $avg: '$averageRating' },
      'surveys': { $push: '$_id' },
      'selectedTags': { $addToSet: '$tags' },
      'peoples': { $addToSet: '$fullname' },
    }
  },
  { $sort: { _id: 1 } }
);

then I tried to change the selectedTags to { $push: { $each: '$tags' } } or { $pushAll: '$tags' } but this does not execute 🙁

Edit 2:

In javascript I do it like that:

return Surveys.aggregate(
  { $match: query },
  { $group: {
    _id: dateGroup,
    participants: { $sum: '$ratersCount' },
    rating: { $avg: '$averageRating' },
    surveys: { $push: '$_id' },
    selectedTags: { $push: '$tags' },
    peoples: { $addToSet: '$fullname' },
  } },
  { $project: {
    _id: null,
    selectedTags: {
      $reduce: {
        input: "$selectedTags",
        initialValue: [],
        in: { $setUnion: ["$$value", "$$this"] }
      }
    },
  } }
);

3

Answers


  1. To mimic functionality of $addToSet update operator with $each modifier in aggregation pipeline you can use a combination of $push on grouping stage and $reduce + $setUnion on projection stage. E.g.:

    db.collection.aggregate([
        {$group:{
           _id: null,
           selectedTags: { $push: '$tags' }      
        }},
        {$project: {
            selectedTags: { $reduce: {
                input: "$selectedTags",
                initialValue: [],
                in: {$setUnion : ["$$value", "$$this"]}
            }}
        }}
    ])
    

    results with a single document which contains a distinct list of tags from all documents in selectedTags array.

    Login or Signup to reply.
  2. You can also use $unwind to get result:

    db.collection.aggregate([
      {$unwind: "$tags"},
      {$group:{
         _id: null,
         selectedTags: { $addToSet: '$tags' }      
      }}
    ])
    
    Login or Signup to reply.
  3. Dannyxu and Alex Beck’s answers both worked, but only partially when used with a group stage. I needed to combine both to get the desired result of a single flat array of tags:

    Model.aggregate()
            .match({ /** some query */ })
            .group({
                _id: '$teamId',
                tagsSet: { $push: '$tags' },
                numRecords: { $sum: 1 },
            })
            .project({
                _id: 0,
                numRecords: 1,
                tagsSet: {
                    $reduce: {
                        input: '$tagsSet',
                        initialValue: [],
                        in: { $setUnion: ['$$value', '$$this'] },
                    },
                },
            })
            .unwind({ path: '$tagsSet' })
            .group({
                _id: null,
                selectedTags: { $addToSet: '$tagsSet' },
                numRecords: { $sum: '$numRecords' },
            })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search