Mongoose: Using $elemMatch to filter a subdocument array generates error [Error: Can't use $elemMatch with ObjectId.]

Created on 28 Mar 2015  路  11Comments  路  Source: Automattic/mongoose

Hi, I want to get all the "activities" on the "sites" that are managed by a specific user so, I've tried to query activities with the following:

            site: {
                $elemMatch: {
                    managers: { // managers is an array of ObjectId
                        $in: [req.user._id] // this is an ObjectId
                    }
                }
            }

I'm getting the error [Error: Can't use $elemMatch with ObjectId.]
Can't find much on that error on internet...
Thanks!

help

Most helpful comment

If managers is an array of ObjectId, you should just use:

Sites.find({ managers: { $in: [req.user._id] } }, callback);

You could also use the below, but not recommended because $elemMatch is only necessary if you need to match arrays of subdocs by multiple criteria.

m.findOne({
  ids: {
    $elemMatch: {
      $in: [new mongoose.Types.ObjectId('000000000000000000000001')] 
    }
  }
}, function() {});

The query you specified will assume that site is an array which contains subdocs with a "manager" key that's an _id.

All 11 comments

If managers is an array of ObjectId, you should just use:

Sites.find({ managers: { $in: [req.user._id] } }, callback);

You could also use the below, but not recommended because $elemMatch is only necessary if you need to match arrays of subdocs by multiple criteria.

m.findOne({
  ids: {
    $elemMatch: {
      $in: [new mongoose.Types.ObjectId('000000000000000000000001')] 
    }
  }
}, function() {});

The query you specified will assume that site is an array which contains subdocs with a "manager" key that's an _id.

Actually I'm not looking for "activities" (not site) where the user logged in is a "manager" of the "site" this activity belongs to. I want to filter the results for specific sub document value.

activity:
    site: ObjectId

site:
    managers: [ObjectId]

You can just use $in here, $elemmatch is redundant here because managers isn't an array of subdocs

I apologize for wasting your time, but I really can't figure out the solution. I tried to go straight as you said:

Mongoose: activities.find({ 'site.managers': { '$in': [ ObjectId("521bd88c08b986f811000001") ] }, delete: false }) { fields: { delete: 0, updated_at: 0, created_at: 0, deleted_at: 0 } }

But this query doesn't return the activities for which the the populated site is managed by the user passed in the query.... it always return empty results.

Hmm that's strange. 2 things you can do to give me some more insight:

1) Does this query work in the mongodb shell? That is, can you do db.activities.find({ 'site.managers': { '$in': [ ObjectId("521bd88c08b986f811000001") ] }, delete: false }); and get back expected results?

2) What does the site doc that you're trying to find with the above query look like?

1) dosn't return any result in the shell
2) data looks like this

// Activity
{
    "__v": 0,
    "_id": {
        "$oid": "551ecdc03ee254bc0b9eef47"
    },
    "site": {
        "$oid": "5516b924994330f404694a73"
    },
    delete: false
}

// Site
{
    "__v": 0,
    "_id": {
        "$oid": "5516b924994330f404694a73"
    },
    "managers": [{
        "$oid": "521bd88c08b986f811000001"
    }],
    "name": "Site1",
    delete: false
}

Hmm so 1) rules out this being a mongoose issue. Is the output from 2) generated by the mongodb shell? It looks like you're storing ObjectIds as extended JSON object ids rather than as actual object ids.

no sorry it is returned from my mongodb GUI, but you get the idea of what the data is.
It might not be possible to select from a collection all "parents" where the specific property "prop" of populated "child" contains the value passed...

I'm running mongodb 2.4.6, maybe that's too old

Ah ok I see what you're trying to do. It's not quite possible to query activities by properties of the corresponding site document - mongodb doesn't support anything like an SQL join. You can make site track the id of the corresponding activity, query on sites, and populate() the activity though.

@valerianrossigneux Did you resolved your problem ?

one more on the queue of this issue

Was this page helpful?
0 / 5 - 0 ratings