本文将会介绍 MongoDB 中的两个元素查询运算符:$exists 以及 $type。
$exists 运算符
$exists 是一个元素查询运算符,语法如下:
{ field: { $exists: <boolean_value> } }
如果 <boolean_value> 设置为 true,$exists 运算符将会匹配指定字段存在数值的文档,数值可以是 null。
如果 <boolean_value> 设置为 false,$exists 运算符将会匹配不包含指定字段的文档。
MongoDB 中的 $exists 运算符并不等价于 SQL 中的 EXISTS 运算符。
从 MongoDB 4.2 开始,$type: 0 不等价于 $exists:false。
接下来的示例将会使用以下 products 集合:
db.products.insertMany([
{ "_id" : 1, "name" : "xPhone", "price" : 799, "releaseDate" : ISODate("2011-05-14T00:00:00Z"), "spec" : { "ram" : 4, "screen" : 6.5, "cpu" : 2.66 }, "color" : [ "white", "black" ], "storage" : [ 64, 128, 256 ] },
{ "_id" : 2, "name" : "xTablet", "price" : 899, "releaseDate" : ISODate("2011-09-01T00:00:00Z"), "spec" : { "ram" : 16, "screen" : 9.5, "cpu" : 3.66 }, "color" : [ "white", "black", "purple" ], "storage" : [ 128, 256, 512 ] },
{ "_id" : 3, "name" : "SmartTablet", "price" : 899, "releaseDate" : ISODate("2015-01-14T00:00:00Z"), "spec" : { "ram" : 12, "screen" : 9.7, "cpu" : 3.66 }, "color" : [ "blue" ], "storage" : [ 16, 64, 128 ] },
{ "_id" : 4, "name" : "SmartPad", "price" : 699, "releaseDate" : ISODate("2020-05-14T00:00:00Z"), "spec" : { "ram" : 8, "screen" : 9.7, "cpu" : 1.66 }, "color" : [ "white", "orange", "gold", "gray" ], "storage" : [ 128, 256, 1024 ] },
{ "_id" : 5, "name" : "SmartPhone", "price" : 599, "releaseDate" : ISODate("2022-09-14T00:00:00Z"), "spec" : { "ram" : 4, "screen" : 9.7, "cpu" : 1.66 }, "color" : [ "white", "orange", "gold", "gray" ], "storage" : [ 128, 256 ] },
{ "_id" : 6, "name" : "xWidget", "spec" : { "ram" : 64, "screen" : 9.7, "cpu" : 3.66 }, "color" : [ "black" ], "storage" : [ 1024 ] },
{ "_id" : 7, "name" : "xReader","price": null, "spec" : { "ram" : 64, "screen" : 6.7, "cpu" : 3.66 }, "color" : [ "black", "white" ], "storage" : [ 128 ] }
])
以下示例使用 $exists 运算符查找包含 price 字段的文档:
db.products.find(
{
price: {
$exists: true
}
},
{
name: 1,
price: 1
}
)
查询返回的文档如下:
{ "_id" : 1, "name" : "xPhone", "price" : 799 }
{ "_id" : 2, "name" : "xTablet", "price" : 899 }
{ "_id" : 3, "name" : "SmartTablet", "price" : 899 }
{ "_id" : 4, "name" : "SmartPad", "price" : 699 }
{ "_id" : 5, "name" : "SmartPhone", "price" : 599 }
{ "_id" : 7, "name" : "xReader", "price" : null }
以下查询使用 $exists 运算符查找包含 price 字段并且价格大于 799 的文档:
db.products.find({
price: {
$exists: true,
$gt: 699
}
}, {
name: 1,
price: 1
});
输出结果如下:
{ "_id" : 1, "name" : "xPhone", "price" : 799 }
{ "_id" : 2, "name" : "xTablet", "price" : 899 }
{ "_id" : 3, "name" : "SmartTablet", "price" : 899 }
下面的示例使用 $exists 运算符查找不包含 price 字段的文档:
db.products.find({
price: {
$exists: false
}
}, {
name: 1,
price: 1
});
查询返回了没有价格的产品:
{ "_id" : 6, "name" : "xWidget" }
$type 运算符
有时候我们需要处理非结构化的数据,而它们没有明确的数据类型。此时,我们就需要使用 $type 运算符。
$type 是一个元素查询运算符,可以查找字段为指定 BSON 类型的文档。
$type 运算符的语法如下:
{ field: { $type: <BSON type> } }
$type 运算符也支持 BSON 类型组成的列表参数:
{ field: { $type: [ <BSON type1> , <BSON type2>, ... ] } }
以上语法中,$type 运算符可以查找字段属于参数列表中任一 BSON 类型的文档。
MongoDB 提供了三种指定 BSON 类型的方法:类型名称、数字编号以及别名。下表列出了这三种方法对应的 BSON 类型:
类型 | 编号 | 别名 |
---|---|---|
Double | 1 | “double” |
String | 2 | “string” |
Object | 3 | “object” |
Array | 4 | “array” |
Binary data | 5 | “binData” |
ObjectId | 7 | “objectId” |
Boolean | 8 | “bool” |
Date | 9 | “date” |
Null | 10 | “null” |
Regular Expression | 11 | “regex” |
JavaScript | 13 | “javascript” |
32-bit integer | 16 | “int” |
Timestamp | 17 | “timestamp” |
64-bit integer | 18 | “long” |
Decimal128 | 19 | “decimal” |
Min key | -1 | “minKey” |
Max key | 127 | “maxKey” |
另外,别名“number”可以匹配以下 BSON 类型:
- double
- 32-bit integer
- 64-bit integer
- decimal
下面的示例我们需要使用新的 products 集合:
db.products.insertMany([
{ "_id" : 1, "name" : "xPhone", "price" : "799", "releaseDate" : ISODate("2011-05-14T00:00:00Z"), "spec" : { "ram" : 4, "screen" : 6.5, "cpu" : 2.66 }, "color" : [ "white", "black" ], "storage" : [ 64, 128, 256 ] },
{ "_id" : 2, "name" : "xTablet", "price" : NumberInt(899), "releaseDate" : ISODate("2011-09-01T00:00:00Z"), "spec" : { "ram" : 16, "screen" : 9.5, "cpu" : 3.66 }, "color" : [ "white", "black", "purple" ], "storage" : [ 128, 256, 512 ] },
{ "_id" : 3, "name" : "SmartTablet", "price" : NumberLong(899), "releaseDate" : ISODate("2015-01-14T00:00:00Z"), "spec" : { "ram" : 12, "screen" : 9.7, "cpu" : 3.66 }, "color" : [ "blue" ], "storage" : [ 16, 64, 128 ] },
{ "_id" : 4, "name" : "SmartPad", "price" : [599, 699, 799], "releaseDate" : ISODate("2020-05-14T00:00:00Z"), "spec" : { "ram" : 8, "screen" : 9.7, "cpu" : 1.66 }, "color" : [ "white", "orange", "gold", "gray" ], "storage" : [ 128, 256, 1024 ] },
{ "_id" : 5, "name" : "SmartPhone", "price" : ["599",699], "releaseDate" : ISODate("2022-09-14T00:00:00Z"), "spec" : { "ram" : 4, "screen" : 9.7, "cpu" : 1.66 }, "color" : [ "white", "orange", "gold", "gray" ], "storage" : [ 128, 256 ] },
{ "_id" : 6, "name" : "xWidget", "spec" : { "ram" : 64, "screen" : 9.7, "cpu" : 3.66 }, "color" : [ "black" ], "storage" : [ 1024 ] }
])
products 集合中的 price 字段可能是 int、double 或者 long 类型。
以下示例使用 $type 运算符查找 price 字段属于字符串类型,或者 price 字段是包含字符串元素的数组的文档:
db.products.find({
price: {
$type: "string"
}
}, {
name: 1,
price: 1
})
查询返回的结果如下:
{ "_id" : 1, "name" : "xPhone", "price" : "799" }
{ "_id" : 5, "name" : "SmartPhone", "price" : [ "599", 699 ] }
字符串类型对应编号为 2,我们也可以在查询中使用数字编号 2 实现相同的结果:
db.products.find({
price: {
$type: 2
}
}, {
name: 1,
price: 1
})
以下示例使用 $type 运算符和别名“number”查找 price 字段属于 int、long 或者 double 类型,或者 price 字段是包含数字的数组的文档:
db.products.find({
price: {
$type: "number"
}
}, {
name: 1,
price: 1
})
查询返回的文档如下:
{ "_id" : 2, "name" : "xTablet", "price" : 899 }
{ "_id" : 3, "name" : "SmartTablet", "price" : NumberLong(899) }
{ "_id" : 4, "name" : "SmartPad", "price" : [ 599, 699, 799 ] }
{ "_id" : 5, "name" : "SmartPhone", "price" : [ "599", 699 ] }
以下示例使用 $type 运算符查找 price 字段属于数字或者字符串类型,或者 price 字段是包含数字或者字符串元素的数组的文档:
db.products.find({
price: {
$type: ["number", "string"]
}
}, {
name: 1,
price: 1
})
查询返回的结果如下:
{ "_id" : 1, "name" : "xPhone", "price" : "799" }
{ "_id" : 2, "name" : "xTablet", "price" : 899 }
{ "_id" : 3, "name" : "SmartTablet", "price" : NumberLong(899) }
{ "_id" : 4, "name" : "SmartPad", "price" : [ 599, 699, 799 ] }
{ "_id" : 5, "name" : "SmartPhone", "price" : [ "599", 699 ] }
查询结果中没有包含 _id 等于 6 的文档,因为该文档中没有 price 字段。