在任何业务中,数据都是您最大的资产。通过分析数据,您可以对客户趋势和行为预测做出决策。这提高了企业的盈利能力和有效的决策。
如果没有数据库软件,像在一个充满记录的系统中查找所有值的平均值这样的简单任务将是乏味的。幸运的是,数据库通过函数和运算符使分析数据变得更容易、更快。
本文将介绍 MongoDB 数据库软件中使用的运算符。
- 什么是 MongoDB 运算符?
- MongoDB 运算符类型
- MongoDB 运算符的最佳实践
什么是 MongoDB 运算符?
MongoDB 是一个 NoSQL 数据库软件,用于管理面向文档的信息。
MongoDB 的关键特性之一是它的速度。为了更快地返回查询,MongoDB 可能会使用运算符来执行特定的功能。
运算符是帮助编译器执行数学或逻辑任务的特殊符号。MongoDB 提供了几种类型的运算符来与数据库交互。
MongoDB 运算符类型
有九种类型的运算符,每种都以其功能命名。例如,逻辑运算符使用逻辑运算。要执行它们,您需要使用特定的关键字并遵循语法。但是,它们很容易遵循!
在本文结束时,您将能够了解每个运算符及其功能的基础知识。
逻辑运算符
逻辑运算符通常用于根据给定条件过滤数据。它们还允许评估许多条件,我们将更详细地讨论这些条件。
以下是您可以使用的一些逻辑运算符:
$and
“and”条件对包含两个或多个表达式的数组执行逻辑“and”运算。它选择满足所有表达式条件的文档。
这是$and
表达式的标准语法:
db.inventory.find( { $and: [ { quantity: { $lt: 15 } }, { price: 10 } ] } )
$or
“or”条件对包含两个或多个表达式的数组执行逻辑“or”运算。它选择至少有一个表达式为真的文档。
这是$or
表达式的标准语法:
例如,如果我们要选择价格为 10 美元或数量小于 15 的单据,我们可以输入以下查询:
db.inventory.find( { $or: [ { quantity: { $lt: 15 } }, { price: 10 } ] } )
我们不必将表达式限制为两个条件——我们可以添加更多。例如,以下查询选择价格等于 10 美元、数量低于 15 或标签固定的那些文档:
db.inventory.find( { $or: [ { quantity: { $lt: 15 } }, { price: 10 }, { tag: stationary }] } )
运行这些子句时,MongoDB 要么执行集合扫描,要么执行索引扫描。如果所有索引都支持子句,则 MongoDB 使用索引来检查$or
表达式。否则,它会改为使用集合扫描。
但是如果你想在同一个字段中测试条件,你可能想要使用$in
运算符符而不是$or
运算符。例如,如果您想要数量为 10 或 20 的文档集合,则可能必须运行以下$in
查询:
db.inventory.find ( { quantity: { $in: [20, 50] } } )
稍后我们将详细介绍$in
运算符。
$nor
此运算符使用一个或多个表达式对数组执行逻辑“nor”运算。接下来,它选择未通过查询表达式的文档。简而言之,它与$or
条件相反。
这是一般语法:
让我们考虑以下查询:
db.inventory.find( { $nor: [ { price: 3.99 }, { sale: true } ] } )
此查询选择包含以下内容的文档:
- 价格字段值不等于 3.99 美元,销售值不等于 true;或者
- 价格字段值不等于 3.99 美元,以及空的或不存在的销售字段;或者
- 没有 price 字段,并且 sale 字段不等于 true;或者
- 价格字段和销售字段均未填充或存在。
$not
此运算符对指定表达式的数组执行逻辑“not”运算。然后它选择与查询表达式不匹配的文档。这包括不包含该字段的文档。
这是一般语法:
例如,采用以下查询:
db.inventory.find( { price: { $not: { $lt: 3.99 } } } )
此查询将选择包含以下内容的文档:
- 一个价格字段,其值大于或等于 $3.99;和
- 价格字段未填充或不存在。
{ $not: { $lt: 3.99 } }
不同于$gte
运营商。{ $gte: 3.99 }
仅返回 price 字段存在且其值小于或等于 $3.99 的文档($not
运算符甚至返回那些 price 字段不存在的文档)。
比较运算符
比较运算符可用于比较一个或多个文档中的值。
下面是超市商店简单库存收集的示例代码:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] }, { _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] }, { _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] }, { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] }, { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }, { _id: 6, item: { name: "strawberry", code: "123" }, tags: [ "B" ] }
我们将在接下来详细介绍每个比较运算符时使用此示例。
Equal to ($eq)
此运算符匹配等于给定值的值:
例如,如果我们想从库存集合中检索具有确切数量值“20”的特定文档,我们将输入以下命令:
db.inventory.find( { qty: { $eq: 20 } } )
该查询将返回以下内容:
{ _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] }, { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
Greater than ($gt)
如果值大于给定值,则此运算符匹配:
{ field: { $gt: value } }
在本例中,我们检索数量大于 15 的文档:
db.inventory.find({"qty": { $gt: 15}})
该查询将返回以下内容:
{ _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] } { _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] } { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] } { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
Less than ($lt)
如果值小于提供的值,则此运算符匹配:
{ field: { $lt: value } }
让我们找到数量小于 25 的文档:
db.inventory.find({"qty": { $lt: 25}})
该查询将返回以下内容:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] } { _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] } { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
Greater or equal to ($gte)
当值大于或等于给定值时,此运算符匹配:
{ field: { $gte: value } }
在此示例中,我们检索数量大于或等于 25 的文档:
db.inventory.find({"qty": { $gte: 25}})
此查询将返回以下内容:
{ _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] } { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] }
Less or equal to ($lte)
此运算符仅在值小于或等于给定值时匹配:
{ field: { $lte: value } }
让我们找到数量小于或等于 25 的文档。
db.inventory.find({"qty": { $lte: 25}})
我们可以期望此查询返回以下内容:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] } { _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] } { _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] } { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }
In ($in)
此运算符返回与指定值匹配的文档:
字段的值等于指定数组中的任何值。例如,要在清单集合中检索值为“30”和“15”的文档,您可以这样做:
db.collection.find({ "qty": { $in: [30, 15]}})
输出将是:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] } { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] }
查询在 MongoDB Shell 中运行。
Not in ($nin)
此运算符返回与给定值不匹配的文档。以下是$nin
运算符的基本语法:
$nin
选择以下文档:
- 字段值不在指定数组中;或者
- 该字段不存在。
如果该字段包含数组,它将选择 value 部分中没有指定元素的数组。例如,我们要选择数量不等于 20 或 15 的那些文档。
此外,它还匹配没有数量字段的文档:
db.collection.find({ "qty": { $nin: [ 20, 15 ]}}, {_id: 0})
输出将是:
{ _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] } { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] } { _id: 6, item: { name: "strawberry", code: "123" }, tags: [ "B" ] }
Not Equal ($ne)
$ne
运算符返回指定值不相等的文档:
{ $ne: value } }
例如,假设我们要选择数量不等于 20 的所有文档:
db.inventory.find( { qty: { $ne: 20 } } )
输出将是:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] } { _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] } { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] } { _id: 6, item: { name: "strawberry", code: "123" }, tags: [ "B" ] }
从上面的输出中,我们可以看到查询将选择没有数量字段的文档。
元素运算符
元素查询运算符可以使用文档的字段来识别文档。元素运算符由$exist
和$type
组成。
$exists
此运算符匹配具有指定字段的文档。该运算符有一个布尔值,可以是true
或false
。
如果指定为true
false
,则查询仅返回不包含该字段的文档。
这是标准语法:
让我们举个例子,我们有一个名为“bagofmarbles”的数组的集合数据,其中每个包都包含不同颜色的弹珠:
{ red: 5, green: 5, blue: null } { red: 3, green: null, blue: 8 } { red: null, green: 3, blue: 9 } { red: 1, green: 2, blue: 3 } { red: 2, blue: 5 } { red: 3, green: 2 } { red: 4 } { green: 2, blue: 4 } { green: 2 } { blue: 6 }
假设我们想要一个只返回存在红色弹珠的袋子的查询。这意味着我们必须将布尔值输入为true
. 让我们来看看:
db.bagofmarbles.find( { red: { $exists: true } } )
结果将由包含字段“red”的文档组成,即使值为null
. 但是,它不会包含字段“red”甚至不存在的文档:
{ red: 5, green: 5, blue: null } { red: 3, green: null, blue: 8 } { red: null, green: 3, blue: 9 } { red: 1, green: 2, blue: 3 } { red: 2, blue: 5 } { red: 3, green: 2 } { red: 4 }
如果我们只想要那些甚至不存在红色弹珠的袋子作为字段,我们可以输入以下查询:
db.bagofmarbles.find( { red: { $exists: false} }
结果将包含那些不包含“红色”字段的文档:
{ green: 2, blue: 4 } { green: 2 } { blue: 6 }
$type
此运算符根据指定的字段类型匹配文档。当您拥有高度非结构化的数据或数据类型不可预测时,这很有用。这些字段类型是指定的 BSON 类型,可以通过类型号或别名来定义。
这是$type
的一般语法:
假设我们有一个包含以下文档的地址簿:
db={ addressBook: [ { "_id": 1, address: "2100 Jupiter Spot", zipCode: "9036325" }, { "_id": 2, address: "25 Moon Place", zipCode: 26237 }, { "_id": 3, address: "2324 Neptune Ring", zipCode: NumberLong(77622222) }, { "_id": 4, address: "33 Saturns Moon", zipCode: NumberInt(117) }, { "_id": 5, address: "1044 Venus Lane", zipCode: [ "99883637232", "73488976234" ] } ] }
观察上述文件,邮政编码有不同的数据类型。这包括 long、double、integer 和 string 值。
如果我们只想要那些具有指定数据类型作为邮政编码的文档——让我们以字符串为例——我们必须在编译器中输入以下查询:
db.addressBook.find({ "zipCode": { $type: "string" } })
这将返回以下文件:
[ { "_id": 1, "address": "2100 Jupiter Spot", "zipCode": "9036325" }, { "_id": 5, "address": "1044 Venus Lane", "zipCode": [ "99883637232", "73488976234" ] } ]
上述查询在 MongoDB Shell 中运行。
此外,还有一个“number”类型,它包括所有 long、integer 或 double 值作为包含指定类型元素的数组:
db.addressBook.find( { "zipCode" : { $type : "number" } } )
输出:
[ { "_id": 2, address: "25 Moon Place", zipCode: 26237 }, { "_id": 3, address: "2324 Neptune Ring", zipCode: NumberLong(77622222) }, { "_id": 4, address: "33 Saturns Moon", zipCode: NumberInt(117) } ]
如果文档具有数组字段类型,则$type
运算符返回其中至少一个数组元素与传递给运算符的类型匹配的文档。
从 MongoDB 3.6 及更高版本开始,查询
$type: array
返回字段本身是数组的文档。但是,在使用相同的查询时,以前的版本用于返回字段是数组的文档,并且至少一个元素是数据类型数组。
数组运算符
MongoDB 还包含数组运算符,用于查询包含数组的文档。
有三个主要运算符$all
:$elemMatch
和$size
。我们将在下面详细讨论每一个。
$all
$all
运算符选择字段值是包含指定元素的数组的文档:
例如,假设我们有一个服装店的文档集合,清单中有以下文档。
{ _id: ObjectId("5234cc89687ea597eabee675"), code: "shirt", tags: [ "sale", "shirt", "button", "y2k", "casual" ], qty: [ { size: "S", num: 10, color: "blue" }, { size: "M", num: 45, color: "blue" }, { size: "L", num: 100, color: "green" } ] }, { _id: ObjectId("5234cc8a687ea597eabee676"), code: "pant", tags: [ "y2k", "trendy", "shine" ], qty: [ { size: "6", num: 100, color: "green" }, { size: "6", num: 50, color: "blue" }, { size: "8", num: 100, color: "brown" } ] }, { _id: ObjectId("5234ccb7687ea597eabee677"), code: "pant2", tags: [ "trendy", "shine" ], qty: [ { size: "S", num: 10, color: "blue" }, { size: "M", num: 100, color: "blue" }, { size: "L", num: 100, color: "green" } ] }, { _id: ObjectId("52350353b2eff1353b349de9"), code: "shirt2", tags: [ "y2k", "trendy" ], qty: [ { size: "M", num: 100, color: "green" } ] }
我们希望从与标签“trendy”和“y2k”链接的库存中检索任何文档(在本例中为衣服)。以下查询使用$all
运算符,其中 tags 字段的值是一个数组,其元素包括“y2k”和“trendy”:
db.inventory.find( { tags: { $all: [ "y2k", "trendy" ] } } )
上面的查询返回以下内容:
{ _id: ObjectId("5234cc8a687ea597eabee676"), code: "pant", tags: [ "y2k", "trendy", "shine" ], qty: [ { size: "6", num: 100, color: "green" }, { size: "6", num: 50, color: "blue" }, { size: "8", num: 100, color: "brown" } ] } { _id: ObjectId("52350353b2eff1353b349de9"), code: "shirt2", tags: [ "y2k", "trendy" ], qty: [ { size: "M", num: 100, color: "green" } ] }
上述查询在 MongoDB Shell 中运行。
从上面的例子中,我们还发现$all
运算符只是简单地执行了与$and
运算相同的功能。
或者,我们可以使用下面的查询,它会给出与上面类似的输出:
db.collection.find({ $and: [ { tags: "y2k" }, { tags: "trendy" } ] })
上述查询在 MongoDB shell 中运行。
$elemMatch
$elemMatch
运算符匹配包含数组字段的文档,其中至少一个元素与所有指定的查询条件匹配:
$lte
虽然我们可以使用and等比较运算符$gte
,但如果我们在 内部仅指定一个查询条件$elemMatch
,并且不使用$not
or$ne
运算符,$elemMatch
则可以省略 using,因为它本质上将执行相同的功能。
在使用这个运算符时,还有一些事情需要记住,主要是:
- 您不能
$where
在运算中指定表达式$elemMatch
。 - 您不能
$text
在运算中指定查询表达式$elemMatch
。
例如,我们在学生结果集合中有以下文档:
{ _id: 1, results: [ 92, 89, 98 ] } { _id: 2, results: [ 85, 99, 99 ] }
以下查询仅匹配结果数组包含至少一个大于或等于 90 且小于 95 的元素的文档:
db.studentresults.find( { results: { $elemMatch: { $gte: 90, $lt: 95 } } })
我们的查询返回以下文档,因为元素 92 既大于或等于 90 又小于 95:
{ "_id" : 1, "results" :[ 92, 89, 98 ] }
$size
$size
运算符返回数组大小与参数中指定的元素数匹配的那些文档:
{ field: { $size: value } }
这是一个例子:
db.collection.find( { field: { $size: 2 } });
这将返回指定集合中的所有文档,其中字段是具有 2 个元素的数组:{ field: [ orange, apple] }
and { field: [ blue, red] }
,但不是{ field: blue}
or { field: [ raspberry, lemon, grapefruit ] }
。
但是,虽然我们可以输入特定值作为大小,但我们不能指定值的范围作为大小。
地理空间运算符
MongoDB 允许您以 GeoJSON 类型的形式存储地理空间数据。GeoJSON 是一种基于 JavaScript 对象表示法的开放标准格式,可以表示地理特征并支持非空间属性。我们将在本文中讨论两种类型的地理空间运算符:几何说明符和查询选择器。
$geometry
此运算符提到 GeoJSON 几何以用于以下地理空间查询运算符:$geoIntersects
、$geoWithin
、$nearSphere
和$near
。$geometry
利用 EPSG:4326 作为默认坐标参考系统 (CRS)。
要使用默认 CRS 提及 GeoJSON 对象,您可以利用以下代码段$geometry
:
要提及带有定制 MongoDB CRS 的单环 GeoJSON 多边形,您可以使用以下代码段(您只能将其用于$geoWithin
和$geoIntersects
):
$polygon
$polygon
运算符可用于为$geoWithin
旧坐标对上的地理空间查询指定多边形。然后,此查询将返回位于多边形范围内的对。但是,$polygon
不会查询任何 GeoJSON 对象。要定义一个多边形,您需要指定一个坐标点数组,如下所示:
在这里,最后一个点隐式连接到第一个点。您可以根据需要提及尽可能多的点或方面。
例如,以下查询将返回坐标位于由 [0,0]、[1,5] 和 [3,3] 定义的多边形内的所有文档:
db.places.find( { loc: { $geoWithin: { $polygon: [ [ 0 , 0 ], [ 1 , 5 ], [ 3 , 3 ] ] } } } )
$geoWithin
此运算符可用于选择具有完全包含在特定形状中的地理空间数据的文档。指定的形状可以是 GeoJSON 多面体、GeoJSON 多边形(多环或单环)或可以由旧坐标对定义的形状。
$geoWithin
运算符将利用$geometry
运算符来提及GeoJSON对象。
要通过默认坐标参考系统 (CRS) 提及 GeoJSON 多面体或多边形,您可以使用下面提到的语法:
对于$geoWithin
提及面积大于单个半球的 GeoJSON 几何的查询,使用默认 CRS 将导致查询互补几何。
$geometry
要提及具有自定义 MongoDB CRS 的单环 GeoJSON 多边形,您可以利用表达式中下面提到的原型:
以下示例选取完全存在于 GeoJSON 多边形内的所有 loc 数据,多边形的面积小于单个半球的面积:
db.places.find( { loc: { $geoWithin: { $geometry: { type : "Polygon" , coordinates: [ [ [ 0, 0 ], [ 3, 6 ], [ 6, 1 ], [ 0, 0 ] ] ] } } } } )
$box
您可以使用$box
为地理空间$geoWithin
查询指定一个矩形,以根据基于点的位置数据提供矩形范围内的文档。$geoWithin
与$box
一起使用时,您将获得基于查询坐标的文档。在这种情况下,$geoWithin
不会查询任何 GeoJSON 形状。
要利用$box
运算符,您需要在数组对象中提及矩形的右上角和左下角:
上述查询将利用平面(平面)几何计算距离。以下查询将返回框内的所有文档,这些文档的点位于:[0,0]、[0,30]、[30,0]、[30,30]:
db.places.find ( { loc: { $geoWithin: { $box: [ [ 0,0 ], [ 30,30 ] ] } } } )
$nearSphere
您可以用$nearSphere
来提及地理空间查询从最近到最远返回文档的点。
MongoDB 使用球面几何计算$nearSphere
. 它将需要一个地理空间索引,如下所示:
- 描述为旧坐标对的位置数据的二维索引。要利用 GeoJSON 点的 2d 索引,您需要在 GeoJSON 对象的坐标字段上生成索引。
- 描述为 GeoJSON 点的位置数据的 2dsphere 索引。
要提及 GeoJSON 点,您可以利用以下语法:
在这里,$minDistance
和$maxDistance
是可选的。$minDistance
可以将结果限制为至少距中心指定距离的那些文档。您可以使用$maxDistance
任一索引。
现在,考虑一个“地点”集合,该集合由具有 2dsphere 索引的位置字段的文档组成。以下示例将返回距离您选择的点至少 2,000 米且最多 6,000 米的点,按从最近到最远的顺序排列:
db.places.find( { location: { $nearSphere: { $geometry: { type : "Point", coordinates : [ -43.9532, 50.32 ] }, $minDistance: 2000, $maxDistance: 6000 } } } )
$geoIntersects
$geoIntersects
运算符允许您选择其地理空间数据与特定 GeoJSON 对象相交的文档(即,指定对象和数据的会聚处为非空)。它利用$geometry
运算符来指定 GeoJSON 对象。
要通过默认坐标参考系统 (CRS) 提及 GeoJSON 多面体或多边形,您可以使用以下语法:
以下实例将使用$geoIntersects
来拾取与坐标数组描述的多边形相交的所有loc数据:
db.places.find( { loc: { $geoIntersects: { $geometry: { type: "Polygon" , coordinates: [ [ [ 0, 0 ], [ 2, 6 ], [ 4, 1 ], [ 0, 0 ] ] ] } } } } )
$center
$center
运算符提到了一个圆,用于$geoWithin
返回圆范围内的旧坐标对的查询。
$center
不返回 GeoJSON 对象。要利用$center
运算符,您需要指定一个包含以下内容的数组:
- 圆的半径,以坐标系使用的单位测量。
- 圆中心点的网格坐标。
下面提到的示例将返回所有具有可以在以 [2,3] 为中心且半径为 40 的圆内找到坐标的文档:
db.places.find( { loc: { $geoWithin: { $center: [ [2, 3], 40 ] } } } )
投影运算符
您可以使用投影运算符来提及运算返回的字段。MongoDB 投影运算符允许find()
函数与数据过滤参数一起使用。这有助于用户仅从文档中提取所需的数据字段。因此,它允许您在不影响整体数据库性能的情况下投射透明和简洁的数据。
$elemMatch(投影)
$elemMatch
运算符负责将查询结果中的字段内容限制为仅包含与条件匹配的第一个元素$elemMatch
。
在使用之前,您需要牢记以下几点$elemMatch
:
- 从 MongoDB 4.4 开始,无论文档中字段的顺序如何,
$elemMatch
现有字段的投影都会返回包含其他现有字段之后的字段。 $elemMatch
和运算符$
都根据指定条件描述数组中的第一个匹配元素。$
运算符将根据查询语句中的某些条件从集合中的每个文档中投影第一个匹配的数组元素,而$elemMatch
投影运算符采用显式条件参数。这使您可以根据查询中不存在的条件进行投影,或者如果您需要根据数组嵌入文档中的各种字段进行投影。
在对您的数据使用运算符$elemMatch
之前,您还应该注意以下限制:
- 您不能在运算符
$text
中提及查询表达式$elemMatch
。 db.collection.find()
视图上的运算不支持$elemMatch
投影运算符。
以下关于$elemMatch
投影运算符的示例假设一个schools
包含以下文档的集合:
{ _id: 1, zipcode: "63108", students: [ { name: "mark", school: 102, age: 9 }, { name: "geoff", school: 101, age: 13 }, { name: "frank", school: 104, age: 12 } ] } { _id: 2, zipcode: "63110", students: [ { name: "harry", school: 103, age: 14 }, { name: "george", school: 103, age: 7 }, ] } { _id: 3, zipcode: "63108", students: [ { name: "harry", school: 103, age: 14 }, { name: "george", school: 103, age: 7 }, ] } { _id: 4, zipcode: "63110", students: [ { name: "jim", school: 103, age: 9 }, { name: "michael", school: 103, age: 12 }, ] }
在本例中,find()
运算查询 zipcode 字段值为 63110 的所有文档。$elemMatch
投影将仅返回学生数组中的第一个匹配元素,其中school
字段的值为 103:
db.schools.find( { zipcode: "63110" }, { students: { $elemMatch: { school: 103 } } } ) This is what the result would look like: { "_id" : 2, "students" : [ { "name" : "harry", "school" : 103, "age" : 14 } ] } { "_id" : 4, "students" : [ { "name" : "jim", "school" : 103, "age" : 9 } ] }
$slice (projection)
$slice
投影运算符可用于指定要在查询结果中返回的数组中的元素数:
也可以这样表达:
为了证明这一点,您可以使用以下文档创建一个示例推文集合:
db.posts.insertMany([ { _id: 1, title: "Nuts are not blueberries.", comments: [ { comment: "0. true" }, { comment: "1. blueberries aren't nuts."} ] }, { _id: 2, title: "Coffee please.", comments: [ { comment: "0. Indubitably" }, { comment: "1. Cuppa tea please" }, { comment: "2. frappucino" }, { comment: "3. Mocha latte" }, { comment: "4. whatever" } ] } ])
以下运算将使用$slice
tweets 数组上的投影运算符返回包含前两个元素的数组。如果数组包含的元素少于两个,则返回数组中的所有元素:
db.posts.find( {}, { comments: { $slice: 2 } } )
该运算将返回以下文档:
{ "_id" : 1, "title" : "Nuts are not blueberries.", "comments" : [ { "comment" : "0. true" }, { "comment" : "1. blueberries aren't nuts." } ] } { "_id" : 2, "title" : "Coffee please.", "comments" : [ { "comment" : "0. Indubitably" }, { "comment" : "1. Cuppa tea please" } ] }
$ (projection)
位置$
运算符限制数组的内容,以返回与该数组的查询条件匹配的第一个元素。当您只需要所选文档中的一个特定数组元素时,可以在find()
方法或findOne()
方法的投影文档中使用$
。
运算符的语法$
如下所示:
在此示例中,students
集合包含以下文档:
{ "_id" : 1, "semester" : 2, "grades" : [ 75, 67, 93 ] } { "_id" : 2, "semester" : 2, "grades" : [ 60, 68, 72 ] } { "_id" : 3, "semester" : 2, "grades" : [ 95, 82, 67 ] } { "_id" : 4, "semester" : 3, "grades" : [ 89, 95, 70 ] } { "_id" : 5, "semester" : 3, "grades" : [ 68, 98, 82 ] } { "_id" : 6, "semester" : 3, "grades" : [ 65, 70, 76 ] }
在以下查询中,投影{ "grades.$": 1 }
仅返回grades
字段的第一个大于或等于 89 的元素:
db.students.find( { semester: 2, grades: { $gte: 89 } }, { "grades.$": 1 } )
此运算返回以下文档:
{"_id": 3, "grades": [95] }
评估运算符
您可以利用 MongoDB 评估运算符来衡量文档中的整体数据结构或单个字段。
让我们看一些常见的 MongoDB 求值运算符。
$mod
您可以使用此运算符匹配指定字段的值等于除以指定值后的余数的文档:
{ field: { $mod: [ divisor, remainder ] } }
假设您在陈列室中有一张属于您拥有的不同品牌的汽车的桌子。以下查询将为您提供库存数量为 250 倍数的所有汽车品牌。
db.cars.find ( { qty: { $mod: [ 250,0 ] } } )
$jsonSchema
$jsonSchema
允许您匹配与指定 JSON 模式匹配的文档。MongoDB 的 JSON 模式实现包括添加bsonType
关键字,它允许您在$jsonSchema
运算符中使用所有 BSON 类型。
bsonType
可以接受与type
运算符相同的字符串别名。这就是$jsonSchema
的语法:
在这里,JSON 模式对象是根据 JSON 模式标准的草案 4 格式化的:
这是一个演示$jsonSchema
如何工作的示例:
{ $jsonSchema: { required: [ "name", "major", "gpa", "address" ], properties: { name: { bsonType: "string", description: "must be a string and is required" }, address: { bsonType: "object", required: [ "zipcode" ], properties: { "street": { bsonType: "string" }, "zipcode": { bsonType: "string" } } } } } }
您还可以在文档验证器中使用$jsonSchema
以在更新和插入运算中强制执行指定的模式:
请记住,$jsonSchema
运算符不支持一些事情:
- 整数类型。您需要使用带有 bsonType 关键字的 BSON 类型 long 或 int。
- 未知关键字。
- 链接属性和 JSON 模式的超媒体,以及 JSON 引用和 JSON 指针的使用。
$text
运算符将$text
在指定字段的内容中查找文本,并使用文本索引进行索引:
在这种情况下,以下代码片段将筛选表格以过滤掉任何包含“Porsche”文本的汽车:
db.cars.find( { $text: { $search: "Porsche" } } )
$regex
$regex
运算符提供正则表达式功能来模式匹配查询中的字符串。MongoDB 利用与 Perl 兼容的正则表达式:
以下示例将帮助过滤掉所有包含字符串“$78900”的汽车:
db.cars.find( { price: { $regex: /$78900/ } } )
$expr
$expr
运算符允许您在查询语言中利用聚合表达式:
您还可以使用$expr
构建查询表达式来比较$match
阶段中同一文档的字段。如果$match
阶段恰好是阶段的一部分$lookup
,$expr
可以借助 let 变量比较字段。
$where
您可以利用$where
运算符将包含完整 JavaScript 函数的字符串或 JavaScript 表达式传递给查询系统。运算符$where
提供了更大的灵活性,但需要数据库为集合中的每个文档处理 JavaScript 函数或表达式。您可以使用obj
或this
在JavaScript函数或表达式中引用此文档。
下面是一个语法示例:
在我们深入研究使用运算符$where
的示例之前,需要牢记一些关键注意事项:
- 您应该只对顶级文档使用查询运算符
$where
。查询运算符$where
在嵌套文档中不起作用,就像在$elemMatch
查询中一样。 - 通常,仅当您无法通过其他运算符
$where
表达查询时才应使用。如果必须使用$where
,请确保至少包含一个其他标准查询运算符来过滤结果集。独立使用$where
需要收集扫描才能正确执行。
这是一个例子来说明这一点:
db.cars.find( { $where: function() { return (hex_md5(this.name)== "9a43e617b50cd379dca1bc6e2a8") } } );
位运算符
位运算符根据位位置条件返回数据。简而言之,它们用于匹配数字或二进制值,其中一组位位置中的任何位的值为 1 或 0。
$bitsAllSet
该运算符将匹配字段中设置了查询提供的所有位位置(即 1)的所有文档:
字段值应为 BinData 实例或 $bitsAllSet
的数值,以匹配当前文档。
在下面的例子中,我们使用了一个包含以下文档的集合:
db.collection.save({ _id: 1, a: 54, binaryValueofA: "00110110" }) db.collection.save({ _id: 2, a: 20, binaryValueofA: "00010100" }) db.collection.save({ _id: 3, a: 20.0, binaryValueofA: "00010100" }) db.collection.save({ _id: 4, a: BinData(0, "Zg=="), binaryValueofA: "01100110" })
下面提到的查询将使用$bitsAllSet
运算符来测试字段 a 是否在位置 1 和位置 5 设置了位,其中最低有效位位于位置 0:
db.collection.find( { a: { $bitsAllSet: [ 1, 5 ] } }
此查询将匹配以下文档:
{ "_id" : 1, "a" : 54, "binaryValueofA" : "00110110" } { "_id" : 4, "a" : BinData(0,"Zg=="), "binaryValueofA" : "01100110" }
$bitsAllClear
$bitsAllClear
运算符将匹配查询提供的所有位位置均为 clear 或0
的文档:
我们将在这里使用用于$bitsAllSet
的示例来演示$bitsAllClear
的用法。以下查询将使用此运算符检查字段 a 是否在位置 1 和 5 处清除位:
db.collection.find( { a: { $bitsAllClear: [ 1, 5 ] } } )
此查询将匹配以下文档:
{ "_id" : 2, "a" : 20, "binaryValueofA" : "00010100" } { "_id" : 3, "a" : 20, "binaryValueofA" : "00010100" }
元运算符
有多种查询修饰符可让您修改 MongoDB 中查询的行为或输出。驱动程序接口可能会提供包装它们以供您使用的游标方法。
$hint
MongoDB 自 v3.2 起已弃用$hint
。但是,此运算符可能仍可用于 Go、Java、Scala、Ruby、Swift 等 MongoDB 驱动程序。它可以强制查询优化器利用特定索引来完成查询,然后可以通过文档或通过索引名称。
您还可以使用$hint
运算符来测试索引策略和查询性能。例如,进行以下运算:
db.users.find().hint( { age: 1 } )
此运算将通过利用age
字段上的索引返回集合中名为users
的所有文档。
您还可以使用以下任一形式提及提示:
db.users.find()._addSpecial( "$hint", { age : 1 } ) db.users.find( { $query: {}, $hint: { age : 1 } } )
如果查询形状存在索引过滤器,MongoDB 将简单地忽略$hint
.
$comment
$comment
运算符允许您在$query
可能出现的任何上下文中将注释附加到查询。由于评论会传播到配置文件日志,因此添加评论可以更轻松地解释和跟踪您的配置文件。
您可以通过以下三种方式之一利用$comment
:
如果要将注释附加到其他上下文中的查询表达式,例如使用 db.collection.update()
,使用$comment
查询运算符而不是元运算符。
$max
您可以提及一个$max
值来指定特定索引的排他上限以约束find()
. 此运算符将为索引中特定顺序的所有键指定上限。
Mongosh 为您提供以下 wrapper 方法max()
:
您还可以通过以下两种形式提及$max
:
例如,如果要指定独占上界,请记住对包含索引{ age: 1 }
的名为collection的集合执行以下运算:
此运算将查询限制为字段年龄小于100的文档,并强制执行将{ age: 1 }
索引从minKey
扫描到 100 的查询计划。
$explain
该运算符将为您提供有关查询计划的信息。它返回一个描述用于返回查询的索引和进程的文档。这在尝试优化查询时很有用。
您可以通过以下任一形式提及$explain
运算符:
db.collection.find()._addSpecial( "$explain", 1 ) db.collection.find( { $query: {}, $explain: 1 } )
MongoDB 运算符的最佳实践
在本节中,我们将了解使用这些 MongoDB 运算符时的一些最佳实践。
嵌入和引用
嵌入是数据建模的自然扩展。它允许您避免应用程序连接,从而减少更新和查询。
您可以在单个文档中嵌入具有 1:1 关系的数据。也就是说,具有“许多”对象与其父文档一起出现的多对一关系的数据也可能是很好的候选对象。
将这些类型的数据存储在同一个文档中听起来像是一个谨慎的选择。但是,嵌入为具有这种数据局部性的读取运算提供了更好的性能。
嵌入式数据模型还可以帮助开发人员在一次写入运算中更新相关数据。这是有效的,因为单个文档写入是事务性的。
您应该考虑在以下情况下使用引用:
- 当您更新文档片段并且它不断变长时,而文档的其余部分是静态的。
- 当访问文档但包含很少使用的数据时。嵌入只会增加内存需求,因此引用更有意义。
- 当文档大小超过 MongoDB 的 16MB 文档限制时。这可能在建模多:1 关系(例如,employees:department)时发生。
检查分析和查询模式
对于大多数开发人员来说,优化性能的第一步是了解实际和预期的查询模式。一旦您足够了解应用程序的查询模式,您就可以创建数据模型并选择适当的索引。
MongoDB 开发人员可以使用各种强大的工具来提高性能。但这并不意味着可以忽略查询配置文件和模式。
例如,提高性能的一种简单方法是分析查询模式并了解可以在何处嵌入数据。在确定主要查询模式后提高 MongoDB 性能的其他方法包括:
- 确保您查询的任何字段都有索引。
- 存储文档上频繁子查询的结果,以减少读取负载。
- 查看您的日志以查看慢速查询,然后检查您的索引。
查看数据索引和建模
在制作数据模型时,您将决定如何对数据之间的关系进行建模。例如,选择何时嵌入文档而不是在不同集合中的不同文档之间创建引用,这是特定于应用程序考虑的一个示例。
JSON 文档的一个主要优点是它们允许开发人员根据应用程序的要求对数据进行建模。嵌套子文档和数组可帮助您利用简单的文本文档对数据之间的复杂关系进行建模。
您还可以使用 MongoDB 对以下内容进行建模:
- 地理空间数据
- 表格、扁平和柱状结构
- 简单的键值对
- 时间序列数据
- 连通图数据结构等的边和节点
监控分片和复制
复制对于提高性能至关重要,因为它通过水平扩展提高了数据可用性。复制可以通过冗余带来更好的性能和更高的安全性。
性能监控可能很麻烦,需要额外的资源和时间来确保顺利运行。您可以利用市场上可用的性能监控工具来满足您的特定需求。
例如,一些 APM 工具可以获取有关 WordPress 站点的 MySQL 数据库查询、PHP 进程、外部 HTTP 调用等的时间戳信息。您还可以使用此免费工具进行调试:
- 长 API 调用
- 长外部 URL 请求
- 缓慢的数据库查询仅举几例。
在 MongoDB 中,可以通过副本集实现复制,该副本集允许开发人员从主节点或服务器跨多个辅助节点复制数据。这使您的复制可以在辅助节点而不是主节点上运行一些查询,从而避免争用并实现更好的负载平衡。
MongoDB 中的分片集群是另一种可能提高性能的方法。与复制类似,分片可用于将大型数据集分布在多个服务器上。
通过利用分片键,开发人员可以跨多个服务器复制分片或数据片段。这些服务器可以协同工作以使用所有数据。
分片具有相当多的优势,包括写入/读取的水平扩展、更高的可用性和更大的存储容量。
确定内存使用
当应用程序的工作集(即经常访问的数据和索引)可以毫无问题地放入内存时,MongoDB 的性能最佳。虽然其他因素对性能至关重要,但 RAM 大小对于实例大小来说是最重要的。
当应用程序的工作集适合 RAM 时,磁盘的读取活动需要很低。但是,如果您的工作集超过了实例服务器的 RAM 或大小,则读取活动将开始激增。
如果您看到这种情况发生,您可以通过转移到具有更多内存的更大实例来解决问题。
将多值字段放在末尾
如果您正在索引几个字段,并且您要查询的字段之一使用这些“多值”运算符之一,那么您应该将它们放在索引的末尾。您需要对索引进行排序,以便精确值的查询字段排在最前面,而“多值”运算符在索引中显示在最后。
一个例外是对字段进行排序。将它们放在“多值”和精确字段之间,以减少所需的内存排序量。
小结
对于 MongoDB,速度就是游戏的名称。为了快速返回查询,MongoDB 利用运算符来执行数学或逻辑任务。简单来说,了解 MongoDB 算子是掌握 MongoDB 的关键。
本文重点介绍了一些可用于数据的关键 MongoDB 运算符,例如比较运算符、逻辑运算符、元运算符和投影运算符等。它还可以帮助您了解如何使用 MongoDB 运算符以及让您充分利用它们的最佳实践。
原文地址:https://www.wbolt.com/mongodb-operators.html