Getting the size of an array within a condition without using $match

I have a query that I need to write where I need to determine the number of elements in an array (which I know can be done through $project with $size and then $match) but then I need to be able to process the array items that meet this condition, and if they don't meet the condition, leave them. I don't want to exclude the items that don't meet the criteria through a $match

Here is the standard approach to counting items in the array that cannot work for what I need:

db.collection.aggregate([
  { $project: { _id:1,  
                themeArraySize: { $gt: [ {$size: "$themes" }, 1 ] } } },
  { $match: { themeArraySize : true }} ,
])

I need to wrap that in a condition, and not exclude anything through match

The "logic" for what I want to achieve for my themes array is this:

  1. If the array has a length > 1 AND
  2. There is an array item with the value "[]" OR "NO THEMES SELECTED"
  3. THEN $PULL "[]" or "NO THEMES SELECTED" from the themes array
  4. ELSE return the themes array

The part i'm struggling with is the how to process the true logic

Putting it in a sentence:

"If there is a theme in the array that is '[]' and this is the only theme, leave it alone. But if '[]' is present, and there are other themes, remove '[]' from the array"

I've made a start:

{
            "$project": {
              "conversationid": 1,
              "themes": {
                "$cond": [
                  {
                    "$and": [
                      {
                        "$or": [
                          {
                            "$in": [
                              "[]",
                              "$themes"
                            ]
                          },
                          {
                            "$in": [
                              "NO THEMES SELECTED",
                              "$themes"
                            ]
                          }
                        ]
                      },
                      {
                        "$themes": {
                          "$gte": [
******I WANT TO SAY THAT THE THEMES ARRAY MUST BE >1 IN SIZE HERE******
                          ]
                        }
                      }
                    ]
                  },
                  {
                    "$pull": {
                      "$themes": {
                        "$in": [
                          "NO THEMES SELECTED",
                          "[]"
                        ]
                      }
                    }
                  },
                  "$themes"
                ]
              }
            }
          },

But I getting the sense that what I want to do isn't possible

Thanks

Upvotes: 2

Views: 6168

Answers (2)

All credit and many thanks to @mickl. I had to make a slight change to their answer as I previously was getting the error "The argument to $size must be an array, but was of type: missing"

To combat this I added a project stage to count the number of items in the array, then added a match to ensure that the number was greater than 0. This meant that I can only be passing an array to @mickl's solution

Full code:

{
    "$project": {
        "item": 1,
        "themes": 1,
        "conversationid": 1,
        "numberOfItemsInArray": {
            "$cond": {
                "if": {
                    "$isArray": "$themes"
                },
                "then": {
                    "$size": "$themes"
                },
                "else": 0
            }
        }
    }
},
{
    "$match": {
        "numberOfItemsInArray": {
            "$gt": 0
        }
    }
},
{
    "$addFields": {
        "themes": {
            "$cond": [
                {
                    "$eq": [
                        {
                            "$size": "$themes"
                        },
                        1
                    ]
                },
                "$themes",
                {
                    "$filter": {
                        "input": "$themes",
                        "cond": {
                            "$and": [
                                {
                                    "$ne": [
                                        "$$this",
                                        "[]"
                                    ]
                                },
                                {
                                    "$ne": [
                                        "$$this",
                                        "NO THEMES SELECTED"
                                    ]
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
},
{
    "$unwind": "$themes"
},

Upvotes: 1

mickl
mickl

Reputation: 49985

You can use $addFields to replace existing themes field, $cond to define your logic and $filter to remove some specific items from themes array, try:

db.collection.aggregate([
    {
        $addFields: {
            themes: {
                $cond: [
                    { $eq: [ { $size: "$themes"} , 1 ] },
                    "$themes",
                    {
                        $filter: {
                            input: "$themes",
                            cond: {
                                $and: [
                                    { $ne: [ "$$this", "[]" ] },
                                    { $ne: [ "$$this", "NO THEMES SELECTED" ] }
                                ]
                            }
                        }
                    }
                ]
            }
        }
    }
])

for below data:

db.collection.save({ themes: [ "something" ] })
db.collection.save({ themes: [ "[]" ] })
db.collection.save({ themes: [ "NO THEMES SELECTED" ] })
db.collection.save({ themes: [ "Something else", "NO THEMES SELECTED" ] })
db.collection.save({ themes: [ "Something else (2)", "[]" ] })

returns:

{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a6"), "themes" : [ "something" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a7"), "themes" : [ "[]" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a8"), "themes" : [ "NO THEMES SELECTED" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a9"), "themes" : [ "Something else" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6aa"), "themes" : [ "Something else (2)" ] }

Upvotes: 4

Related Questions