$elemMatch on arrays of primitives

56 views
Skip to first unread message

Diego Nieto Cid

unread,
Apr 29, 2016, 3:57:15 PM4/29/16
to mongodb-user
Hello

I've got a question about the $elemMatch projection when applied to arrays of primitive elements.

First of all, I create the "people" collection:

> db.createCollection("people")
{ "ok" : 1 }

Each record of the collection will hold the name of the person and an array (actually a set) of fruits he/she likes.
So, I insert one sample record:

> db.people.insert({name: "John", likes:["pears", "apples"]})
WriteResult({ "nInserted" : 1 })

Now I'd like to query the database for the fact that a person exists (by name) and, as a side-effect[1], whether he likes a given fruit or not, without returning the whole likes array to client code.

For example, if my inputs are "John" and "pears" (name and fruit, respectively) I'd like the database to output

{ _id: ObjectId(...),
  name
: "John",
  likes
: ["pears"] }

but if my inputs were "John" and "oranges" it should return

{ _id: ObjectId(...),
  name
: "John",
  likes
: [] }

instead (or maybe without the likes property, it doesn't matter actually).

In the documentation I found the $elemMatch projection which seems to be useful for this case.

Thus, the query I came up with is the following:

> db.people.find({name: "John"}, {name: 1, likes: {$elemMatch: "apples"}})

Unfortunately, when run in the console the following error is generated:

error: {
 
"$err" : "Can't canonicalize query: BadValue elemMatch: Invalid argument, object required.",
 
"code" : 17287
}

I know that if the "likes" field were an array of objects as in the following example:

> db.people.remove({})
WriteResult({ "nRemoved" : 1 })
> db.people.insert({name: "John", likes:[{fruit:"pears"}, {fruit:"apples"}]})
WriteResult({ "nInserted" : 1 })

then the adjusted query would work fine:

> db.people.find({name: "John"}, {name: 1, likes: {$elemMatch: {fruit:"apples"}}})
{ "_id" : ObjectId("57239ae85efde9605cb2b27b"), "name" : "John", "likes" : [ { "fruit" : "apples" } ] }
> db.people.find({name: "John"}, {name: 1, likes: {$elemMatch: {fruit:"oranges"}}})
{ "_id" : ObjectId("57239ae85efde9605cb2b27b"), "name" : "John" }

However, I'm curious about the simpler version of the "likes" field. :)
Should $elemMatch not working with arrays of primitives be considered a bug? Or is such behaviour a deliberate design decision?

I could run two queries to learn the facts I want, as follows:

> db.people.find({name: "John"}, {name: 1})
{_id: ....}
> db.people.find({$and: [{_id: <returned id>}, {likes: "pears"}]}, {name: 1})
{_id: ....}

but I believe I'll switch to an array of objects for now, as it allows me to do what I want in just one query.

What other options would you consider?

Thanks,
Diego

----

[1] What I mean by side-effect is that I don't want the "and" of the two conditions, Mongo should return the document for the person, regardless of its tastes, in such
a way I can learn if the person exists and if that is the case whether he likes some fruit.
Reply all
Reply to author
Forward
0 new messages