Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: How to apply multiple $project on aggregation

Given below is a sample document from the my_collection

{
  "_id":ObjectId("5b3f4e1013aad111501b34c4")
  "Text":["kjsdf sdfjgnks vk ekl sdsdd."],
  "Title":["lkdjf wufk"],
  "List":["3412","1244","7654","8476"],
  "__v":0
}

I want to aggregate over my_collection such that:

  1. A field "totalList" is generated which would contain the count of items in the array in the field "List".
  2. Prevent field "__v" from showing up in the result.
  3. Change the Value of the output field "List" to a join of the array
  4. Remove the trailing comma at the end of the string in "List"
  5. Cut short the string generated by the 4th point to contain only the first 50 characters in the field of "List"

So I wrote this code:

  db.my_collection.aggregate( 
      [ 
         {
           $addFields: {
             totalList: { $size: "$List" }  // I can achieve the 1st point with this
           }
         },
         { $project : { 
                          __v:0,  // I can achieve the 2nd point with this
                          List:
                          {
                            $reduce:  // I can achieve the 3rd point with this
                               {
                                 input: "$List",
                                 initialValue: "",
                                 in: { $concat : ["$$value", "$$this", ","] }
                               }
                          }
                       }
          }
      ] 
  );

However, I'm getting the following error when I run the above command:

  Failed to execute a script.

  Error:
  Assert: command failed: {
    "ok" : 0,
    "errmsg" : "Bad projection specification, cannot include fields or add computed fields during an exclusion projection: { __v: 0.0, List: { $reduce: { input: \"$List\", initialValue: \"\", in: { $concat: [ \"$$value\", \"$$this\", \",\" ] } } } }",
    "code" : 40182,
    "codeName" : "Location40182"
  } : aggregate failed
  _getErrorWithCode@src/mongo/shell/utils.js:25:13
  doassert@src/mongo/shell/assert.js:16:14
  assert.commandWorked@src/mongo/shell/assert.js:370:5
  DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1319:5
  DBCollection.prototype.aggregate@:1:355
  @(shell):1:1

  Error: command failed: {
    "ok" : 0,
    "errmsg" : "Bad projection specification, cannot include fields or add computed fields during an exclusion projection: { __v: 0.0, List: { $reduce: { input: \"$List\", initialValue: \"\", in: { $concat: [ \"$$value\", \"$$this\", \",\" ] } } } }",
    "code" : 40182,
    "codeName" : "Location40182"
  } : aggregate failed :
  _getErrorWithCode@src/mongo/shell/utils.js:25:13
  doassert@src/mongo/shell/assert.js:16:14
  assert.commandWorked@src/mongo/shell/assert.js:370:5
  DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1319:5
  DBCollection.prototype.aggregate@:1:355
  @(shell):1:1

Hence, I rewrote it to:

  db.my_collection.aggregate( 
      [ 
         {
           $addFields: {
             totalList: { $size: "$List" }  // Achieve the 1st point with this
           }
         },
         { $project : { 
                          "Text":1,
                          "Title":1,
                          __v:{     // Achieve the 2nd point with this
                              $cond: {
                                 if: { $eq: [1,1] },
                                 then: "$$REMOVE",
                                 else: 0
                              }
                          },
                          List:
                          {
                            $reduce:  // Achieve the 3rd point with this
                               {
                                 input: "$List",
                                 initialValue: "",
                                 in: { $concat : ["$$value", "$$this", ","] }
                               }
                          }
                       }
          }
      ] 
  );

Now, I could achieve the first 3 points.
However, how can I achieve the 4th and 5th point?

like image 434
Temp O'rary Avatar asked Dec 24 '22 05:12

Temp O'rary


2 Answers

You can try below aggregation

db.collection.aggregate([
  { "$addFields": {
    "totalList": { "$size": "$List" },
    "List": {
      "$substr": [
        { "$reduce": {
          "input": "$List",
          "initialValue": "",
          "in": { "$concat": ["$$value", ",", "$$this"] }
        }},
        1,
        50
      ]
    }
  }},
  { "$project": { "List": 1, "Test": 1, "Title": 1, "totalList": 1 }}
])

Output

[
  {
    "List": "3412,1244,7654,8476",
    "Title": [
      "lkdjf wufk"
    ],
    "_id": ObjectId("5b3f4e1013aad111501b34c4"),
    "totalList": 4
  }
]

Try it here

like image 97
Ashh Avatar answered Jan 12 '23 06:01

Ashh


Please try the following aggregation. I have mentioned the changes for #4 and #5 in comments below.

db.sample.aggregate( 
    [ 
     {
       $addFields: {
         totalList: { $size: "$List" } //This is for #1
       }
     },
     { $project : { 
                      "Text":1,
                      "Title":1,
                      "totalList":1,
                      __v:{     
                          $cond: {
                             if: { $eq: [1,1] }, //This is for #2
                             then: "$$REMOVE",
                             else: 0
                          }
                      },
                      List:
                      {
                        $reduce: //This is for #3
                           {
                             input: "$List",
                             initialValue: "",
                             in: {
                                   $concat: [
                                     "$$value",
                                     {
                                       $cond: { 
                                         if: { $eq: [ "$$value", "" ] }, //This is for #4
                                         then: "",
                                         else: ", "
                                       }
                                     },
                                     "$$this"
                                   ]
                                 }
                           }
                      }
                   }
      },
      {
        $project : { 
                      "Text" : 1,
                      "Title" : 1,
                      "__v" : 1,
                      "List" : { $substr: [ "$List", 0, 50 ] }, //This is for #5
                      "totalList":1
        }

      }
    ] 
);
like image 21
Subhashree Pradhan Avatar answered Jan 12 '23 06:01

Subhashree Pradhan