$addFields with $arrayElemAt and $filter returns nothing

Hey,

I have a problem with an $addFields aggregation in a pipeline. And despite it’s great though, even ChatGPT couldn’t solve it :wink:

In a pipeline, I have a document like this:

{
  "_id": {
    "$oid": "63e0c7c6acf3efb62bddb066"
  },
  "quoteID": {
    "$oid": "63d0e7af279938cefad3e1fc"
  },
  "userID": 10,
  "timestamp": 1675675642.5906088,
  "outcome": "Draw",
  "quote": {
    "_id": {
      "$oid": "63d0e7af279938cefad3e1fc"
    },
    "league": {
      "id": 78,
      "name": "Bundesliga",
      "country": "Germany",
      "logo": "https://media.api-sports.io/football/leagues/78.png",
      "flag": "https://media.api-sports.io/flags/de.svg",
      "season": 2022
    },
    "fixture": {
      "id": 871300,
      "timezone": "UTC",
      "date": "2023-01-20T19:30:00+00:00",
      "timestamp": 1674243000
    },
    "update": "2023-01-20T12:01:00+00:00",
    "bookmakers": [
      {
        "id": 6,
        "name": "Bwin",
        "bets": [
          {
            "id": 1,
            "name": "Match Winner",
            "values": [
              {
                "value": "Home",
                "odd": "3.90"
              },
              {
                "value": "Draw",
                "odd": "3.90"
              },
              {
                "value": "Away",
                "odd": "1.83"
              }
            ]
          }
        ]
      }
    ],
    "timestamp": 1674635183.38935
  },
  "match": {
    "_id": {
      "$oid": "63cfee5ef80c2c7462b89a84"
    },
    "fixture": {
      "id": 871300,
      "referee": "Daniel Siebert, Germany",
      "timezone": "UTC",
      "date": "2023-01-20T19:30:00+00:00",
      "timestamp": 1674243000,
      "periods": {
        "first": 1674243000,
        "second": 1674246600
      },
      "venue": {
        "id": 738,
        "name": "Red Bull Arena",
        "city": "Leipzig"
      },
      "status": {
        "long": "Match Finished",
        "short": "FT",
        "elapsed": 90
      }
    },
    "league": {
      "id": 78,
      "name": "Bundesliga",
      "country": "Germany",
      "logo": "https://media-3.api-sports.io/football/leagues/78.png",
      "flag": "https://media-3.api-sports.io/flags/de.svg",
      "season": 2022,
      "round": "Regular Season - 16"
    },
    "teams": {
      "home": {
        "id": 173,
        "name": "RB Leipzig",
        "logo": "https://media-3.api-sports.io/football/teams/173.png",
        "winner": null
      },
      "away": {
        "id": 157,
        "name": "Bayern Munich",
        "logo": "https://media-3.api-sports.io/football/teams/157.png",
        "winner": null
      }
    },
    "goals": {
      "home": 1,
      "away": 1
    },
    "score": {
      "halftime": {
        "home": 0,
        "away": 1
      },
      "fulltime": {
        "home": 1,
        "away": 1
      },
      "extratime": {
        "home": null,
        "away": null
      },
      "penalty": {
        "home": null,
        "away": null
      }
    },
    "timestamp": 1675673856.844521
  },
  "result": "Draw"
}

As you can imagine I want to simplify the document, thus, I want to add another field “points” that reflects the correct odd. So for example when the outcome was predicted correctly, a new field should directly show the odd of the respective outcome (in this case a Draw → points: 3.90).

This code ($addFields) returns nothing (same document as above but without the additional “points” field) even tho in the pipeline in the step before there’s a document:

{
  points: {
    $cond: {
      if: {
        $eq: ["$outcome", "$result"],
      },
      then: {
        $arrayElemAt: [
          {
            $filter: {
              input:
                "$quote.bookmakers.bets.values",
              as: "value",
              cond: {
                $eq: [
                  "$$value.value",
                  "$outcome",
                ],
              },
            },
          },
          0,
        ],
      },
      else: 0,
    },
  },
}

Hardcoding “$outcome” to “Draw” also doesn’t change it. When I use
"$quote.bookmakers.bets.values"
in the else: statement and making the if condition above to return false, it does return the correct Array (just for testing purposes to see if it can access the data).

Does anyone have an idea? I use MongoDB 6.0.3 in MongoDB Compass.

Thanks!!

Hello @Patrick01234,

Welcome to the MongoDB Community forum :sparkles:

Apologies for the late response!

Here your pipeline looks fine and after a few tweaking, it works as per your expectation and returns the desired result. Here is the pipeline for your reference:

db.test.aggregate([
  {
    $addFields: {
      points: {
        $cond: {
          if: {
            $eq: ["$outcome", "$result"],
          },
          then: {
            $first: {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $arrayElemAt: [
                        {
                          $arrayElemAt: [
                            "$quote.bookmakers.bets.values",
                            0,
                          ],
                        },
                        0,
                      ],
                    },
                    cond: {
                      $eq: [
                        "$$this.value",
                        "$outcome",
                      ],
                    },
                  },
                },
                as: "val",
                in: "$$val.odd",
              },
            },
          },
          else: 0,
        },
      },
    },
  },
])

Here I’ve used the $map and $filter operators together to extract and filter the relevant data, and then used $first to select the first element from the resulting array. Also, if the condition in the $cond operator is false, the value of the "points" field will be set to 0.

The pipeline returns the following output:

{
  "_id": {
    "$oid": "63e0c7c6acf3efb62bddb066"
  },
  "quoteID": {
    "$oid": "63d0e7af279938cefad3e1fc"
  },
  "userID": 10,
  "timestamp": 1675675642.5906088,
  "outcome": "Draw",
  "quote": {
    "_id": {
      "$oid": "63d0e7af279938cefad3e1fc"
    },
    "league": {
      "id": 78,
      "name": "Bundesliga",
      "country": "Germany",
      "logo": "https://media.api-sports.io/football/leagues/78.png",
      "flag": "https://media.api-sports.io/flags/de.svg",
      "season": 2022
    },
    "fixture": {
      "id": 871300,
      "timezone": "UTC",
      "date": "2023-01-20T19:30:00+00:00",
      "timestamp": 1674243000
    },
    "update": "2023-01-20T12:01:00+00:00",
    "bookmakers": [
      {
        "id": 6,
        "name": "Bwin",
        "bets": [
          {
            "id": 1,
            "name": "Match Winner",
            "values": [
              {
                "value": "Home",
                "odd": "3.90"
              },
              {
                "value": "Draw",
                "odd": "3.90"
              },
              {
                "value": "Away",
                "odd": "1.83"
              }
            ]
          }
        ]
      }
    ],
    "timestamp": 1674635183.38935
  },
  "match": {
    "_id": {
      "$oid": "63cfee5ef80c2c7462b89a84"
    },
    "fixture": {
      "id": 871300,
      "referee": "Daniel Siebert, Germany",
      "timezone": "UTC",
      "date": "2023-01-20T19:30:00+00:00",
      "timestamp": 1674243000,
      "periods": {
        "first": 1674243000,
        "second": 1674246600
      },
      "venue": {
        "id": 738,
        "name": "Red Bull Arena",
        "city": "Leipzig"
      },
      "status": {
        "long": "Match Finished",
        "short": "FT",
        "elapsed": 90
      }
    },
    "league": {
      "id": 78,
      "name": "Bundesliga",
      "country": "Germany",
      "logo": "https://media-3.api-sports.io/football/leagues/78.png",
      "flag": "https://media-3.api-sports.io/flags/de.svg",
      "season": 2022,
      "round": "Regular Season - 16"
    },
    "teams": {
      "home": {
        "id": 173,
        "name": "RB Leipzig",
        "logo": "https://media-3.api-sports.io/football/teams/173.png",
        "winner": null
      },
      "away": {
        "id": 157,
        "name": "Bayern Munich",
        "logo": "https://media-3.api-sports.io/football/teams/157.png",
        "winner": null
      }
    },
    "goals": {
      "home": 1,
      "away": 1
    },
    "score": {
      "halftime": {
        "home": 0,
        "away": 1
      },
      "fulltime": {
        "home": 1,
        "away": 1
      },
      "extratime": {
        "home": null,
        "away": null
      },
      "penalty": {
        "home": null,
        "away": null
      }
    },
    "timestamp": 1675673856.844521
  },
  "result": "Draw",
  "points": "3.90"
}

I hope it helps!

Best,
Kushagra