Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongo $ne query with an array not working as expected

Tags:

mongodb

I'm trying to run the following queries with the data inserted as follows. For whatever reason the $ne with 0 in the value doesn't seem to work. I tried this on both linux and mac using v2.0.4. Also ran these using the mongo shell.

Anybody have any ideas? Is this a bug or something I'm misunderstanding?

db.associated.insert({
    "diskinfo" : {
            "physical" : [
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 509"}
            ]
    }})

db.associated.insert({
    "diskinfo" : {
            "physical" : [
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 5"},
                    {"merror_count" : "Count: 0"}
            ]
    }})

db.associated.insert({
    "diskinfo" : {
            "physical" : [
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 0"},
                    {"merror_count" : "Count: 0"}
            ]
    }})

ran these queries on the mongo shell. and got the results in the comments

db.associated.find( { "diskinfo.physical.merror_count" : { $ne : 'Count: 0'}}).count()
//  Result: 0, Expected: 2
db.associated.find( { "diskinfo.physical.merror_count" : { $ne : 'Count: 509'}}).count()
// Result: 2, Expected: 2
db.associated.find( { "diskinfo.physical.merror_count" : { $ne : 'Count: 5'}}).count()
// Result: 2, Expected: 2
like image 287
Phil Avatar asked Dec 13 '22 01:12

Phil


2 Answers

Although this is an old question I think there is still place for some additional explanation about the semantics of $ne with arrays.

  1. It is well-known that the behavior of mongo with queries of the form

    db.collection.find({a:v})

when a is an array: it returns all the documents such that (at least) one element in the array key is v. In the example:

db.associated.find({"diskinfo.physical.merror_count" : "Count: 0"}).count()

returns 3 because the the array "diskinfo.physical.merror_count" contains a value "Count: 0" in the three documents of the collection.

  1. Then, it seems reasonable (at least is a possibility) to expect that

    db.collection.find({a:{$ne:v}})

    will return the documents such that the a contains something different from v. However, this is not the behavior in Mongo. Instead, the use of $ne indicates that only those documents whose array a does not contain v are selected.

This semantics can seem odd at the beginning but makes sense if we consider that in this way the expression:

 db.collection.find({a:v}).count() + db.collection.find({a:{$ne:v}}).count()

return the total number of elements in the collection. Thus, in the example it makes sense that

db.associated.find({"diskinfo.physical.merror_count" : {$ne: "Count: 0"}}).count()

returns 0, because the array "diskinfo.physical.merror_count" contains at least one occurrence of "Count: 0" in the 3 documents of the collection.

like image 91
RafaelCaballero Avatar answered Dec 14 '22 15:12

RafaelCaballero


These results are correct.

Your expectation may be based on the number of elements in the embedded array which match your predicate. However, it's the number of documents that satisfy the query that you will be getting back.

In the first case you query for all documents which don't have diskinfo.physical.merror_count equal to 'Count: 0'. Every document has diskinfo.physical.merror_count that's 'Count: 0' so you get back 0.

Look at it this way, every document in your collection has a diskinfo.physical.merror_count value that's something other than 'Count: 0'. If you queried with equality instead of inequality you would get back all three.

Are you trying to find all documents which have only entries that are 'Count: 0'?

There doesn't seem to be a straight forward way to do this, but one query that would get you this would be:

db.associated.find( { "diskinfo.physical.merror_count" : { $gt : 'Count: 0'}}).count()

At least this would be the solution if the counts were actually integers - it happens to work with strings as well because "1">"0", etc.

like image 28
Asya Kamsky Avatar answered Dec 14 '22 14:12

Asya Kamsky