1 - MongoDB Query Operators - 3
1 - MongoDB Query Operators - 3
There are many query operators that can be used to compare and reference document fields.
Comparison Operators
Name Description
$eq Matches values that are equal to a specified value.
$gt Matches values that are greater than a specified value.
$gte Matches values that are greater than or equal to a specified value.
$in Matches any of the values specified in an array.
$lt Matches values that are less than a specified value.
$lte Matches values that are less than or equal to a specified value.
$ne Matches all values that are not equal to a specified value.
$nin Matches none of the values specified in an array.
Logical Operators
$and Joins query clauses with a logical AND returns all documents that match the conditions of
both clauses.
$not Inverts the effect of a query predicate and returns documents that do not match the query
predicate.
$nor Joins query clauses with a logical NOR returns all documents that fail to match both clauses.
$or Joins query clauses with a logical OR returns all documents that match the conditions of either
clause.
Evaluation
There are many update operators that can be used during document updates.
Fields
Array
**************************
Comparison Operators
db.inventory.insertMany( [
{ _id: 1, item: { name: "ab", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] },
{ _id: 2, item: { name: "cd", code: "123" }, qty: 20, tags: [ "B" ] },
{ _id: 3, item: { name: "ij", code: "456" }, qty: 25, tags: [ "A", "B" ] },
{ _id: 4, item: { name: "xy", code: "456" }, qty: 30, tags: [ "B", "A" ] },
{ _id: 5, item: { name: "mn", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
])
$eq (=) - Equals a Specified Value
$eq
Specifies equality condition. The $eq operator matches documents where the value of a field
equals the specified value.
Syntax
The following example queries the inventory collection to select all documents where the
value of the qty field equals 20:
db.inventory.find( { qty: 20 } )
[
{ _id: 2, item: { name: "cd", code: "123" }, qty: 20, tags: [ "B" ] },
{ _id: 5, item: { name: "mn", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
]
The following example queries the inventory collection to select all documents where the
value of the name field in the item document equals "ab". To specify a condition on a field in an
embedded document, use the dot notation.
Example :
Example :
{ _id: 1, item: { name: 'ab', code: '123' }, qty: 15, tags: [ 'A', 'B', 'C' ]}
{ _id: 2, item: { name: 'cd', code: '123' }, qty: 20, tags: [ 'B' ]}
The following example queries the inventory collection to select all documents where the tags array
contains an element with the value "B" [1]:
Example :
db.inventory.find( { tags: { $eq: "B" } } )
Example :
db.inventory.find( { tags: "B" } )
[
{ _id: 1, item: { name: "ab", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] },
{ _id: 2, item: { name: "cd", code: "123" }, qty: 20, tags: [ "B" ] },
{ _id: 3, item: { name: "ij", code: "456" }, qty: 25, tags: [ "A", "B" ] },
{ _id: 4, item: { name: "xy", code: "456" }, qty: 30, tags: [ "B", "A" ] }
]
The following example queries the inventory collection to select all documents where the
tags array equals exactly the specified array or the tags array contains an element that equals the
array [ "A", "B" ].
Example :
db.inventory.find( { tags: { $eq: [ "A", "B" ] } } )
Example :
db.inventory.find( { tags: [ "A", "B" ] } )
Both queries match the following documents:
[
{ _id: 3, item: { name: "ij", code: "456" }, qty: 25, tags: [ "A", "B" ] },
{ _id: 5, item: { name: "mn", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
]
The following examples demonstrate the differences in behavior between implicit and
explicit regular expression matching. Consider a collection with these documents:
Example :
db.companies.insertMany( [
{ _id: 001, company: "MongoDB" },
{ _id: 002, company: "MongoDB2" }
])
A string expands to return the same values whether an implicit match or an explicit use of $eq. Both
of these queries:
[ { company: "MongoDB" } ]
An explicit query using $eq and a regular expression will only match an object which is also a
regular expresssion.
The example query won't return anything since values in the company field are strings.
[
{ company: "MongoDB" },
{ company: "MongoDB2" }
]
$gt (>)
$gt selects those documents where the value of the specified field is greater than (i.e. >) the
specified value.
Syntax
db.inventory.insertMany( [
{
"item": "nuts", "quantity": 30,
"carrier": { "name": "Shipit", "fee": 3 }
},
{
"item": "bolts", "quantity": 50,
"carrier": { "name": "Shipit", "fee": 4 }
},
{
"item": "washers", "quantity": 10,
"carrier": { "name": "Shipit", "fee": 1 }
}
])
Example :
Select all documents in the inventory collection where quantity is greater than 20:
Output :
{ _id: ObjectId("61ba25cbfe687fce2f042414"), item: 'nuts', quantity: 30,
carrier: { name: 'Shipit', fee: 3 }
},
db.inventory.updateOne(
{ "carrier.fee": { $gt: 2 } }, { $set: { "price": 9.99 } }
)
Output :
{
{ _id: ObjectId("61ba3ec9fe687fce2f042417"), item: 'nuts', quantity: 30,
carrier: { name: 'Shipit', fee: 3 }, price: 9.99 },
$gte
$gte selects the documents where the value of the specified field is greater than or equal to
(i.e. >=) a specified value (e.g. value.)
Syntax
The $gte operator has the following form:
db.inventory.insertMany( [
{
"item": "nuts", "quantity": 30,
"carrier": { "name": "Shipit", "fee": 3 }
},
{
"item": "bolts", "quantity": 50,
"carrier": { "name": "Shipit", "fee": 4 }
},
{
"item": "washers", "quantity": 10,
"carrier": { "name": "Shipit", "fee": 1 }
}
])
Select all documents in the inventory collection where quantity is greater than or equal to 20:
$lt
$lt selects the documents where the value of the field is less than (i.e. <) the specified value.
$lte
$lte selects the documents where the value of the field is less than or equal to (i.e. <=) the
specified value.
$ne
$ne selects the documents where the value of the specified field is not equal to the specified
value. This includes documents that do not contain the specified field.
Example : Update
db.inventory.updateMany(
{ "carrier.fee" : { $ne: 1 } },
{ $set: { "price": 9.99 } }
)
db.inventory.insertMany( [
{
"item": "nuts", "quantity": 30,
"carrier": { "name": "Shipit", "fee": 3 }
},
{
"item": "bolts", "quantity": 50,
"carrier": { "name": "Shipit", "fee": 4 }
},
{
"item": "washers", "quantity": 10,
"carrier": { "name": "Shipit", "fee": 1 }
}
])
The following complete example adds a type array to two inventory documents and runs a query
with $ne:
// Find documents where the type array differs from [ "hardware", "fasteners" ]
db.inventory.find( { type: { $ne: [ "hardware", "fasteners" ] } } )
$ne matches documents where the array doesn't contain the specified value and documents that
don't have the array. For example:
$in
The $in operator selects the documents where the value of a field equals any value in the specified
array.
Syntax : { field: { $in: [<value1>, <value2>, ... <valueN> ] } }
Note : $in operator to tens of values. Using hundreds of parameters or more can negatively
impact query performance.
db.inventory.insertMany( [
{ "item": "Pens", "quantity": 350, "tags": [ "school", "office" ] },
{ "item": "Erasers", "quantity": 15, "tags": [ "school", "home" ] },
{ "item": "Maps", "tags": [ "office", "storage" ] },
{ "item": "Books", "quantity": 5, "tags": [ "school", "storage", "home" ] }
])
Example :
Output :
{ item: 'Erasers', quantity: 15, tags: [ 'school', 'home' ] }
{ item: 'Books', quantity: 5, tags: [ 'school', 'storage', 'home' ] }
$in - Array
db.inventory.updateMany(
{ tags: { $in: [ "home", "school" ] } },
{ $set: { exclude: false } }
)
This query selects all documents in the inventory collection where the tags field holds either
a string that starts with be or st or an array with at least one element that starts with be or st.
$nin
The specified field value is not in the specified array or the specified field does not exist.
db.inventory.insertMany( [
{ "item": "Pens", "quantity": 350, "tags": [ "school", "office" ] },
{ "item": "Erasers", "quantity": 15, "tags": [ "school", "home" ] },
{ "item": "Maps", "tags": [ "office", "storage" ] },
{ "item": "Books", "quantity": 5, "tags": [ "school", "storage", "home" ] }
])
db.inventory.find( { quantity: { $nin: [ 5, 15 ] } }, { _id: 0 } )
Output :
Set the exclude field to true for documents that don't have the "school" tag.
db.inventory.updateMany(
{ tags: { $nin: [ "school" ] } },
{ $set: { exclude: true } }
)
Logical Operators
$and
Note
MongoDB provides an implicit AND operation when specifying a comma separated list of
expressions.
Example:
The query can be rewritten with an implicit AND operation that combines the operator
expressions for the price field:
The query selects all documents where: the qty field value is less than 10 or greater than 50, and
the sale field value is equal to true or the price field value is less than 5.
The query cannot use an implicit AND operation because it uses the $or operator more than once.
db.inventory.find( {
$and: [
{ $or: [ { qty: { $lt : 10 } }, { qty : { $gt: 50 } } ] },
{ $or: [ { sale: true }, { price : { $lt : 5 } } ] }
]
})
$or
The $or operator performs a logical OR operation on an array of one or more <expressions>
and selects the documents that satisfy at least one of the <expressions>.
This query will select all documents in the inventory collection where either the quantity
field value is less than 20 or the price field value equals 10.
When using $or with <expressions> that are equality checks for the value of the same field,
use the $in operator instead of the $or operator.
For example, to select all documents in the inventory collection where the quantity field
value equals either 20 or 50, use the $in operator:
$not
$not performs a logical NOT operation on the specified <operator-expression> and selects
the documents that do not match the <operator-expression>. This includes documents that do not
contain the field.
Regular Expressions
For example, the following query selects all documents in the inventory collection where the
item field value does not start with the letter p.
db.inventory.find( { item: { $not: /^p.*/ } } )
For example, the following query selects all documents in the inventory collection where the
item field value does not start with the letter p.
$nor
$nor performs a logical NOR operation on an array of one or more query predicates and selects the
documents that fail all the query predicates in the array. The $nor has the following syntax:
contain the price field whose value is not equal to 1.99 and contain the sale field whose value is not
equal to true or
contain the price field whose value is not equal to 1.99 but do not contain the sale field or
do not contain the price field but contain the sale field whose value is not equal to true or
do not contain the price field and do not contain the sale field
This query will select all documents in the inventory collection where:
Compare that with the following query which uses the $nor operator with the $exists
operator:
Element
$exists
The $exists operator matches documents that contain or do not contain a specified field,
including documents where the field value is null.
Note : MongoDB $exists does not correspond to SQL operator exists. For SQL exists, refer to the
$in operator.
When <boolean> is true, $exists matches the documents that contain the field, including
documents where the field value is null. If <boolean> is false, the query returns only the documents
that do not contain the field. [1]
This query will select all documents in the inventory collection where the qty field exists
and its value does not equal 5 or 15.
Null Values
The following examples uses a collection named spices with the following
documents:
$exists: true
db.spices.find( { saffron: { $exists: true } } )
The results consist of those documents that contain the field saffron, including the document
whose field saffron contains a null value:
$exists: false
The following query specifies the query predicate cinnamon: { $exists: false }:
The results consist of those documents that do not contain the field cinnamon:
{ saffron: 2, mustard: 5 }
{ saffron: 4 }
{ mustard: 6 }
$type
$type selects documents where the value of the field is an instance of the specified BSON
type(s). Querying by data type is useful when dealing with highly unstructured data where data
types are not predictable.
Syntax
A $type expression for a single BSON type has the following syntax:
You can specify either the number or alias for the BSON type.
The $type expression can also accept an array of BSON types and has the following syntax:
The above query matches documents where the field value is any of the listed types. The types
specified in the array can be either numeric or string aliases.
db.data.insertMany( [
{ _id : 1, x : { "$minKey" : 1 } },
{ _id : 2, y : { "$maxKey" : 1 } }
])
The addressBook contains addresses and zipcodes, where zipCode has string, int, double, and long
values:
db.addressBook.insertMany( [
{ _id : 1, address : "2030 Martian Way", zipCode : "90698345" },
{ _id : 2, address : "156 Lunar Place", zipCode : 43339374 },
{ _id : 3, address : "2324 Pluto Place", zipCode : NumberLong(3921412) },
{ _id : 4, address : "55 Saturn Ring" , zipCode : NumberInt(88602117) },
{ _id : 5, address : "104 Venus Drive", zipCode : ["834847278", "1893289032"] }
])
The following queries return all documents where zipCode is the BSON type double or is an array
containing an element of the specified type:
db.addressBook.find( { zipCode : { $type : 1 } } );
db.addressBook.find( { zipCode : { $type : "double" } } );
The grades collection contains names and averages, where classAverage has string, int, and double
values:
db.grades.insertMany( [
{ _id : 1, name : "Alice King" , classAverage : 87.333333333333333 },
{ _id : 2, name : "Bob Jenkins", classAverage : "83.52" },
{ _id : 3, name : "Cathy Hart", classAverage: "94.06" },
{ _id : 4, name : "Drew Williams" , classAverage : NumberInt("93") }
])
The following queries return all documents where classAverage is the BSON type string or double
or is an array containing an element of the specified types. The first query uses numeric aliases
while the second query uses string aliases.
The restaurants collection uses minKey for any grade that is a failing grade:
db.restaurants.insertOne( [
{
_id: 1,
address: {
building: "230",
coord: [ -73.996089, 40.675018 ],
street: "Huntington St",
zipcode: "11231"
},
borough: "Brooklyn",
cuisine: "Bakery",
grades: [
{ date : new Date(1393804800000), grade : "C", score : 15 },
{ date : new Date(1378857600000), grade : "C", score : 16 },
{ date : new Date(1358985600000), grade : MinKey(), score : 30 },
{ date : new Date(1322006400000), grade : "C", score : 15 }
],
name : "Dirty Dan's Donuts",
restaurant_id : "30075445"
}
])
And maxKey for any grade that is the highest passing grade:
db.restaurants.insertOne( [
{
_id : 2,
address : {
building : "1166",
coord : [ -73.955184, 40.738589 ],
street : "Manhattan Ave",
zipcode : "11222"
},
borough: "Brooklyn",
cuisine: "Bakery",
grades: [
{ date : new Date(1393804800000), grade : MaxKey(), score : 2 },
{ date : new Date(1378857600000), grade : "B", score : 6 },
{ date : new Date(1358985600000), grade : MaxKey(), score : 3 },
{ date : new Date(1322006400000), grade : "B", score : 5 }
],
name : "Dainty Daisey's Donuts",
restaurant_id : "30075449"
}
])
The following query returns any restaurant whose grades.grade field contains minKey or is an array
containing an element of the specified type:
db.restaurants.find(
{ "grades.grade" : { $type : "minKey" } }
)
{
_id : 1,
address : {
building : "230",
coord : [ -73.996089, 40.675018 ],
street : "Huntington St",
zipcode : "11231"
},
borough : "Brooklyn",
cuisine : "Bakery",
grades : [
{ date : ISODate("2014-03-03T00:00:00Z"), grade : "C", score : 15 },
{ date : ISODate("2013-09-11T00:00:00Z"), grade : "C", score : 16 },
{ date : ISODate("2013-01-24T00:00:00Z"), grade : { "$minKey" : 1 }, score : 30 },
{ date : ISODate("2011-11-23T00:00:00Z"), grade : "C", score : 15 }
],
name : "Dirty Dan's Donuts",
restaurant_id : "30075445"
}
The following query returns any restaurant whose grades.grade field contains maxKey or is an array
containing an element of the specified type:
db.restaurants.find(
{ "grades.grade" : { $type : "maxKey" } }
)
{
_id : 2,
address : {
building : "1166",
coord : [ -73.955184, 40.738589 ],
street : "Manhattan Ave",
zipcode : "11222"
},
borough : "Brooklyn",
cuisine : "Bakery",
grades : [
{ date : ISODate("2014-03-03T00:00:00Z"), grade : { "$maxKey" : 1 }, score : 2 },
{ date : ISODate("2013-09-11T00:00:00Z"), grade : "B", score : 6 },
{ date : ISODate("2013-01-24T00:00:00Z"), grade : { "$maxKey" : 1 }, score : 3 },
{ date : ISODate("2011-11-23T00:00:00Z"), grade : "B", score : 5 }
],
name : "Dainty Daisey's Donuts",
restaurant_id : "30075449"
}
db.sensorReading.insertMany( [
{ _id : 1, readings : [ 25, 23, [ "Warn: High Temp!", 55 ], [ "ERROR: SYSTEM
SHUTDOWN!", 66 ] ] },
{ _id : 2, readings : [ 25, 25, 24, 23 ] },
{ _id : 3, readings : [ 22, 24, [] ] },
{ _id : 4, readings : [] },
{ _id : 5, readings : 24 }
])
The following query returns any document in which the readings field is an array, empty or
non-empty.
{
_id : 1,
readings : [
25,
23,
[ "Warn: High Temp!", 55 ],
[ "ERROR: SYSTEM SHUTDOWN!", 66 ]
]
},
{
_id : 2,
readings : [ 25, 25, 24, 23 ]
},
{
_id : 3,
readings : [ 22, 24, [] ]
},
{
_id : 4,
readings : []
}
In the documents with _id : 1, _id : 2, _id : 3, and _id : 4, the readings field is an array.
Evaluation
Name Description
$mod Performs a modulo operation on the value of a field and selects documents with a
specified result.
When $expr appears in a $match stage that is part of a $lookup subpipeline, $expr can refer
to let variables defined by the $lookup stage. For an example, see Use Multiple Join Conditions and
a Correlated Subquery.
The $eq, $lt, $lte, $gt, and $gte comparison operators placed in an $expr operator can use an
index on the from collection referenced in a $lookup stage. Limitations:
Indexes can only be used for comparisons between fields and constants, so the let operand
must resolve to a constant.
For example, a comparison between $a and a constant value can use an index, but a
comparison between $a and $b cannot.
Indexes are not used for comparisons where the let operand resolves to an empty or missing
value.
$expr can contain expressions that compare fields from the same document.
db.monthlyBudget.insertMany( [
{ _id : 1, category : "food", budget : 400, spent : 450 },
{ _id : 2, category : "drinks", budget : 100, spent : 150 },
{ _id : 3, category : "clothes", budget : 100, spent : 50 },
{ _id : 4, category : "misc", budget : 500, spent : 300 },
{ _id : 5, category : "travel", budget : 200, spent : 650 }
])
The following operation uses $expr to find documents where the spent amount exceeds the budget:
Output:
{ _id : 1, category : "food", budget : 400, spent : 450 }
{ _id : 2, category : "drinks", budget : 100, spent : 150 }
{ _id : 5, category : "travel", budget : 200, spent : 650 }
Some queries require the ability to execute conditional logic when defining a query filter.
The aggregation pipeline provides the $cond operator to express conditional statements. By using
$expr with the $cond operator, you can specify a conditional filter for your query statement.
db.supplies.insertMany( [
{ _id : 1, item : "binder", qty : NumberInt("100"), price : NumberDecimal("12") },
{ _id : 2, item : "notebook", qty : NumberInt("200"), price : NumberDecimal("8") },
{ _id : 3, item : "pencil", qty : NumberInt("50"), price : NumberDecimal("6") },
{ _id : 4, item : "eraser", qty : NumberInt("150"), price : NumberDecimal("3") },
{ _id : 5, item : "legal pad", qty : NumberInt("42"), price : NumberDecimal("10") }
])
Assume that for an upcoming sale next month, you want to discount the prices such that:
If qty is greater than or equal to 100, the discounted price will be 0.5 of the price.
If qty is less than 100, the discounted price is 0.75 of the price.
Before applying the discounts, you would like to know which items in the supplies collection have a
discounted price of less than 5.
The following example uses $expr with $cond to calculate the discounted price based on the qty and
$lt to return documents whose calculated discount price is less than NumberDecimal("5"):
The following table shows the discounted price for each document and whether discounted price is
less than NumberDecimal("5") (i.e. whether the document meets the query condition).
The db.collection.find() operation returns the documents whose calculated discount price is less
than NumberDecimal("5"):
Even though $cond calculates an effective discounted price, that price is not reflected in the
returned documents. Instead, the returned documents represent the matching documents in their
original state. The find operation did not return the binder or legal pad documents, as their
discounted price was greater than 5.
$text
$text performs a text query on the content of the fields indexed with a text index.
Syntax
{
$text: {
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>
}
}
Examples
The following examples assume a collection articles that has a version 3 text index on the field
subject:
db.articles.insertMany( [
{ _id: 1, subject: "coffee", author: "xyz", views: 50 },
{ _id: 2, subject: "Coffee Shopping", author: "efg", views: 5 },
{ _id: 3, subject: "Baking a cake", author: "abc", views: 90 },
{ _id: 4, subject: "baking", author: "xyz", views: 100 },
{ _id: 5, subject: "Café Con Leche", author: "abc", views: 200 },
{ _id: 6, subject: "Сырники", author: "jkl", views: 80 },
{ _id: 7, subject: "coffee and cream", author: "efg", views: 10 },
{ _id: 8, subject: "Cafe con Leche", author: "xyz", views: 10 }
])
This operation returns the documents that contain the term coffee in the indexed subject
field, or more precisely, the stemmed version of the word:
If the $search string is a space-delimited string, $text performs a logical OR operation on each term
and returns documents that contain any of the terms.
The following example specifies a $search string of three terms delimited by space, "bake coffee
cake":
This operation returns documents that contain either bake or coffee or cake in the indexed subject
field, or more precisely, the stemmed version of these words:
This operation returns documents that contain the string coffee shop:
This operation returns documents that contain both the strings, including documents that contain
terms from both the strings:
[
{ _id: 8, subject: 'Cafe con Leche', author: 'xyz', views: 10 },
{ _id: 5, subject: 'Café Con Leche', author: 'abc', views: 200 },
{ _id: 1, subject: 'coffee', author: 'xyz', views: 50 },
{ _id: 7, subject: 'coffee and cream', author: 'efg', views: 10 },
{ _id: 2, subject: 'Coffee Shopping', author: 'efg', views: 5 }
]
A negated term is a term that is prefixed by a minus sign -. If you negate a term, the $text
operator excludes the documents that contain those terms from the results.
The following example matches documents that contain the word coffee but do not contain
the term shop, or more precisely the stemmed version of the words:
Case Sensitivity
To enable case sensitivity, specify $caseSensitive: true. Specifying $caseSensitive: true may
impact performance.
$mod
Select documents where the value of a field divided by a divisor has the specified remainder. That
is, $mod performs a modulo operation to select documents. The first argument is the dividend, and
the second argument is the remainder.
Syntax: { field: { $mod: [ divisor, remainder ] } }
db.inventory.insertMany( [
{ "_id" : 1, "item" : "abc123", "qty" : 0 },
{ "_id" : 2, "item" : "xyz123", "qty" : 5 },
{ "_id" : 3, "item" : "ijk123", "qty" : 12 }
])
Then, the following query selects those documents in the inventory collection where value
of the qty field modulo 4 equals 0:
[
{ '_id' : 1, 'item' : 'abc123', 'qty' : 0 },
{ '_id' : 3, 'item' : 'ijk123', 'qty' : 12 }
]
db.employees.insertMany( [
{ "_id" : 1, "name" : "Rob", "salary" : 37000 },
{ "_id" : 2, "name" : "Trish", "salary" : 65000 },
{ "_id" : 3, "name" : "Zeke", "salary" : 99999 },
{ "_id" : 4, "name" : "Mary", "salary" : 200000 }
])
A company is giving a $1,000 raise to all employees earning less than $100,000.
The following command matches all employees who earn less than $100,000 and have not received
a raise, increments those salaries by $1,000, and sets raiseApplied to true:
db.employees.updateMany(
{ salary: { $lt: 100000 }, raiseApplied: { $ne: true } },
{ $inc: { salary: 1000 }, $set: { raiseApplied: true } }
)
updateMany() modifies the matching employee documents individually. The individual document
updates are atomic operations, but the updateMany() operation as a whole is not atomic.
If the operation fails to update all matched documents, you can safely rerun an idempotent
command until no additional documents match the specified filter. In this case, each document's
salary field is only updated one time regardless of how many times it is retried because the
command is idempotent.
After all eligible employees have received their raises, you can remove the raiseApplied field with
the following command:
db.employees.updateMany(
{ },
{ $unset: { raiseApplied: 1 } }
)