Alexander Zeitler
Alexander Zeitler

Reputation: 13089

Find empty object properties in MongoDb collection

How can I find all documents in a MongoDb collection where a property of the document or its sub-documents contains an empty object value {}? The name of the property is not known.

Example of which documents should be returned:

{
  data: {
      comment: {}
  }
}

As said data and comment as property names are unknown.

Upvotes: 5

Views: 3776

Answers (1)

CodeFuller
CodeFuller

Reputation: 31282

The way to iterate object properties within aggregation pipeline is $objectToArray operator, which converts a document to the array of key-value pairs. Unfortunately, it does not flatten embedded documents. Until such support is implemented, I don't see a way to accomplish your task with the pure aggregation pipeline.

However you could always use $where operator and put the logic into JavaScript code. It should recursively iterate over all document properties and check whether the value is an empty document. Here is a working sample:

db.collection.find({"$where" : function () {

    function hasEmptyProperties(doc) {

        for (var property in doc) {
            var value = doc[property];
            if (value !== null && value.constructor === Object &&
                (Object.keys(value).length === 0 || hasEmptyProperties(value))) {
                return true;
            }
        }

        return false;
    }

    return hasEmptyProperties(this);

}});

If you fill the collection with the following data:

db.collection.insert({ _id: 1, p: false });
db.collection.insert({ _id: 2, p: [] });
db.collection.insert({ _id: 3, p: null });
db.collection.insert({ _id: 4, p: new Date() });
db.collection.insert({ _id: 5, p: {} });
db.collection.insert({ _id: 6, nestedDocument: { p: "Some Value" } });
db.collection.insert({ _id: 7, nestedDocument: { p1: 1, p2: {} } });
db.collection.insert({ _id: 8, nestedDocument: { deepDocument: { p: 1 } } });
db.collection.insert({ _id: 9, nestedDocument: { deepDocument: { p: {} } } });

the query will correctly detect all documents with empty properties:

{ "_id" : 5, "p" : {  } }
{ "_id" : 7, "nestedDocument" : { "p1" : 1, "p2" : {  } } }
{ "_id" : 9, "nestedDocument" : { "deepDocument" : { "p" : {  } } } }

Just for reference, here is an aggregation pipeline based on $objectToArray which detects empty properties, however not within nested documents:

db.collection.aggregate(
[
    { "$project": {
        _id: 1,
        "properties": { "$objectToArray": "$$ROOT" }
    }},

    { "$project": {
        _id: 1,
        propertyIsEmpty: { 
            $map: {
                input: "$properties.v",
                as: "value",
                in: { $eq: ["$$value", {} ] }
            }
        }
    }},

    { "$project": {
        _id: 1,
        anyPropertyIsEmpty: { $anyElementTrue: [ "$propertyIsEmpty" ] }
    }},

    {$match : {"anyPropertyIsEmpty" : true}},

    { "$project": {
        _id: 1,
    }},
]);

Upvotes: 7

Related Questions