To explain consider the following documents:
{
name: "PacMan",
players: [ { userId: "Bill" }, { userId: "Ted" }, { userId: "Death" } ]
}
{
name: "Frogger",
players: [ { userId: null }, { userId: "Bill" }, { userId: null } ]
}
{
name: "Defender",
players: [ ]
}
If you issue a query like:
>db.games.find({ "players.userId" : "Bill" })
{
name: "PacMan",
players: [ { userId: "Bill" }, { userId: "Ted" }, { userId: "Death" } ]
}
{
name: "Frogger",
players: [ { userId: null }, { userId: "Bill" }, { userId: null } ]
}
You will get the two documents you expect that has the player with that userId present in the array. But if we change that to null
>db.games.find({ "players.userId" : null })
{
name: "Frogger",
players: [ { userId: null }, { userId: "Bill" }, { userId: null } ]
}
{
name: "Defender",
players: [ ]
}
Wait a minute, you get a document you didn't expect. There are no elements in the players array. So why is it matching? Let's look at this form:
>db.games.find({ "players.userId" : {$exists: true } })
{
name: "PacMan",
players: [ { userId: "Bill" }, { userId: "Ted" }, { userId: "Death" } ]
}
{
name: "Frogger",
players: [ { userId: null }, { userId: "Bill" }, { userId: null } ]
}
Now that gives us results only where the players.userId field is actually there, and the third document will not be included as it has no items that match this condition. So finally consider the last form:
>db.games.find({ $and: [
{"players.userId" : {$exists: true }},
{"players.userId": null }
] })
{
name: "Frogger",
players: [ { userId: null }, { userId: "Bill" }, { userId: null } ]
}
This will find the result whsere there is a field present and it has a null value.
So to sum up. Even if an array does not contain any elements that would match a null value test it may still return as a result as the null condition is considered to be true as the empty items evaluates to null. If you want to exclude this case then you use $exists in order to test for the presence of the field in the first place.
null.