Home > OS >  Query for only a single document based on key name in MongoDB
Query for only a single document based on key name in MongoDB

Time:10-01

I am working on a MERN project. I have created a collection in MongoDB having different types of document. Is it an accepted practice to have different structure documents in a single collection? Secondly i need to fetch only a single document from the collection using the key name. My documents are

[{
  "_id": {
    "$oid": "6333f72822dc0acc4bea17bd"
  },
  "designation": [
    {
      "name": "Chairman",
      "level": 17
    },
    {
      "name": "Director",
      "level": 13
    },
    {
      "name": "Secretary ",
      "level": 13
    },
   
    {
      "name": "Account Officer",
      "level": 9
    },
   
    {
      "name": "Data Entry Operator-GR B",
      "level": 5
    }   
 
  ]
},
{
  "_id": {
    "$oid": "6334313b22dc0acc4bea17c2"
  },
  "storeRole": ["manager", "approver", "accepter", "firstsignatory"]
},
{
  "_id": {
    "$oid": "63369d2083a7cc2e818990dd"
  },
  "designationSuffix": ["I","II", "III"]
}]

How do I get any of the three documents if I only know the key name i.e(designation, storeRole, designationSuffix). I dont want to use ID value.

CodePudding user response:

Welcome to SO. First, yes it accepted practice and indeed, a powerful feature of MongoDB to have different shapes of data in a single collection.

There are two important things to remember when querying for data:

  1. Matching on fields that don't even exist in a document is OK; the document will simply be skipped. This permits you, for example, to query for storeRole and ignore the other documents with designation, etc. -- unless of course you wish to look for those too using an $or expression.
  2. Matching (using $match) for elements in an array will return the whole array, not just the elements that match.

To illustrate this point, let's expand your input data slightly:

{"designation": [
    {"name": "Chairman","level": 17},
    {"name": "Director", "level": 13}
  ]
},
{"designation": [
    {"name": "Secretary","level": 13}
  ]
},

We will use dot notation to reach into the structures in the designation array to find those docs where at least one of the name fields is Chairman:

db.foo.aggregate([
    {$match: {"designation.name": "Chairman"}}
]);
{
    "_id" : 0,
    "designation" : [
        {
            "name" : "Chairman",
            "level" : 17
        },
        {
            "name" : "Director",
            "level" : 13
        }
    ]
}

The query eliminated the document with name = Secretary as expected but properly returned the whole document (and the whole array) where name = Chairman. Very often the goal is to fetch only the matching items in the array; this is accomplished with the $filter operator:

db.foo.aggregate([
    {$match: {"designation.name": "Chairman"}},
    {$project: {
        // Assigning the output of $filter to the same name as input:             
        designation: {$filter: {
            input: "$designation",
            as: "zz",
            cond: {$eq: ['$$zz.name','Chairman']}
        }}
    }}
]);
{
    "_id" : 0,
    "designation" : [
        {
            "name" : "Chairman",
            "level" : 17
        }
    ]
}

An alternative approach which is useful when query conditions yield null or empty arrays instead of eliminating the document altogether is to $filter first, then match only on results where the array has a length > 1. We must use the $ifNull function to protect $size from being passed a null by turning it into an empty (but not null) array:

db.foo.aggregate([
    {$project: {
        // Assigning the output of $filter to the same name as input:             
        designation: {$filter: {
            input: "$designation",
            as: "zz",
            cond: {$eq: ['$$zz.name','Chairman']}
        }}
    }},

    {$match: {$expr: {$gt:[{$size: {$ifNull:["$designation",[] ]}}, 0]}} }
]);

Try commenting out the $match to see what $filter returns when a document has the target array field but no matches vs. when the document does not have the field.

  • Related