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 ?

3
  • possible duplicate of MongoDB extract only the selected item in array Commented Mar 27, 2014 at 12:08
  • This question has been asked and answered many times on StackOverflow already. Check out the link above and this: stackoverflow.com/questions/5562485/… for example. Commented Mar 27, 2014 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. Commented Mar 27, 2014 at 12:21

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.