Get the size of an array if the input array's element match atleast one

Viewed 51

I want to count the views of videos. I have the following structure.

{
    _id: ObjectId("some_id")
    views:[
        { userId:1, groupId:[1,2,4] },
        { userId:2, groupId:[2,4] },
        { userId:1, groupId:[3,5] }
    ]
}

I just need to count if both arrays (views.groupId and input array) have common elements, increase the count by one. If the input array is is [1,3,5] then the no of views is 2. (first object has element 1 and third object has elements 3,5)

I wrote the following code, but it gives wrong answer.

{
    $project: {
        views: {
            $size: {
                $ifNull: [{
                        $filter: {
                            input: '$views',
                            as: 'views',
                            cond: {
                                $setIntersection: [
                                    '$$views.groupId',
                                    [1,3,5]
                                ]
                            }
                        }
                    },
                    []
                ]
            }
        }
    }
}

I spent more time to solve it by myself, but no luck today. Thanks in advance

2 Answers

The approach with $setIntersection and $size is correct but since you want to transform an array into a single value it's better to use $reduce. Additionally the usage of $min ensures that the value added in each iteration will by 1 or 0 (1 at most):

db.collection.aggregate([
    {
        $project: {
            views: {
                $reduce: {
                    input: "$views",
                    initialValue: 0,
                    in: {
                        $add: [
                            "$$value",
                            {
                                $min: [ 1, { $size: { $setIntersection: [ "$$this.groupId", [ 1,3,5 ] ]  } } ]
                            }
                        ]
                    }
                }
            }
        }
    }
])

Mongo Playground

You need to define a valid condition to the $filter operator.

In your case, if you return the Number value in the cond parameter, it will convert it into Boolean with this convention:

db.collection.aggregate([
  {
    $project: {
      views: {
        $size: {
          $ifNull: [
            {
              $filter: {
                input: "$views",
                as: "view",
                cond: {
                  $size: {
                    $setIntersection: [
                      "$$view.groupId",
                      [ 1, 3, 5]
                    ]
                  }
                }
              }
            },
            []
          ]
        }
      }
    }
  }
])

MongoPlayground | Alternative $gt

Related