on arrays of primitives

From: Diego Nieto Cid <dnietoc@xxxxxxxxx>
To: mongodb-user <mongodb-user@xxxxxxxxxxxxxxxx>
Date: Fri, 29 Apr 2016 11:07:40 -0700 (PDT)
Why ads?
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.

-- 
You received this message because you are subscribed to the Google Groups "mongodb-user"
group.

For other MongoDB technical support options, see: https://docs.mongodb.org/manual/support/
--- 
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mongodb-user+unsubscribe@xxxxxxxxxxxxxxxx.
To post to this group, send email to mongodb-user@xxxxxxxxxxxxxxxx.
Visit this group at https://groups.google.com/group/mongodb-user.
To view this discussion on the web visit https://groups.google.com/d/msgid/mongodb-user/55f934de-55c0-4040-8730-15ca32946723%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Why ads?