1

I have a collection filled with documents looking somehting like this:

{
  _id: <id>
  arrayOne: [{name: <string>, listings: [<string>, <string>, ...]}],
  arrayTwo: [{name: <string>, listings: [<string>, <string>, ...]}]
}

What i need is a flat array where each string in listings from both arrayOne and arrayTwo is represented without duplicates, so I do this:

aggregate( [
  { $match: { _id: <id>} },
  { $unwind: '$arrayOne' },
  { $unwind: '$arrayOne.listings' },
  { $unwind: '$arrayTwo' },
  { $unwind: '$arrayTwo.listings' },
  { $group : { _id : '$_id', setOne: { $addToSet: '$arrayOne.listings'}, setTwo: {$addToSet: '$arrayTwo.listings'} } },
  { $project: {unique_appearances: {$setUnion: ['$setOne', '$setTwo']}}}
] );

This works just fine until we run this on a document where arrayOne or arrayTwo is empty.

Today, I solve it by adding a fake value (before unwinding), which i filter out at the last line, like this:

aggregate( [
  { $match: { _id: <id>} },
  { $project: {
      _id: '$_id',
      arrayOne: { $cond : [{$gt: ['$arrayOne', []]}, '$arrayOne', [{listings: ['cheezeburglars']}]] },
      arrayTwo: { $cond: [{$gt: ['$arrayTwo', []]}, '$arrayTwo', [{listings: ['cheezeburglars']}]] }
    }
  },
  { $unwind: '$arrayOne' },
  { $unwind: '$arrayOne.listings' },
  { $unwind: '$arrayTwo' },
  { $unwind: '$arrayTwo.listings' },
  { $group : { _id : '$_id', setOne: { $addToSet: '$arrayOne.listings'}, setTwo: {$addToSet: '$arrayTwo.listings'} } },
  { $project: {
    unique_appearances:  {
      $setDifference: [{$setUnion: ['$setOne: ', '$setTwo']}, ['cheezeburglars']]
    }
  }}
] );

This works, but I feel that my solution is a bit hacky. Is there a better way to solve this?

1 Answer 1

4

You can do this in MongoDB 3.2+ by including the preserveNullAndEmptyArrays: true option in the relevant $unwind stages:

aggregate( [
  { $match: { _id: <id>} },
  { $unwind: { path: '$arrayOne', preserveNullAndEmptyArrays: true } },
  { $unwind: '$arrayOne.listings' },
  { $unwind: { path: '$arrayTwo', preserveNullAndEmptyArrays: true } },
  { $unwind: '$arrayTwo.listings' },
  { $group : { _id : '$_id', setOne: { $addToSet: '$arrayOne.listings'}, setTwo: {$addToSet: '$arrayTwo.listings'} } },
  { $project: {unique_appearances: {$setUnion: ['$setOne', '$setTwo']}}}
] );

Setting that option will cause the $unwind to still include the document in the output, even when the field is empty (or null or missing).

Sign up to request clarification or add additional context in comments.

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.