Implementing a NAND gate in a MongoDB query

I was tasked the other day to implement a filter into an existing query in one of our company projects. More specifically, the task at hand…

Implementing a NAND gate in a MongoDB query
Pretty much every single software project out there...

I was tasked the other day to implement a filter into an existing query in one of our company projects. More specifically, the task at hand was to filter out specific records based on whether the records matched certain conditions. Sounds easy enough, right?

Before we dive into the actual implementation, what in the world is a NAND gate though? A NAND logic gate takes two inputs, which can be thought of as the two switches, and it gives you an output, which can be thought of as whether the light is on or off. If both inputs are “on” or both inputs are “off”, then the output is “off” (like when both switches are turned off and the light is off). But if one input is “on” and the other input is “off”, then the output is “on” (like when one switch si on and the other is off, and the light is on). So, a NAND gate basically says “If both switches are the same (either both on or both off), then turn the light off. Otherwise, turn the light on.”

The conditions

This section is completely arbitrary and will depend entirely on business/customer needs. I’m describing it here just as a means to understand the thought process to build the actual filter.

The business needs to show on customer reports certain transactions that either lack a field we will call notifications or for those who have it, only if nested type and description don’t match a certain string. Basically, give me everything that doesn’t have notifications or if they do, give me those that have a type and description different than these two strings.

The implementation

So, we have a base query. Let’s say for the sake of visualization it is something very basic like this:

const query = { 
  client: someClientId, 
  dateTime: { $gte: start, $lte: end } 
};

We want to add our condition on top of this base query. Remember, we want this condition to kick in if the hypothetical notifications embedded field either doesn’t exist or some nested fields don’t match some specific strings. Let’s tackle the first part of the condition with an $or clause:

query.$or = [ 
  { notifications: { $exists: false } }, 
  { 
    // When the field exists, additional checks go here 
  } 
];

Since this is a basic $or condition, it goes one way or the other. The first one is pretty self explanatory. If notifications doesn’t exist, the second condition never comes into play. Now, here’s the kicker: MongoDB doesn’t have a built-in $nand clause.

To achieve the same effect as if we had a truly native $nand implementation, we need to combine a $nor with $and to get a DIY$nand clause:

query.$or = [ 
  { notifications: { $exists: false } }, 
  { 
    $nor: [ 
      $and: [ 
        { 'notifications.type': { $eq: 'SOME_TYPE' } }, 
        { 'notifications.description': { $eq: 'SOME_DESCRIPTION' } } 
      ] 
    ] 
  } 
];

The explanation

What exactly is the effect of this DIY $nand clause doing to my query? Remember the business condition at the beginning: we want to exclude certain transactions based on them matching two specific strings in two specific fields. The $nand condition is doing exactly that.

This query is telling Mongo we want to fetch all the records that either don’t have a notifications field or if they have it, give me those that don’t have notifications.type equal SOME_TYPE and notifications.description equal SOME_DESCRIPTION .