Search code examples
node.jsmongodbaggregation-frameworkconditional-statementsprojection

MongoDB assymetrical return of data, first item in array returned in full, the rest with certain properties omitted?


I'm new to MongoDB and getting to grips with its syntax and capabilities. To achieve the functionality described in the title I believe I can create a promise that will run 2 simultaneous queries on the document - one to get the full content of one item in the array (or at least the data that is omitted in the other query, to re-add after), searched for by most recent date, the other to return the array minus specific properties. I have the following document:

{ 
  _id : ObjectId('5rtgwr6gsrtbsr6hsfbsr6bdrfyb'),
  uuid : 'something',
  mainArray : [
      {
          id : 1,
          title: 'A',
          date: 05/06/2020,
          array: ['lots','off','stuff']
      },
      {
          id : 2,
          title: 'B',
          date: 28/05/2020,
          array: ['even','more','stuff']
      },
      {
          id : 3,
          title: 'C',
          date: 27/05/2020,
          array: ['mountains','of','knowledge']
      }
  ]
}

and I would like to return

{ 
  uuid : 'something',
  mainArray : [
      {
          id : 1,
          title: 'A',
          date: 05/06/2020,
          array: ['lots','off','stuff']
      },
      {
          id : 2,
          title: 'B'
      },
      {
          id : 3,
          title: 'C'
      }
  ]
}

How valid and performant is the promise approach versus constructing one query that would achieve this? I have no idea how to perform such 'combined-rule'/conditions in MongoDB, if anyone could give an example?


Solution

  • If your subdocument array you want to omit is not very large. I would just remove it at the application side. Doing processing in MongoDB means you choose to use the compute resources of MongoDB instead of your application. Generally your application is easier and cheaper to scale, so implementation at the application layer is preferable.

    But in this exact case it's not too complex to implement it in MongoDB:

    db.collection.aggregate([
      {
        $addFields: { // keep the first element somewhere
          first: { $arrayElemAt: [ "$mainArray", 0] }
        }
      },
      {
        $project: { // remove the subdocument field
          "mainArray.array": false
        }
      },
      {
        $addFields: { // join the first element with the rest of the transformed array
          mainArray: {
            $concatArrays: [
              [ // first element
                "$first"
              ],
              { // select elements from the transformed array except the first
                $slice: ["$mainArray", 1, { $size: "$mainArray" }]
              }
            ]
          }
        }
      },
      {
        $project: { // remove the temporary first elemnt
          "first": false
        }
      }
    ])
    

    MongoDB Playground