2

I have used the $group pipeline and inside it, I have used $addToSet which has created an array of objects which contains two values - {subGenre (string), flag (bool)}. In the next pipeline of $project, I need to run a loop through this array and I need to select only that element of the array where flag is false.

So my code looks like this:

let data = await Books.aggregate(
   [
    {
        $group: {
          _id:  genre,
          price: { $sum: "$price" },
           data: {
            $addToSet: {
              subGenre: "$subGenre",
              flag: "$flagSelectGenre"
            }
          }
        }
       }
      ]
    );

This would return documents like:

    _id: {
      genre: "suspense",
    },
   price: 10210.6,
   data: [
      {
        subGenre: "Thriller",
        flag: false,
      },
      {
        subGenre: "jumpScare",
        flag: true,
      }
      {
        subGenre: "horror",
        flag: false,
      }
    ]

After this, I need to run a $project pipeline where I have to only project that element of the data array where the flag is true. The flag will be true for only one element.

$project: {
          price: "$price",
          subGenre: {$...... } // some condition on data array??
}

The final output should look like this:

   price: 10210.6,
   subGenre: "jumpScare",

2 Answers 2

2

You can do it like this:

  • $filter - to filter items from data array, where flag property is equal to true.
  • $first - to get first item from above array.
  • $getField - to get value of subGenre property of the above item.
db.collection.aggregate([
  {
    "$project": {
      "_id": 0,
      "price": 1,
      "data": {
        "$getField": {
          "field": "subGenre",
          "input": {
            "$first": {
              "$filter": {
                "input": "$data",
                "cond": "$$this.flag"
              }
            }
          }
        }
      }
    }
  }
])

Working example

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

4 Comments

How to select that element where the flag does not exist? I am unable to use $exist with cond here. My syntax is similar to yours with difference in $filter - $filter: { input: "$data", cond: { $exists: [ "$$this.flag", false ] } } its giving error - Unrecognized expression '$exist'
$exists is not an Aggregate operator. Why didn't you use my solution? flag is Boolean anyway, so only the items where flag is true will be filtered.
I want to select the element where the flag does not exist. Actually, the real problem is the flag will be set to true if that element of the array is edited. I need to select the element which was not edited. So I need the element where flag does not exist.
But you said the opposite in the question. In that case, you can use the $type operator: "cond": { "$eq": [ { "$type": "$$this.flag"}, "bool" ] }
2

You can use $filter array operator to loop and filter elements by required conditions,

  • $filter to iterate loop of data array, if flag is true then return element
  • $let to define a variable and store the above filter result
  • $first to return the first element from the filtered result, you can also use $arrayElemAt if you are using lower version of the MongoDB
  {
    $project: {
      price: 1,
      subGenre: {
        $let: {
          vars: {
            data: {
              $filter: {
                input: "$data",
                cond: "$$this.flag"
              }
            }
          },
          in: { $first: "$$data.subGenre" }
        }
      }
    }
  }

Playground


Another approach using $indexOfArray and $arrayElemAt operators,

  • $indexOfArray will find the matching element index of the array
  • $arrayElemAt to get specific element by specifying the index of the element
  {
    $project: {
      price: 1,
      subGenre: {
        $arrayElemAt: [
          "$data.subGenre",
          { $indexOfArray: ["$data.flag", true] }
        ]
      }
    }
  }

Playground

2 Comments

Nice solution with $let!
Yeah, there are many ways to do this, yours is also a nice solution with $getField.

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.