0

I have a collection with four documents

{_id:as1d2a1das2d1afdfd, content:{ name: 'John'} }
{_id:sdasd512a12asdad, content:{ sub: {name:'John'}}}
{_id:sdaasddasdassd512a12, content:{ sub: {sub1:{name:'John'}}}}
{_id:sdaasddasdassd512a12, content:{ sub: {sub1:{name:'Terry'}}}}

Which query can I use to find all document with an element that has a name attribute equal to 'John' ?

karel
  • 5,489
  • 46
  • 45
  • 50
Ryo
  • 995
  • 2
  • 25
  • 41
  • 1
    This situation should prompt you to reconsider the way in which you store your documents. I do not believe there is an easy "query only" method to overcome this. I'll be happy to be proved wrong though :) – Lix Dec 09 '13 at 08:27
  • Yes,there is only demo for which i want ,certainly data's structure is not like that – Ryo Dec 09 '13 at 08:33
  • What will happen if there is more than one name attribute? – Lix Dec 09 '13 at 08:53
  • If you haven't already, I'd suggest you read this http://docs.mongodb.org/manual/applications/data-models-tree-structures/ to see some suggestions for modeling tree structures. Basically, your query will always be slow or require lots of unnecessary indexing that will add significant overhead and wouldn't support structures of all sizes. – WiredPrairie Dec 09 '13 at 11:58

2 Answers2

0

Right now MongoDB find() is limited to one document level, you can extended this functionality by your own iteration using cursor and applying programming logic to achieve the desired solution.

find more info at:

The Mighty Programmer
  • 1,242
  • 12
  • 23
  • `"limited to one sub document ... level"`? What about `find({'content.sub.sub1.name':'Terry'})`? That is a valid query and it drills down into 2 sub documents... – Lix Dec 09 '13 at 08:56
  • correction: `one document level` means you have to use reference of level 1 to drill down. – The Mighty Programmer Dec 09 '13 at 09:21
0

To do this, you would have to iterate over each item that your cursor returns and traverse each document "manually".

This is not a mongo query! Note that my actual query is an empty find().

All I'm doing here is iterating over results and filtering out results that are not relevant.

var results = [];
db.data.find().forEach( function( doc ){
  if ( find_name_recursive( doc ) ){
      results.push( doc );
  }
});

function find_name_recursive( doc ) {
  for ( var attr in doc ) {
    if ( doc.name != "undefined" && doc.name == "John" ){
      return true;
    }
    if ( doc[ attr ] !== null && typeof( doc[ attr ] ) == "object" ) {
      // drill down deeper into the object
      find_name_recursive( doc[ attr ] );
    }
  }
}

Note that this is not tested code (don't have mongo on this machine). The majority of the recursive search function was taken from this post - Traverse all the Nodes of a JSON Object Tree with JavaScript


References -

Community
  • 1
  • 1
Lix
  • 47,311
  • 12
  • 103
  • 131
  • As this would require a complete collection scan and retrieval of every document, it's not a particularly efficient solution. – WiredPrairie Dec 09 '13 at 12:02
  • Thanks for your support , i've test your code with my data at here http://jsfiddle.net/KS9kH/29/ But i have problem ,the snipt code return find_recursive( doc[ attr ][i] ); in for loop seem not looping with all doc[ attr ] element, it only do with first element of doc[attr] array and return ,please help me correct it,thanks :) – Ryo Dec 10 '13 at 01:41