Reputation: 2529
Let's say I have a collection with accounts that each account has array of users:
{
_id: 1,
users: [
{
firstName: a,
lastName: b
},
{
firstName: c,
lastName: c
}
]
}
I want to query all accounts where at least one user has the same firstName
and lastName
I tried to do something like this:
db.accounts.find({users: {
$elemMatch: {
$where: "this.firstName == this.lastName"
}
}})
But of course it didn't work because of this error:
Can't canonicalize query: BadValue $elemMatch cannot contain $where expression
I want to use the find
and not aggregate
.
Is there some other way to do it?
Upvotes: 4
Views: 2006
Reputation: 50406
Bit out of the box, but since $where
relies on JavaScript evaluation, then this aggregate with $redact
should actually run a lot faster since it uses native operators:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": "$users",
"as": "user",
"in": { "$eq": [ "$$user.firstName", "$$user.lastName" ] }
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
So the $anyElementTrue
combined with $map
basically works the same as JavaScript .some()
here, in that if any of the elements actually had the same "firstName"
as "lastName"
, then this is a true
match and the document is "kept". Otherwise it is "pruned" and discarded.
So with the native operator speed, I would go with that over $where
evaulation. I know you say you don't want to, but you possibly did not consider the statement was this simple or this much faster than the alternative either.
Upvotes: 2
Reputation: 48396
Please try to use $where
with Array.some
as below.
> db.collection.find({
$where: function() {
return this.users.some(function(obj){
return obj.firstName == obj.lastName;
})
}})
Upvotes: 4
Reputation: 37048
The documentation is quite clear about it:
You cannot specify a $where expression as a query criterion for $elemMatch.
What you can do, is iterate the array within $where
:
db.collection.find(function(){
for(var i in this.users) {
if(this.users[i].firstName == this.users[i].lastName) {
return true;
}
}
})
Upvotes: 0