16

assume that i have a BlogPost model with zero-to-many embedded Comment documents. can i query for and have MongoDB return only Comment objects matching my query spec?

eg, db.blog_posts.find({"comment.submitter": "some_name"}) returns only a list of comments.

edit: an example:

import pymongo

connection = pymongo.Connection()
db = connection['dvds']

db['dvds'].insert({'title': "The Hitchhikers Guide to the Galaxy",
                   'episodes': [{'title': "Episode 1", 'desc': "..."},
                                {'title': "Episode 2", 'desc': "..."},
                                {'title': "Episode 3", 'desc': "..."},
                                {'title': "Episode 4", 'desc': "..."},
                                {'title': "Episode 5", 'desc': "..."},
                                {'title': "Episode 6", 'desc': "..."}]})

episode = db['dvds'].find_one({'episodes.title': "Episode 1"}, 
                              fields=['episodes'])

in this example, episode is:

{u'_id': ObjectId('...'),
 u'episodes': [{u'desc': u'...', u'title': u'Episode 1'},
               {u'desc': u'...', u'title': u'Episode 2'},
               {u'desc': u'...', u'title': u'Episode 3'},
               {u'desc': u'...', u'title': u'Episode 4'},
               {u'desc': u'...', u'title': u'Episode 5'},
               {u'desc': u'...', u'title': u'Episode 6'}]}

but i just want:

{u'desc': u'...', u'title': u'Episode 1'}
1
  • "The future" of the accepted answer is now the past, as aggregation framework already exists. I answered how to do it. Commented Aug 31, 2013 at 12:27

6 Answers 6

8

I think what you wanted is this:

print db.dvds.aggregate([
  {"$unwind": "$episodes"}, # One document per episode
  {"$match": {"episodes.title": "Episode 1"} }, # Selects (filters)
  {"$group": {"_id": "$_id", # Put documents together again
              "episodes": {"$push": "$episodes"},
              "title": {"$first": "$title"} # Just take any title
             }
  },
])["result"]

The output (besides the whitespaces) is:

[ { u'episodes': [ { u'title': u'Episode 1',
                     u'desc': u'...'
                   }
                 ],
    u'_id': ObjectId('51542645a0c6dc4da77a65b6'),
    u'title': u'The Hitchhikers Guide to the Galaxy'
  }
]

If you want to get rid from the u"_id", append the pipeline with:

  {"$project": {"_id": 0,
                "episodes": "$episodes",
                "title": "$title"}
               }
Sign up to request clarification or add additional context in comments.

Comments

6

This same question was asked over on the Mongo DB Google Groups page. Apparently its not currently possible but it is planned for the future.

http://groups.google.com/group/mongodb-user/browse_thread/thread/4e6f5a0bac1abccc#

Comments

3

The mongodb javascript shell is documented at http://docs.mongodb.org/manual/reference/method/

If you want to get back only specific fields of an object, you can use

db.collection.find( { }, {fieldName:true});

If, on the other hand, you are looking for objects which contain a specific field, you can sue

db.collection.find( { fieldName : { $exists : true } } );

1 Comment

i've tried to clarify my question with an example in the original post. thanks.
1

Match more simple:

db['dvd'].find_one( {'episodes.title': "Episode 1"},{'episodes.title': true} )

Query:

coll.find( criteria, fields );

Get just specific fields from the object. E.g.:

coll.find( {}, {name:true} );

http://www.mongodb.org/display/DOCS/dbshell+Reference

Comments

1

I also met the same problem. The way I do is use aggregate function. First unwind it and then match it.

db.dvds.aggregate([{$unwind:"$episodes"},{$match:{"episodes.title":"Episode 1"}}])

The results will be like

{ "_id" : ObjectId("5a129c9e6944555b122c8511"),
   "title" : "The Hitchhikers Guide to the Galaxy",
   "episodes" : { "title" : "Episode 1", "desc" : "..." } }

It's not perfect, but then you can edit it by python.

Comments

0

Look at db.eval:

You should do something like:

episode = connection['dvds'].eval('function(title){
  var t = db.dvds.findOne({"episodes.title" : title},{episodes:true});
  if (!t) return null;
  for (var i in t.episodes) if (t.episodes[i].title == title) return t.episodes[i];
}', "Episode 1");

so filtering of episodes will be on a server-side.

Comments

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.