MongoDB $filter

In MongoDB, the $filter aggregation pipeline operator returns a subset of an array based on a specified condition.

The $filter operator returns an array with only those elements that match the condition, in their original order.

Syntax

The syntax goes like this:

{ $filter: { input: <array>, as: <string>, cond: <expression> } }

Each field is as explained below.

FieldSpecification
inputAn expression that resolves to an array.
asThis is an optional field. It specifies a name for the variable that represents each individual element of the input array. If no name is specified (i.e. if you omit this field), the variable name defaults to this.
condAn expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as.

Example

Suppose we have a collection called players with the following documents

{ "_id" : 1, "player" : "Homer", "scores" : [ 1, 5, 3 ] }
{ "_id" : 2, "player" : "Marge", "scores" : [ 8, 17, 18 ] }
{ "_id" : 3, "player" : "Bart", "scores" : [ 15, 11, 8 ] }

Here’s an example of applying the $filter operator to filter the array elements in the scores field:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 1, 2, 3 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$scores",
              as: "score",
              cond: { $gt: [ "$$score", 10 ] }
          }
        }
    }
  }
])

Result:

{ "_id" : 1, "highScores" : [ ] }
{ "_id" : 2, "highScores" : [ 17, 18 ] }
{ "_id" : 3, "highScores" : [ 15, 11 ] }

In this example, we filtered the arrays to just those elements that have a value greater than 10. Only those values are returned.

Any values that are less than 10 are omitted from the result. In the case of the first document, this results in an empty array.

Here, we used the as field to name the return variable score. We then referred to that variable in the cond field using $$score. As mentioned, you can omit the as field, and then refer to the return variable using $$this. More on this later.

Empty Arrays

If the array is empty, then an empty array is returned.

Suppose we have the following document in our collection:

{ "_id" : 4, "player" : "Farnsworth", "scores" : [ ] }

Here’s what happens when we apply $filter to that array:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 4 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$scores",
              as: "score",
              cond: { $gt: [ "$$score", 10 ] }
          }
        }
    }
  }
])

Result:

{ "_id" : 4, "highScores" : [ ] }

Wrong Type

Applying $filter to a field that doesn’t contain an array returns an error.

Example:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 4 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$player",
              as: "player",
              cond: { $gt: [ "$$player", 10 ] }
          }
        }
    }
  }
])

Result:

Error: command failed: {
	"ok" : 0,
	"errmsg" : "input to $filter must be an array not string",
	"code" : 28651,
	"codeName" : "Location28651"
} : aggregate failed :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
doassert@src/mongo/shell/assert.js:18:14
_assertCommandWorked@src/mongo/shell/assert.js:618:17
assert.commandWorked@src/mongo/shell/assert.js:708:16
DB.prototype._runAggregate@src/mongo/shell/db.js:266:5
DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1046:12
@(shell):1:1

Null Values

If the field contains null instead of an array, the result is null.

Imagine we have the following document in the collection:

{ "_id" : 5, "player" : "Meg", "scores" : null }

Here’s what happens when we apply $filter to the scores field:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 5 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$scores",
              as: "score",
              cond: { $gt: [ "$$score", 10 ] }
          }
        }
    }
  }
])

Result:

{ "_id" : 5, "highScores" : null }

Non-Existent Field

Applying $filter to a field that doesn’t exist results in null being returned.

Example:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 5 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$name",
              as: "name",
              cond: { $gt: [ "$$name", 10 ] }
          }
        }
    }
  }
])

Result:

{ "_id" : 5, "highScores" : null }

The Variable Name is Optional

In the previous examples, I use the as field to assign a name to the variable.

As mentioned, the as field is optional. If you omit this field, the variable name defaults to this.

Here’s an example:

db.players.aggregate([
  { 
    $match: { _id: { $in: [ 1, 2, 3 ] } } 
  },
  {
    $project: {
        highScores: {
          $filter: {
              input: "$scores",
              cond: { $gt: [ "$$this", 10 ] }
          }
        }
    }
  }
])

Result:

{ "_id" : 1, "highScores" : [ ] }
{ "_id" : 2, "highScores" : [ 17, 18 ] }
{ "_id" : 3, "highScores" : [ 15, 11 ] }

This is the same as the first example, except in this example, we omit the as field, and therefore refer to the variable using $$this.