MongoDB
Query Filters
A query filter is the object you pass to find(), find_one(), update_one(), delete_one(), and almost every other MongoDB operation to specify which documents to match. Filters are written as plain Python dictionaries — no string parsing, no SQL injection risk. MongoDB provides a rich set of operators that cover every matching scenario: comparisons, logical combinations, element checks, array queries, and regular expressions. Mastering filters means you can pinpoint exactly the documents you need in any collection.
Every example in this lesson runs against the Dataplexa Store dataset.
Exact Match — The Simplest Filter
The most basic filter is an exact match — specify a field name and the value it must equal. MongoDB checks every document in the collection (or index, if one exists) and returns those where the field equals the value exactly.
# Exact match filters — field must equal the specified value
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# Single field exact match
premium_users = db.users.find({"membership": "premium"})
print("Premium users:")
for u in premium_users:
print(f" {u['name']} ({u['country']})")
# Multi-field exact match — ALL conditions must be true (implicit AND)
uk_premium = db.users.find({"membership": "premium", "country": "UK"})
print("\nPremium users in UK:")
for u in uk_premium:
print(f" {u['name']}")
# Exact match on a nested field using dot notation
london_users = db.users.find({"city": "London"})
print("\nUsers in London:")
for u in london_users:
print(f" {u['name']}")Alice Johnson (UK)
Clara Diaz (Spain)
Eva Müller (Germany)
Premium users in UK:
Alice Johnson
Users in London:
Alice Johnson
- Multiple fields in one filter are combined with an implicit
AND— all conditions must be satisfied - Exact match is case-sensitive —
"Premium"does not match"premium" - Exact match on an array field matches documents where the array contains that value — e.g.
{"tags": "newsletter"}matches any user whosetagsarray includes"newsletter"
Comparison Operators
Comparison operators let you match documents where a field is greater than, less than, or not equal to a value. They are the backbone of range queries — finding products within a price band, orders above a total, or users above a certain age.
# Comparison operators — $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# $gt — products with price greater than $50
expensive = db.products.find(
{"price": {"$gt": 50}},
{"name": 1, "price": 1, "_id": 0}
)
print("Products over $50:")
for p in expensive:
print(f" ${p['price']:>7.2f} {p['name']}")
# $gte / $lte — products in the $20–$100 range
mid_range = db.products.find(
{"price": {"$gte": 20, "$lte": 100}},
{"name": 1, "price": 1, "_id": 0}
)
print("\nProducts $20–$100:")
for p in mid_range:
print(f" ${p['price']:>7.2f} {p['name']}")
# $ne — all orders that are NOT delivered
not_delivered = db.orders.find(
{"status": {"$ne": "delivered"}},
{"_id": 1, "status": 1}
)
print("\nOrders not delivered:")
for o in not_delivered:
print(f" {o['_id']} — {o['status']}")
# $in — users from UK or Germany
uk_de = db.users.find(
{"country": {"$in": ["UK", "Germany"]}},
{"name": 1, "country": 1, "_id": 0}
)
print("\nUsers from UK or Germany:")
for u in uk_de:
print(f" {u['name']} ({u['country']})")
# $nin — products NOT in Electronics
non_electronic = db.products.find(
{"category": {"$nin": ["Electronics"]}},
{"name": 1, "category": 1, "_id": 0}
)
print("\nNon-electronics products:")
for p in non_electronic:
print(f" {p['name']} ({p['category']})")$ 89.99 Mechanical Keyboard
$299.99 Monitor 27-inch
$349.99 Standing Desk
Products $20–$100:
$ 29.99 Wireless Mouse
$ 49.99 USB-C Hub
$ 89.99 Mechanical Keyboard
Orders not delivered:
o002 — shipped
o004 — processing
o006 — cancelled
Users from UK or Germany:
Alice Johnson (UK)
Bob Smith (UK)
Eva Müller (Germany)
Non-electronics products:
Notebook A5 (Stationery)
Standing Desk (Furniture)
Ballpoint Pens 10-pack (Stationery)
$inaccepts a list of values — the field must equal any one of them. It is more efficient than multiple$orconditions on the same field$ninis the inverse of$in— matches documents where the field does not equal any value in the list- Combine
$gteand$lteon the same field for a range query — both operators apply to the same field simultaneously
Logical Operators
Logical operators combine multiple filter conditions. $and requires all conditions to be true, $or requires at least one to be true, $not inverts a condition, and $nor requires all conditions to be false.
# Logical operators — $and, $or, $not, $nor
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# $or — products that are either in Furniture OR cost less than $5
budget_or_furniture = db.products.find(
{"$or": [
{"category": "Furniture"},
{"price": {"$lt": 5}}
]},
{"name": 1, "category": 1, "price": 1, "_id": 0}
)
print("Furniture OR under $5:")
for p in budget_or_furniture:
print(f" {p['name']:30} ${p['price']:>7.2f} ({p['category']})")
# $and — explicit AND (useful when filtering on the same field twice)
# Find products in Electronics with a rating above 4.4
top_electronics = db.products.find(
{"$and": [
{"category": "Electronics"},
{"rating": {"$gt": 4.4}}
]},
{"name": 1, "rating": 1, "_id": 0}
)
print("\nTop-rated Electronics (rating > 4.4):")
for p in top_electronics:
print(f" {p['name']} — rating: {p['rating']}")
# $nor — orders that are neither cancelled NOR processing
active = db.orders.find(
{"$nor": [
{"status": "cancelled"},
{"status": "processing"}
]},
{"_id": 1, "status": 1}
)
print("\nOrders not cancelled and not processing:")
for o in active:
print(f" {o['_id']} — {o['status']}")
# $not — users who are NOT basic members
not_basic = db.users.find(
{"membership": {"$not": {"$eq": "basic"}}},
{"name": 1, "membership": 1, "_id": 0}
)
print("\nNon-basic members:")
for u in not_basic:
print(f" {u['name']} — {u['membership']}")Standing Desk $349.99 (Furniture)
Notebook A5 $ 4.99 (Stationery)
Ballpoint Pens 10-pack $ 3.49 (Stationery)
Top-rated Electronics (rating > 4.4):
Wireless Mouse — rating: 4.5
Mechanical Keyboard — rating: 4.7
Monitor 27-inch — rating: 4.6
Orders not cancelled and not processing:
o001 — delivered
o002 — shipped
o003 — delivered
o005 — delivered
o007 — delivered
Non-basic members:
Alice Johnson — premium
Clara Diaz — premium
Eva Müller — premium
- Multi-field implicit AND (comma-separated in one dict) is more concise than explicit
$andfor different fields - Use explicit
$andwhen you need two conditions on the same field — a single dict key can only appear once $notwraps another operator — it cannot be used alone at the top level without an operator inside it
Element Operators
Element operators check whether a field exists in a document or what type it is — essential for working with flexible schemas where different documents may have different fields.
# Element operators — $exists, $type
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# $exists — find users who have a tags field
has_tags = db.users.find(
{"tags": {"$exists": True}},
{"name": 1, "tags": 1, "_id": 0}
)
print("Users with a tags field:")
for u in has_tags:
print(f" {u['name']} — tags: {u['tags']}")
# $exists: False — find documents missing a field
no_phone = db.users.find(
{"phone": {"$exists": False}},
{"name": 1, "_id": 0}
)
print("\nUsers with no phone field:")
for u in no_phone:
print(f" {u['name']}")
# $type — find products where price is a double (correctly typed)
numeric_prices = db.products.find(
{"price": {"$type": "double"}},
{"name": 1, "price": 1, "_id": 0}
)
print("\nProducts with numeric (double) price:")
for p in numeric_prices:
print(f" {p['name']} — ${p['price']}")Alice Johnson — tags: ['early_adopter', 'newsletter']
Bob Smith — tags: ['newsletter']
Clara Diaz — tags: ['early_adopter']
David Lee — tags: []
Eva Müller — tags: ['early_adopter', 'newsletter']
Users with no phone field:
Alice Johnson
Bob Smith
Clara Diaz
David Lee
Eva Müller
Products with numeric (double) price:
Wireless Mouse — $29.99
Mechanical Keyboard — $89.99
Notebook A5 — $4.99
Standing Desk — $349.99
USB-C Hub — $49.99
Ballpoint Pens 10-pack — $3.49
Monitor 27-inch — $299.99
$exists: Truematches documents that have the field — even if its value isnull$exists: Falseis invaluable during data quality audits — finding documents that are missing required fields- Combine
$existsand$typeduring schema migrations to identify and fix incorrectly typed fields
Array Filters
MongoDB makes querying arrays natural — you can match on a single element, require multiple elements to be present, or use $elemMatch to apply multiple conditions to the same array element.
# Array filter operators — $all, $size, $elemMatch
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# Simple array element match — find users tagged 'newsletter'
newsletter = db.users.find(
{"tags": "newsletter"},
{"name": 1, "tags": 1, "_id": 0}
)
print("Users tagged 'newsletter':")
for u in newsletter:
print(f" {u['name']} — {u['tags']}")
# $all — users who have BOTH tags
both_tags = db.users.find(
{"tags": {"$all": ["early_adopter", "newsletter"]}},
{"name": 1, "tags": 1, "_id": 0}
)
print("\nUsers with both 'early_adopter' AND 'newsletter' tags:")
for u in both_tags:
print(f" {u['name']}")
# $size — users with exactly 2 tags
two_tags = db.users.find(
{"tags": {"$size": 2}},
{"name": 1, "tags": 1, "_id": 0}
)
print("\nUsers with exactly 2 tags:")
for u in two_tags:
print(f" {u['name']} — {u['tags']}")
# $elemMatch — orders containing an item from product p001 with qty > 0
has_p001 = db.orders.find(
{"items": {"$elemMatch": {"product_id": "p001", "qty": {"$gte": 1}}}},
{"_id": 1, "user_id": 1, "total": 1}
)
print("\nOrders containing product p001:")
for o in has_p001:
print(f" {o['_id']} — user: {o['user_id']} total: ${o['total']}")Bob Smith — ['newsletter']
Alice Johnson — ['early_adopter', 'newsletter']
Eva Müller — ['early_adopter', 'newsletter']
Users with both 'early_adopter' AND 'newsletter' tags:
Alice Johnson
Eva Müller
Users with exactly 2 tags:
Alice Johnson — ['early_adopter', 'newsletter']
Eva Müller — ['early_adopter', 'newsletter']
Orders containing product p001:
o001 — user: u001 total: $44.96
o005 — user: u004 total: $329.98
- A plain array equality check (
{"tags": "newsletter"}) matches any document where the array contains that value $allrequires every listed value to be present in the array — order does not matter$elemMatchis essential when you need multiple conditions to apply to the same array element — without it, conditions could match different elements$sizematches arrays of an exact length — it cannot be combined with range operators like$gt
Regular Expression Filters
The $regex operator matches string fields against a regular expression — useful for partial string matching, case-insensitive search, and pattern validation.
# $regex — pattern matching on string fields
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["dataplexa"]
# Find products whose name starts with "Wire" or "Mech"
starts_w = db.products.find(
{"name": {"$regex": "^Wire", "$options": "i"}},
{"name": 1, "_id": 0}
)
print("Products starting with 'Wire':")
for p in starts_w:
print(f" {p['name']}")
# Case-insensitive search — products containing "board" anywhere
contains_board = db.products.find(
{"name": {"$regex": "board", "$options": "i"}},
{"name": 1, "_id": 0}
)
print("\nProducts containing 'board' (case-insensitive):")
for p in contains_board:
print(f" {p['name']}")
# Find users whose email ends with @example.com
example_emails = db.users.find(
{"email": {"$regex": "@example\\.com$"}},
{"name": 1, "email": 1, "_id": 0}
)
print("\nUsers with @example.com email:")
for u in example_emails:
print(f" {u['name']} — {u['email']}")Wireless Mouse
Products containing 'board' (case-insensitive):
Mechanical Keyboard
Users with @example.com email:
Alice Johnson — alice@example.com
Bob Smith — bob@example.com
Clara Diaz — clara@example.com
David Lee — david@example.com
Eva Müller — eva@example.com
$options: "i"makes the match case-insensitive — use it for user-facing search^anchors the pattern to the start of the string,$anchors to the end- Regex filters cannot use standard indexes efficiently unless the pattern is anchored to the start (
^prefix) — for full-text search on large collections use a text index or Atlas Search instead
Summary Table
| Operator | Category | Matches When | Example |
|---|---|---|---|
$eq |
Comparison | Field equals value | {"age": {"$eq": 30}} |
$gt / $gte |
Comparison | Field greater than / or equal | {"price": {"$gte": 50}} |
$in |
Comparison | Field equals any value in list | {"country": {"$in": ["UK","DE"]}} |
$or |
Logical | At least one condition is true | {"$or": [{...},{...}]} |
$exists |
Element | Field exists / does not exist | {"phone": {"$exists": false}} |
$all |
Array | Array contains all listed values | {"tags": {"$all": ["a","b"]}} |
$elemMatch |
Array | One array element matches all conditions | {"items": {"$elemMatch": {...}}} |
$regex |
Evaluation | String matches a pattern | {"name": {"$regex": "^Wire"}} |
Practice Questions
Practice 1. Write a filter to find all products in the Dataplexa Store with a price between $10 and $100 inclusive.
Practice 2. What is the difference between using $or and listing multiple fields in a single filter dict?
Practice 3. Why is $elemMatch necessary when filtering on multiple conditions inside an array of sub-documents?
Practice 4. Write a filter to find all users in the Dataplexa Store from either Spain or USA.
Practice 5. What $regex option makes a pattern match case-insensitively?
Quiz
Quiz 1. Which operator would you use to find products whose category is one of three possible values?
Quiz 2. What does {"tags": "early_adopter"} match in MongoDB?
Quiz 3. When must you use explicit $and instead of the implicit multi-field AND?
Quiz 4. What does $exists: false match?
Quiz 5. Which regex anchor ensures a pattern only matches at the beginning of a string?
Next up — Projection: controlling exactly which fields MongoDB returns to keep responses lean and secure.