Facebook Pixel

Logical Query Building in MongoDB With Mongoose

Building queries with logical operators is a core skill. Here is how to do it in Mongoose.

Logical Query Building in MongoDB With Mongoose

Building queries with logical operators is a core skill. Here is how to do it in Mongoose.

$and: All Conditions Match

User.find({
  $and: [
    { age: { $gte: 18 } },
    { age: { $lte: 35 } },
    { gender: 'male' }
  ]
});

When you pass multiple fields in the same object, MongoDB ANDs them implicitly. Use $and explicitly only when you need the same field twice with different operators.

$or: Any Condition Matches

User.find({
  $or: [
    { email: '[email protected]' },
    { username: 'kunal' }
  ]
});

Use for "find by email OR username".

$nor: No Condition Matches

User.find({
  $nor: [
    { role: 'admin' },
    { role: 'moderator' }
  ]
});

Finds users who are neither admin nor moderator.

$not: Negate a Condition

User.find({
  age: { $not: { $gte: 18 } }
});

Finds users under 18. Same as { $lt: 18 }, but $not is useful for complex conditions.

Combining $and and $or

User.find({
  $and: [
    { status: 'active' },
    { $or: [{ age: { $lt: 18 } }, { age: { $gt: 65 } }] }
  ]
});

Active users who are under 18 or over 65.

Building Dynamic Filters

const filter = {};
if (req.query.role) filter.role = req.query.role;
if (req.query.gender) filter.gender = req.query.gender;
if (req.query.minAge || req.query.maxAge) {
  filter.age = {};
  if (req.query.minAge) filter.age.$gte = parseInt(req.query.minAge);
  if (req.query.maxAge) filter.age.$lte = parseInt(req.query.maxAge);
}
const users = await User.find(filter);

Build the filter object based on which query params are present. Pass to User.find().

Whitelist Filter Fields

Do not let users set arbitrary filter fields. Whitelist the fields they can filter by:

const allowedFilters = ['role', 'gender', 'status'];
const filter = {};
for (const key of allowedFilters) {
  if (req.query[key] !== undefined) filter[key] = req.query[key];
}

Prevents query injection and limits the surface.

The Takeaway

Build logical queries in Mongoose with $and (all match), $or (any match), $nor (none match), and $not (negate). Combine them for complex conditions. Build dynamic filters from query params. Whitelist filter fields to prevent query injection.

$and takes an array of conditions; all must match. $or takes an array; any must match. Combine them: $and: [{ status: 'active' }, { $or: [{ age: { $lt: 18 } }, { age: { $gt: 65 } }] }] for active users under 18 or over 65.

When you need the same field twice with different operators, or to group $or conditions with other conditions. For most queries, passing multiple fields in one object ANDs them implicitly. Use $and only when implicit AND is not enough.

Start with an empty filter object. For each query param that is present, add it to the filter. For range queries (minAge, maxAge), build an age object with $gte and $lte. Pass the filter to User.find(filter).

To prevent query injection. If users can set arbitrary filter fields, they can probe your data model. Whitelist the fields they can filter by (role, gender, status) and ignore the rest. Limits the surface.

Finds documents where none of the conditions match. User.find({ $nor: [{ role: 'admin' }, { role: 'moderator' }] }) finds users who are neither admin nor moderator. Useful for exclusion queries.

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.