IsaacBok
IsaacBok

Reputation: 434

NOT AND in mongodb

I have a mongodb collection (collection1) and I want to construct a mongodb query against it that meets the following conditions:

1) condition # 1
_field1 has to exist

2) condition # 2 (this is the trickier one)
Considering '_field2.drivers' is an array, we CAN'T have this combination:
(size of _fields2.drivers = 1) AND (_fields2.drivers.template = 'templateX')

Here is what I tried:

{ 
    "collection1" : 
    {
        $and:
        [
            {
                '_field1':
                {
                    $exists:true
                }
            }
            {
                $not:
                {
                    $and:
                    [
                        {
                            '_field2.Drivers':
                            {
                                $size:1
                            }
                        }
                        {
                            '_field2.Drivers.template':"templateX"
                        }
                    ]
                }
            }

        ]

    } 
}

It seems like in mongodb you can't use $not and $and together.
Also the premise "NOT (A AND C) is equivalent to NOT A OR NOT C" won't work for me.
How can I say NOT (A AND B) in mongo? I just can't have that combination (A AND B) together...


To Clarify

size of _field2.Drivers can be = 1 as long as _field2.Drivers.template is not = 'templateX'
and _field2.Drivers.template can be = 'templateX' as long as size of _fields2.Drivers is not = 1

Upvotes: 4

Views: 675

Answers (2)

IsaacBok
IsaacBok

Reputation: 434

OK I found a slight problem with Neil's answer. It does not support the case where size of _field2.Drivers is not 1, and _field2.Drivers.template is not "templateX". We should accept this case as per the requirements. So the right answer would be:

{
    "_field1": { "$exists": true },
    "$or": [
        { 
            "_field2.Drivers": { "$not": { "$size": 1 } }
        },
        { 
            "_field2.Drivers": { "$size": 1 },
            "field2.Drivers.template": { "$ne": "templatex" }
        }
    ]
}

Upvotes: 0

Neil Lunn
Neil Lunn

Reputation: 151132

ALL MongoDB query expression arguments are "already" an "AND" clause unless explicitly stated otherwise. So you "rarely" actually need to express $and "explicitly" within a written query. There are few real cases, and most are actually suited to other query operators instead.

The combined logic of inclusive "AND" conditions therefore needs only one $not assertion on $size only, and the other negative can just be $ne instead:

{
    "_field1": { "$exists": true },
    "_field2.Drivers": { "$not": { "$size": 1 } },
    "field2.Drivers.template": { "$ne": "templatex" }
}

Unless of course you actually mean $or for "either" condition:

{
    "$or": [
        { "_field1": { "$exists": true } },
        { 
          "_field2.Drivers": { "$not": { "$size": 1 } },
          "field2.Drivers.template": { "$ne": "templatex" }
        }
    ]
}

Or extend on the $or:

{
    "_field1": { "$exists": true },
    "$or": [
        { 
            "_field2.Drivers": { "$not": { "$size": 1 } },
            "field2.Drivers.template": "templatex" 
        },
        { 
            "_field2.Drivers": { "$size": 1 },
            "field2.Drivers.template": { "$ne": "templatex" }
        }
    ]
}

There is also a slightly "obtuse" usage of $nor that applies here with a shorter syntax if not so clear a meaning:

{
    "_field1": { "$exists": true },
    "$nor": [
        { 
            "_field2.Drivers": { "$size": 1 },
            "field2.Drivers.template": "templatex" 
        }
    ]
}

Which with a single argument to $nor is a syntactically "similar" to "NOT AND()" as you are going to get.

Note: The fields "_field2" and "field2" are different here. You might actually mean to be referencing the same thing, in which case one is named incorrectly in your query.

Upvotes: 4

Related Questions