In MongoDB, the $setEquals
aggregation pipeline operator compares two or more arrays and returns true
if they have the same distinct elements and false
otherwise.
$setEquals
accepts two or more arguments, all of which can be any valid expression as long as they each resolve to an array. $setEquals
treats the arrays as sets.
Example
Suppose we have a collection called data
with the following documents:
{ "_id" : 1, "a" : [ 1, 2, 3 ], "b" : [ 1, 2, 3 ] } { "_id" : 2, "a" : [ 1, 2, 3 ], "b" : [ 1, 2 ] } { "_id" : 3, "a" : [ 1, 2 ], "b" : [ 1, 2, 3 ] } { "_id" : 4, "a" : [ 1, 2, 3 ], "b" : [ 3, 4, 5 ] } { "_id" : 5, "a" : [ 1, 2, 3 ], "b" : [ 4, 5, 6 ] }
We can apply the $setEquals
operator against the a
and b
fields in those documents.
Example:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3, 4, 5 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
{ "a" : [ 1, 2, 3 ], "b" : [ 1, 2, 3 ], "result" : true } { "a" : [ 1, 2, 3 ], "b" : [ 1, 2 ], "result" : false } { "a" : [ 1, 2 ], "b" : [ 1, 2, 3 ], "result" : false } { "a" : [ 1, 2, 3 ], "b" : [ 3, 4, 5 ], "result" : false } { "a" : [ 1, 2, 3 ], "b" : [ 4, 5, 6 ], "result" : false }
Nested Arrays
The $setEquals
operator does not descend into any nested arrays. It only evaluates top-level arrays.
Suppose our collection also contains the following documents:
{ "_id" : 6, "a" : [ 1, 2, 3 ], "b" : [ [ 1, 2, 3 ] ] } { "_id" : 7, "a" : [ 1, 2, 3 ], "b" : [ [ 1, 2 ], 3 ] }
And we apply $setEquals
to those two documents:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 6, 7 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
{ "a" : [ 1, 2, 3 ], "b" : [ [ 1, 2, 3 ] ], "result" : false } { "a" : [ 1, 2, 3 ], "b" : [ [ 1, 2 ], 3 ], "result" : false }
In the first document, the b
field contained an array that contained just one element – another array. In this case, The outer array was evaluated, and it was found to not contain the same values that were in the the array at a
.
However, if the a
field had contained a nested array itself, it might have been a different story.
Suppose we have the following documents:
{ "_id" : 8, "a" : [ [ 1, 2, 3 ] ], "b" : [ [ 1, 2, 3 ] ] } { "_id" : 9, "a" : [ [ 1, 2, 3 ] ], "b" : [ [ 1, 2 ], 3 ] }
And we apply $setEquals
to those documents:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 8, 9 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
{ "a" : [ [ 1, 2, 3 ] ], "b" : [ [ 1, 2, 3 ] ], "result" : true } { "a" : [ [ 1, 2, 3 ] ], "b" : [ [ 1, 2 ], 3 ], "result" : false }
In the first document, a
matches b
exactly, and so the result is true
.
In the second document, the nested array at a
is different to the nested array at b
, and so we get false
.
Missing Fields
Applying $setEquals
to a non-existent field results in an error.
Consider the following documents:
{ "_id" : 10, "a" : [ 1, 2, 3 ] } { "_id" : 11, "b" : [ 1, 2, 3 ] } { "_id" : 12 }
The first document doesn’t have a b
field, the second document doesn’t have an a
field, and the third document doesn’t have either.
Here’s what happens when we apply $setEquals
to the a
and b
fields:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 10, 11, 12 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
Error: command failed: { "ok" : 0, "errmsg" : "All operands of $setEquals must be arrays. One argument is of type: missing", "code" : 17044, "codeName" : "Location17044" } : aggregate failed : _getErrorWithCode@src/mongo/shell/utils.js:25:13 doassert@src/mongo/shell/assert.js:18:14 _assertCommandWorked@src/mongo/shell/assert.js:639:17 assert.commandWorked@src/mongo/shell/assert.js:729:16 DB.prototype._runAggregate@src/mongo/shell/db.js:266:5 DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1058:12 @(shell):1:1
As the message states, all operands must be arrays. A missing argument/field is not an array.
Wrong Data Type
As seen in the previous example, all operands of $setEquals
must be arrays. If the field they refer to is missing, an error is thrown. The same error occurs when the operand is not missing, but is simply the wrong type.
Suppose our collection contains the following documents:
{ "_id" : 13, "a" : [ 1, 2, 3 ], "b" : 3 } { "_id" : 14, "a" : 3, "b" : [ 1, 2, 3 ] } { "_id" : 15, "a" : 2, "b" : 3 }
And we apply $setEquals
to those documents:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 13, 14, 15 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
Error: command failed: { "ok" : 0, "errmsg" : "All operands of $setEquals must be arrays. One argument is of type: double", "code" : 17044, "codeName" : "Location17044" } : aggregate failed : _getErrorWithCode@src/mongo/shell/utils.js:25:13 doassert@src/mongo/shell/assert.js:18:14 _assertCommandWorked@src/mongo/shell/assert.js:639:17 assert.commandWorked@src/mongo/shell/assert.js:729:16 DB.prototype._runAggregate@src/mongo/shell/db.js:266:5 DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1058:12 @(shell):1:1
Duplicate Values
The $setEquals
operator ignores duplicate entries. It also ignores the order of the elements.
Suppose we have the following documents:
{ "_id" : 16, "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ 1, 2, 3 ] } { "_id" : 17, "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ 1, 2 ] } { "_id" : 18, "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ ] } { "_id" : 19, "a" : [ 3, 2, 1, 2, 3, 1 ], "b" : [ 2, 3, 1 ] } { "_id" : 20, "a" : [ 1, 3, 2, 2, 3, 1 ], "b" : [ 2, 1 ] } { "_id" : 21, "a" : [ 2, 3, 1, 2, 3, 1 ], "b" : [ ] }
Then we apply the $setEquals
operator to them:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 16, 17, 18, 19, 20, 21 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
result: { $setEquals: [ "$a", "$b" ] }
}
}
]
)
Result:
{ "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ 1, 2, 3 ], "result" : true } { "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ 1, 2 ], "result" : false } { "a" : [ 1, 1, 2, 2, 3, 3 ], "b" : [ ], "result" : false } { "a" : [ 3, 2, 1, 2, 3, 1 ], "b" : [ 2, 3, 1 ], "result" : true } { "a" : [ 1, 3, 2, 2, 3, 1 ], "b" : [ 2, 1 ], "result" : false } { "a" : [ 2, 3, 1, 2, 3, 1 ], "b" : [ ], "result" : false }
More than Two Arguments
As mentioned, $setEquals
accepts two or more arguments. In all cases, the arguments must have the same distinct values in order to return true
. Otherwise the result will be false
.
Suppose we have the following documents:
{ "_id" : 22, "a" : [ 1, 2 ], "b" : [ 1, 2 ], "c" : [ 1, 2 ] } { "_id" : 23, "a" : [ 1, 2 ], "b" : [ 1, 2 ], "c" : [ 1, 2, 3 ] }
These documents have an extra field – a c
field.
Now let’s apply $setEquals
to those three fields:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 22, 23 ] } } },
{
$project:
{
_id: 0,
a: 1,
b: 1,
c: 1,
result: { $setEquals: [ "$a", "$b", "$c" ] }
}
}
]
)
Result:
{ "a" : [ 1, 2 ], "b" : [ 1, 2 ], "c" : [ 1, 2 ], "result" : true } { "a" : [ 1, 2 ], "b" : [ 1, 2 ], "c" : [ 1, 2, 3 ], "result" : false }