Logical Operators

To be or not to be..

Overview

Logical operators allow you to match documents based on the boolean result βœ… ❌ coming from a set of expressions. The logical expressions are evaluated for each document in your filter and if their boolean result matches the expected one (true or false) are included or excluded respectively in the returned result. The section contains samples for the following operators:

Operator

Description

AND

Matches documents that fulfill all specified conditions

NOT

Matches documents that don't fulfill the specified expression

OR

Matches documents that fulfill at least one of a set of conditions

NOR

Matches documents that fail to fulfill both conditions

AND operator - $and

The AND operator performs a logical AND on a set of expressions and match documents that satisfy all of them.

Builders<T>.Filter
    .And(FilterDefinition<T>[] filters)

Create as many filter definitions you want and pass them as an argument to the And FilterDefinitionBuilder method.

The sample uses an And operator to find all documents that have male gender AND have the profession field equal to "Doctor".

LogicalOperators.cs
var collection = database
    .GetCollection<User>(Constants.UsersCollection);

// create an equality filter on the gender for 'male' (0)
var maleFilter = Builders<User>.Filter
    .Eq(u => u.Gender, Gender.Male);
    
// create an equality filter on profession for 'Doctor'
var doctorFilter = Builders<User>.Filter
    .Eq(u => u.Profession, "Doctor");
    
// compine the filters with AND operator
var maleDoctorsFilter = Builders<User>.Filter
    .And(maleFilter, doctorFilter);

var maleDoctors = await collection.Find(maleDoctorsFilter).ToListAsync();

The Gender property on the User class is an Enum type and the driver is smart enough πŸ§™β€β™‚οΈ πŸ¦‰ to translate it properly when sending the query to MongoDB

You can combine as many filters as you want to an AND operator. The following sample finds all documents that match the following criteria:

  1. They have gender Female πŸ‘©β€βš•οΈ

  2. They are either "Teacher", Nurse" or "Dentist" 🏫 πŸ₯

  3. Their salary is between 2000 AND 3200 πŸ’°

LogicalOperators.cs
var collection = database.GetCollection<User>(Constants.UsersCollection);

// female filter - condition 1
var femaleFilter = Builders<User>.Filter.Eq(u => u.Gender, Gender.Female);

// profession filter - condition 2
var teacherOrNurseFilter = Builders<User>.Filter
  .In(u => u.Profession, new[] { "Teacher", "Nurse", "Dentist" });
    
// salary filter - condition 3
var salaryFilter = Builders<User>.Filter.And(
                Builders<User>.Filter.Gte(u => u.Salary, 2000),
                Builders<User>.Filter.Lte(u => u.Salary, 3200));

// combined filter
var combinedFilter = Builders<User>.Filter
                .And(femaleFilter, teacherOrNurseFilter, salaryFilter);
    
var matchedUsers = await collection.Find(combinedFilter).ToListAsync();

The inner filter definitions passed as arguments in the And method can be as complex as you want to.

NOT operator - $ne

The NOT operator performs a logical NOT on an expression and match documents that don't satisfy the expression.

Builders<T>.Filter
    .Not(FilterDefinition<T> filter)

Create a filter definition and pass it as an argument to the Not FilterDefinitionBuilder method. The boolean expression's result for the matched documents will be false, meaning that they don't satisfy the expression.

The sample uses an Not operator to find all documents having male gender, which can be translated as NOT female.

LogicalOperators.cs
var collection = database
    .GetCollection<User>(Constants.UsersCollection);

// Negates the internal equality filter
var notMaleFilter = Builders<User>.Filter.Not(
    // equality filter on gender for Male
    Builders<User>.Filter.Eq(u => u.Gender, Gender.Male)
);

var femaleUsers = await collection.Find(notMaleFilter).ToListAsync();

OR operator - $or

The OR operator performs a logical OR on an set of expressions and matches documents that satisfy at least on of the expressions.

Builders<T>.Filter.Or(FilterDefinition<T>[] filters)

Create as many filter definitions you want and pass them as an argument to the Or FilterDefinitionBuilder method. The filters passed as parameters can be as complex as you want.

The sample uses an Or operator to find all documents having salary, either too low (less than 1500) or too high ( greater than 4000).

LogicalOperators.cs
var collection = database.GetCollection<User>(Constants.UsersCollection);

// users with salary either < 1500 (too low) or > 4000 (too high)
var orSalaryFilter = Builders<User>.Filter.Or(
    Builders<User>.Filter.Lt(u => u.Salary, 1500), // 1st expression
    Builders<User>.Filter.Gt(u => u.Salary, 4000)  // 2nd expression
);

var lowOrHighSalaryUsers = await collection
    .Find(orSalaryFilter).ToListAsync();

NOR operator - $nor

The NOR operator performs a logical NOR on an set of expressions and matches documents that fail to satisfy all the expressions. Despite the fact that MongoDB supports the $nor operator, you won't find any method on the C# driver. That's totally fine though because you can built it using the AND operator and negating the internal filters.

Create as many filter definitions you want to fail and pass them as an argument to the And FilterDefinitionBuilder method. The filters passed as parameters can be as complex as you want.

The sample finds documents that fail to satisfy the following criteria:

  1. Have profession equal to "Doctor"

  2. Have salary less than 4500

Since the matched documents needs to fail the above criteria, you should use their negates with an AND operator.

LogicalOperators.cs
var collection = database
    .GetCollection<User>(Constants.UsersCollection);

var norFilter = Builders<User>.Filter.And(
    Builders<User>.Filter // negate 1st filter
        .Not(Builders<User>.Filter.Eq(u => u.Profession, "Doctor")),    
    Builders<User>.Filter // negate 2nd filter
        .Not(Builders<User>.Filter.Lt(u => u.Salary, 4500))
);

var norUsers = await collection
    .Find(norFilter).ToListAsync();

πŸ’‘ Of course you can build a simple extension method that does this for you and makes things a little bit easier.

var collection = database.GetCollection<User>(Constants.UsersCollection);

var firstFilterToFail = Builders<User>.Filter
    .Eq(u => u.Profession, "Doctor");
    
var secondFilterToFail = Builders<User>.Filter
    .Lt(u => u.Salary, 4500);
    
var extensionNorFilter = Builders<User>.Filter
    .Nor(firstFilterToFail, secondFilterToFail);

Last updated