1

My mongodb collection looks like this:

 {
        "_id" : ObjectId("5333bf6b2988dc2230c9c924"),
        "name" : "Mongo2",
        "notes" : [
                {
                    "title" : "mongodb1",
                    "content" : "mongo content1"
                },
                {
                    "title" : "replicaset1",
                    "content" : "replca content1"
                }
        ]
    }
    {
        "_id" : ObjectId("5333fd402988dc2230c9c925"),
        "name" : "Mongo2",
        "notes" : [
                {
                    "title" : "mongodb2",
                    "content" : "mongo content2"
                },
                    {
                            "title" : "replicaset1",
                            "content" : "replca content1"
                    },
                    {
                            "title" : "mongodb2",
                            "content" : "mongo content3"
                    }
            ]
    }

I want to query only notes that have the title "mongodb2" but do not want the complete document. I am using the following query:

 > db.test.find({ 'notes.title': 'mongodb2' }, {'notes.$': 1}).pretty()
    {
            "_id" : ObjectId("5333fd402988dc2230c9c925"),
            "notes" : [
                    {
                            "title" : "mongodb2",
                            "content" : "mongo bakwas2"
                    }
            ]
    }

I was expecting it to return both notes that have title "mongodb2". Does mongo return only the first document when we query for a document within a document ?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
jay
  • 43
  • 3
  • possible duplicate of [MongoDB extract only the selected item in array](http://stackoverflow.com/questions/3985214/mongodb-extract-only-the-selected-item-in-array) – WiredPrairie Mar 27 '14 at 12:08
  • This question has been asked and answered many times on StackOverflow already. Check out the link above and this: http://stackoverflow.com/questions/5562485/get-particular-element-from-mongodb-array for example. – WiredPrairie Mar 27 '14 at 12:09
  • I find it hard to follow those questions, because they are from a time where the aggregation framework didn't exist. The accepted answer to one of those questions is even to use map/reduce, which is almost certainly not what the OP wants because M/R is not suited for ad-hoc queries. – mnemosyn Mar 27 '14 at 12:21

2 Answers2

1

$ always refers to the first match, as does the $elemMatch projection operator.

I think you have three options:

  1. separate the notes so each is a document of its own
  2. accept sending more data over the network and filter client-side
  3. use the aggregation pipeline ($match and $project)

I'd probably choose option 1, but you probably have a reason for your data model.

mnemosyn
  • 45,391
  • 6
  • 76
  • 82
  • I did not separate notes as a separate document as all notes have few common values that would be repeated. If I am to choose between option 2 and 3, not sure which one would have better performance. Also need to think about the implementation as am using Spring-data repositories. – jay Mar 27 '14 at 12:15
1

The positional $ operator can only return the first match index that it finds.

Using aggregate:

db.test.aggregate([

    // Match only the valid documents to narrow down
    { "$match": { "notes.title": "mongodb2" } },

    // Unwind the array
    { "$unwind": "$notes" },

    // Filter just the array
    { "$match": { "notes.title": "mongodb2" } },

    // Reform via group
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "notes": { "$push": "$notes" }
    }}       
])

So you can use this to "filter" specific documents from the array.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317