Reputation: 2127
I'm having what I feel is a very simple problem and I just missing something so I'm hoping someone can point out where I'm going wrong. I'm trying to use the $setIsSubset operation referencing a child property that contains a list of items, but it is telling me it is null. Basically the the query is wrong, but I don't see why. Here is the output from the Mongo shell:
$ mongo setTest
MongoDB shell version: 2.6.3
connecting to: setTest
> db.items.insert({"credentials":{"authorities":["AUTH1"]}});
WriteResult({ "nInserted" : 1 })
> db.items.find();
{ "_id" : ObjectId("53acd0e214c4ee1272550de4"), "credentials" : { "authorities" : [ "AUTH1" ] } }
> var userAuthorities = ["AUTH1","AUTH2","AUTH3"];
> db.items.aggregate([
... { $redact:
... {
... $cond:
... {
... if: { $setIsSubset: ["$credentials.authorities", userAuthorities ] },
... then: "$$DESCEND",
... else: "$$PRUNE"
... }
... }
... }
... ]);
assert: command failed: {
"errmsg" : "exception: both operands of $setIsSubset must be arrays. First argument is of type: EOO",
"code" : 17310,
"ok" : 0
} : aggregate failed
Error: command failed: {
"errmsg" : "exception: both operands of $setIsSubset must be arrays. First argument is of type: EOO",
"code" : 17310,
"ok" : 0
} : aggregate failed
at Error (<anonymous>)
at doassert (src/mongo/shell/assert.js:11:14)
at Function.assert.commandWorked (src/mongo/shell/assert.js:244:5)
at DBCollection.aggregate (src/mongo/shell/collection.js:1149:12)
at (shell):1:10
2014-06-26T20:04:54.465-0600 Error: command failed: {
"errmsg" : "exception: both operands of $setIsSubset must be arrays. First argument is of type: EOO",
"code" : 17310,
"ok" : 0
} : aggregate failed at src/mongo/shell/assert.js:13
>
Can anyone see what I'm doing wrong?
Upvotes: 2
Views: 1842
Reputation: 151092
Your problem here is not with the set operators themselves but with your understanding of $redact
.
The $redact
pipeline stage recursively traverses the document to test for the field values so the problem here is that the way you are referencing the array to match is not actually present at the "current depth" all of the time.
You have a couple of ways of approaching this. Either first manipulate to make sure that an array is present at all depths using $project
:
var userAuthorities = ["AUTH1","AUTH2","AUTH3"];
db.items.aggregate([
{ "$project": {
"credentials": 1,
"authorities": { "$literal": [] }
}},
{ "$redact": {
"$cond": {
"if": { "$setIsSubset": [ "$authorities", userAuthorities ] },
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
Or test for the presence of the field and replace with an empty array in-line via $ifNull
:
db.items.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$setIsSubset": [
{ "$ifNull": [ "$authorities", { "$literal": [] } ]},
userAuthorities
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
Or finally just always accept that you are using the $$ROOT
comparison. But sort of defeats the purpose of $redact
db.items.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$setIsSubset": [
"$$ROOT.credentials.authories"
userAuthorities
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
But basically putting it, the comparison with $redact
is relative to the level of the document that is currently descended to. So your variable comparisons have to consider that as well.
Upvotes: 5