Kevin
Kevin

Reputation: 33

Reduce mongodb aggregation with condition

I have an array of objects call "extra" with different properties: some objects have "plus" and some haven't. I want to create inside this "extra" array, 2 different arrays one called "cheap" with all the object that don't have the "plus" property and one called "exp" with only the objects with the "plus" property. I think I can use the $reduce method in mongodb aggregate with $concatArrays and check with $cond if the property plus exists or not. Something like that:

Data example:

{
    extra: [
        {
            description: "laces",
            type: "exterior",
            plus: '200'
        },
        {
            description: "sole",
            type: "interior"
        },
        {
            description: "logo",
            type: "exterior"
        },
        {
            description: "stud",
            type: "exterior",
            plus: '450'
        }
    ],
}
 {
    $project: {
        extra: {
            $reduce: {
                input: ['$extra'],
                initialValue: {cheap: [], exp: []},
                $cond: {
                    if: {$eq: ['$$this.plus', null]},
                    then: {
                        in: {
                            cheap: {
                                $concatArrays: ['$$value.cheap', '$$this'],
                            },
                        },
                    },
                    else: {
                        in: {
                            exp: {
                                $concatArrays: ['$$value.exp', '$$this'],
                            },
                        },
                    },
                },
            },
        },
    },
}    

It doesn't work...I tried many ways or writing the $cond part without luck. I can't figure it out. Thank you all. K.

Upvotes: 1

Views: 3954

Answers (1)

Tom Slabbaert
Tom Slabbaert

Reputation: 22316

Apart from some minor syntax issues you've had another problem is your understand of the $ne operator.

In this case you expect a missing value to be equal to null, this is not how Mongo works. so for a document:

{ name: "my name" }

The aggregation query:

{ $cond: { $eq: ["$missingField", null] } }

Will not give true as you expect as missing is not equal to null. I took the liberty to fix the syntax issues you've had, this working pipeline is the way to go:

db.collection.aggregate([
  {
    $project: {
      extra: {
        $reduce: {
          input: "$extra",
          initialValue: {
            cheap: [],
            exp: []
          },
          in: {
            cheap: {
              "$concatArrays": [
                "$$value.cheap",
                {
                  $cond: [
                    "$$this.plus",
                    [],
                    [
                      "$$this"
                    ],
                    
                  ]
                }
              ]
            },
            exp: {
              "$concatArrays": [
                "$$value.exp",
                {
                  $cond: [
                    "$$this.plus",
                    [
                      "$$this"
                    ],
                    []
                  ]
                }
              ]
            }
          }
        },
        
      },
      
    },
    
  }
])

Mongo Playground

One thing to note is that $cond evaluates the plus field, meaning if the field does exist with a null value or a 0 value then it will consider this document matched for the cheap array. This is something to consider and change in case these are possible.

Upvotes: 2

Related Questions