views:

43

answers:

2

Imagine I have this object (written with Ruby literals) stored in a MongoDB:

{"tags" => ["foo", "bar"], 
 "jobs" => [{"title" => "Chief Donkey Wrangler", "tags" => ["donkeys"]}] }

Now, I want to search for objects based on the tags on the first level of data, not the second. I can write a query like this (using the Ruby MongoDB library):

things.find("tags" => {"$exists" => "foo"})

This will obviously match the first example, but it will also match an example like this:

{"tags" => ["baz", "bar"],
 "jobs" => [{"title" => "Trainee Donkey Wrangler", "tags" => ["donkeys", "foo"]}] }

How do I ensure that I am searching only the top-level of keys? I'm interested in knowing the answer in both JavaScript, Ruby and in a language-agnostic way, as I'd like to use MongoDB as a cross-language store.

Obviously, I could pass a map-reduce function to the datastore to pick out the stuff I'm trying to get, but I'm interested to see if it is supported at a higher level (and to reduce the amount of time I spend writing JavaScript map-reduce functions!)

+2  A: 

Actually, the query you specify won't match your second example. To match the second example, you'd do:

things.find({"jobs.tags" => "foo"})

There's no recursive application of the query selector.

Kyle Banker
Oh, okay, that's strange. If I use the $exists filter, it does do a recursive match, but if I do it using the simpler match, it doesn't. Strange. Well, cheers for the answer.
Tom Morris
Right, the problem is that $exists works like a boolean -- either a truthy value or false. So your original query is matching any document with a "tags" key. The "foo" isn't being processed.
Kyle Banker
It seems like there's a bug in the Ruby library that is interpreting any string passed as an argument as equivalent to passing true.
David O.
+1  A: 

You're not using $exists properly. $exists does not allow you to search for a match of a field, it just checks for the existence of such a field. I'm guessing that the Ruby MongoDB library is treating your request for 'foo' as equivalent to true, b/c $exists only accepts true/false as an argument

As @kb points out, you want to use the dot notation to reach into the objects.

David O.