294

I have a problem when querying mongoDB with nested objects notation:

db.messages.find( { headers : { From: "reservations@marriott.com" } } ).count()
0
db.messages.find( { 'headers.From': "reservations@marriott.com" }  ).count()
5

I can't see what I am doing wrong. I am expecting nested object notation to return the same result as the dot notation query. Where am I wrong?

Machavity
  • 30,841
  • 27
  • 92
  • 100
Edmondo
  • 19,559
  • 13
  • 62
  • 115

4 Answers4

570

db.messages.find( { headers : { From: "reservations@marriott.com" } } )

This queries for documents where headers equals { From: ... }, i.e. contains no other fields.


db.messages.find( { 'headers.From': "reservations@marriott.com" } )

This only looks at the headers.From field, not affected by other fields contained in, or missing from, headers.


Dot-notation docs

shx2
  • 61,779
  • 13
  • 130
  • 153
  • Is there any way of doing this without the quotes around "headers.From"? – trysis May 01 '14 at 15:08
  • I don't know, just wondering, and thought it may sometimes be useful. – trysis May 02 '14 at 17:09
  • 4
    @trysis - In practice, I have found that declaring inline objects (like the examples in mongo[ose] docs, and in most examples out there) simply isn't enough in the real world. I have developed the habit of creating 'conditions' and 'fields' objects on which I can do stuff like `conditions['some.path'] = 'value'` in my business logic, then run a single query at the end: `find(conditions, fields, callback);` – Ryan Wheale May 21 '14 at 02:17
  • What if let's say I have a key which contains "domain.com", this will not work: `domains.domain.com`. Is there any workaround for this scenario (without altering the domain.com to something else e.g domain_com)? – Rens Tillmann Jun 11 '20 at 19:05
  • 2
    Answering my own comment, it's best to avoid using dots completely in your keys. In my solution I completely ditched the domains being keys, and created a slice/array instead. – Rens Tillmann Jun 11 '20 at 19:16
32

Since there is a lot of confusion about queries MongoDB collection with sub-documents, I thought its worth to explain the above answers with examples:

First I have inserted only two objects in the collection namely: message as:

> db.messages.find().pretty()
{
    "_id" : ObjectId("5cce8e417d2e7b3fe9c93c32"),
    "headers" : {
        "From" : "reservations@marriott.com"
    }
}
{
    "_id" : ObjectId("5cce8eb97d2e7b3fe9c93c33"),
    "headers" : {
        "From" : "reservations@marriott.com",
        "To" : "kprasad.iitd@gmail.com"
    }
}
>

So what is the result of query: db.messages.find({headers: {From: "reservations@marriott.com"} }).count()

It should be one because these queries for documents where headers equal to the object {From: "reservations@marriott.com"}, only i.e. contains no other fields or we should specify the entire sub-document as the value of a field.

So as per the answer from @Edmondo1984

Equality matches within sub-documents select documents if the subdocument matches exactly the specified sub-document, including the field order.

From the above statements, what is the below query result should be?

> db.messages.find({headers: {To: "kprasad.iitd@gmail.com", From: "reservations@marriott.com"}  }).count()
0

And what if we will change the order of From and To i.e same as sub-documents of second documents?

> db.messages.find({headers: {From: "reservations@marriott.com", To: "kprasad.iitd@gmail.com"}  }).count()
1

so, it matches exactly the specified sub-document, including the field order.

For using dot operator, I think it is very clear for every one. Let's see the result of below query:

> db.messages.find( { 'headers.From': "reservations@marriott.com" }  ).count()
2

I hope these explanations with the above example will make someone more clarity on find query with sub-documents.

krishna Prasad
  • 3,541
  • 1
  • 34
  • 44
  • Will it be the same if I use populate method after the find method? ` const notes = await Note2.find({"note.title": "some-title"}).populate("note")` Here I'm populating the field note and in that field I have a title that I need to use for the find method. But it returns an empty array. Why does that happen? Can you please take a look at the question regarding this issue? https://stackoverflow.com/q/73659370/14835590 – Nasem Sep 09 '22 at 09:27
30

The two query mechanism work in different ways, as suggested in the docs at the section Subdocuments:

When the field holds an embedded document (i.e, subdocument), you can either specify the entire subdocument as the value of a field, or “reach into” the subdocument using dot notation, to specify values for individual fields in the subdocument:

Equality matches within subdocuments select documents if the subdocument matches exactly the specified subdocument, including the field order.


In the following example, the query matches all documents where the value of the field producer is a subdocument that contains only the field company with the value 'ABC123' and the field address with the value '123 Street', in the exact order:

db.inventory.find( {
    producer: {
        company: 'ABC123',
        address: '123 Street'
    }
});
MasterAM
  • 16,283
  • 6
  • 45
  • 66
Edmondo
  • 19,559
  • 13
  • 62
  • 115
  • 10
    I was going crazy. This seems to me quite inconsistent, because when querying objects it's direct properties can be matched in any order. – Capaj Nov 13 '15 at 14:21
0
    //Simple and best solution complete code with example
    
    const mongoose = require("mongoose");
    mongoose
      .connect("mongodb://127.0.0.1:27017/test")
      .then(() => console.log("Connected!"));
    const blogSchema = new mongoose.Schema({
      title: String,
      author: String,
      body: String,
    
      date: { type: Date, default: Date.now },
      hidden: Boolean,
      meta: {
        votes: Number,
        favs: Number,
      },
    });
    const Bloging = mongoose.model("Blog", blogSchema);
    const createDocument = async () => {
      const doc = new Bloging({
        title: "node js",
        author: "adil",
        body: "Monngose Traning",
    
        hidden: true,
        meta: {
          votes: 1000,
          favs: 30,
        },
      });
      const doc2 = new Bloging({
        title: "vue js",
        author: "adil",
        body: "Monngose Traning",
    
        hidden: true,
        meta: {
          votes: 20,
          favs: 80,
        },
      });
      const doc3 = new Bloging({
        title: "React js",
        author: "adil",
        body: "Monngose Traning",
    
        hidden: true,
        meta: {
          votes: 30,
          favs: 60,
        },
      });
      await Bloging.insertMany([doc, doc2, doc3]); // Throws "document must have an _id before saving"
    };
    const getDocument = async () => {
      const result = await Bloging.find({ "meta.favs": { $gt: 60 } });
      console.log(result);
    };
 createDocument();
    getDocument();
   
    
        enter code here
    
    **strong text**
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 14 '23 at 02:24