Rob Watts
Rob Watts

Reputation: 7146

Find MongoDB documents by subdocument size

I have a collection with documents like the following:

{
    "_id" : ObjectId("..."),
    "some_key" : "some_value",
    ...,
    "another_key" : {
        "a key" : "a value",
        ...
    }
}

There are a couple different types of documents that go into this collection, with the biggest differences happening in the "another_key" field.

I would like to be able to find documents based on the size of the subdocument in "another_key". If it were an array, I could use this query:

{
    "another_key" : {
        $size : 0
    }
}

to find all documents for which it has been left empty. Unfortunately, as per the documentation this only works for arrays.

Is there a way to do this for subdocuments?

Upvotes: 3

Views: 4713

Answers (2)

Sede
Sede

Reputation: 61235

Using Object.keys to get the sub-documents keys and the $where operator.

db.collection.find( { 
    'timestamp': "your value",
    '$where': function() { return Object.keys(this.another_key).length === 0; } 
})

Upvotes: 1

Brian Noah
Brian Noah

Reputation: 2972

Nested objects aren't really subdocuments, and I don't refer to them as subdocuments, since they don't act like a document, and can't use $ operators on them. Also indexing doesn't really work on them, however we can index 'another_key.key' for better search performance.

I would suggest turn another_key into an array of objects(subdocuments) that looks like:

db.items.insert({
    some_key : 'some_value',
    another_key : [
        {key: 'akey', value: 'avalue'},
        {key: 'bkey', value: 'bvalue'}
    ]
});

You can now find by $size, and key if you want.

mongos> db.items.find({another_key: {$size: 0}})
 // 0 documents
mongos> db.items.find({another_key: {$size: 1}})
 // 0 documents
mongos> db.items.find({another_key: {$size: 2}})
 { "_id" : ObjectId("54f62a48e2fdcc95d4934424"), "some_key" : "some_value", "another_key" : [ { "key" : "akey", "value" : "avalue" }, { "key" : "bkey", "value" : "bvalue" } ] }
mongos> db.items.find({another_key: {$size: 3}})
 // 0 documents

mongos> db.collection.find({'another_key.key': 'akey'})
 { "_id" : ObjectId("54f62a48e2fdcc95d4934424"), "some_key" : "some_value", "another_key" : [ { "key" : "akey", "value" : "avalue" }, { "key" : "bkey", "value" : "bvalue" } ] }

Unfortunately you can't do comparitive $size queries:

mongos> db.items.find({another_key: {$size: {$gt:1} } } )
 error: {
     "$err" : "Can't canonicalize query: BadValue $size needs a number",
     "code" : 17287
 }

Upvotes: 1

Related Questions