【Mongo】MongoDB文档查询
系列文章索引:
【Mongo】初步认识MongoDB shell
【Mongo】MongoDB文档的增删改操作
数据查询
find()
方法
方法说明
find()
方法没有参数时会匹配集合中的所有内容,find({})
和find()
功能一样find({"_id":1})
表示查询_id
字段为 1 的文档;find({"_id":1,"name":"salta"})
多个筛选条件表示为and
的关系,表示查询_id=1 and name='salta'
的文档;增加查询条件也一样find({},{"price":0,"_id":0})
表示从查询结果中剔除price
和_id
字段;find({},{"name":1,"price":1})
表示从查询结果中只保留name
和price
字段。find()
方法中的筛选条件必须是常量
注意:第二个参数中如果是保留字段,则必须都是保留字段;如果是剔除字段,则必须都是剔除字段。否则 mongodb 会报错。但_id
字段不受此规则限制,可以随意剔除。举例如下:
//以下代码会报错
db.game.find({},{name: 1,price: 0,}
);
//以下代码能正常运行
db.game.find({},{name: 1,_id: 0,}
);
运行效果如下:
> db.game.find({},{... "name":1,
... "price":0
... })
Error: error: {"ok" : 0,"errmsg" : "Cannot do exclusion on field price in inclusion projection","code" : 31254,"codeName" : "Location31254"
}
> db.game.find({},{... "name":1,
... "_id":0
... })
{ "name" : "salta legend" }
{ "name" : "monster hunter: rise" }
{ "name" : "monster hunter: world" }
{ "name" : "bayonetta" }
{ "name" : "nier replicant" }
{ "name" : "god of war" }
{ "name" : "Nioh 2" }
条件查询
$lt
,$lte
,$gt
,$gte
,$ne
$lt
小于$lte
小于等于$gt
大于$gte
大于等于$ne
不等于,$ne
可用于任意数据类型
以下代码用来查询价格大于 20,且价格小于等于 280 的 document
db.game.find({price: {$gt: 20,$lte: 280,},
});
运行效果如下:
> db.game.find({... "price":{... "$gt":20,
... "$lte":280
... }
... })
{ "_id" : 1, "name" : "monster hunter: world", "price" : 128, "platform" : [ "steam" ], "comments" : [ { "author" : "xilia", "content" : "hello salta", "hidden" : true }, { "author" : "salta" }, { "author" : "link" }, { "author" : "link" } ] }
{ "_id" : ObjectId("62ba596511721fa1455eb94b"), "name" : "nier replicant", "company" : "square enix", "price" : 180 }
{ "_id" : ObjectId("62bd39540a71331919b6295e"), "name" : "god of war", "price" : 30 }
$or
$or
运算符用于表示两个是筛选条件是或的关系,以下代码用来筛选价格大于280或者价格小于等于20的document
db.game.find({"$or":[{"price":{"$gt":280}},{"price":{"$lte":20}}
]})
代码运行效果如下:
> db.game.find({"$or":[
... {"price":{"$gt":280}},
... {"price":{"$lte":20}}
... ]})
{ "_id" : ObjectId("62b9515ba8e0e5b80c720d94"), "name" : "salta legend", "orderDate" : ISODate("2022-06-27T06:42:35.640Z"), "price" : 327 }
{ "_id" : ObjectId("62ba565711721fa1455eb949"), "name" : "monster hunter: rise", "orderDate" : ISODate("2022-06-28T01:16:07.114Z"), "price" : 298 }
{ "_id" : ObjectId("62ba596511721fa1455eb94a"), "name" : "bayonetta", "company" : "nintendo", "price" : 318 }
$in
和$nin
$in
用来筛选在某些列出的范围内的属性(和sql语句中的in关键字相同)
$nin
是not in的意思
以下代码用来查询价格在[327,30]两个数字中的结果:
db.game.find({"price":{"$in":[327,30]
}})
代码运行效果如下:
> db.game.find({"price":{... "$in":[327,30]
... }})
{ "_id" : ObjectId("62b9515ba8e0e5b80c720d94"), "name" : "salta legend", "orderDate" : ISODate("2022-06-27T06:42:35.640Z"), "price" : 327 }
{ "_id" : ObjectId("62bd39540a71331919b6295e"), "name" : "god of war", "price" : 30 }
$mod
$mod
为取模运算符,以下代码用来筛选出 对price模3余0的document。
db.game.find({"price":{"$mod":[3,0]
}})
代码运行效果为:
> db.game.find({"price":{... "$mod":[3,0]
... }})
{ "_id" : ObjectId("62b9515ba8e0e5b80c720d94"), "name" : "salta legend", "orderDate" : ISODate("2022-06-27T06:42:35.640Z"), "price" : 327 }
{ "_id" : ObjectId("62ba596511721fa1455eb94a"), "name" : "bayonetta", "company" : "nintendo", "price" : 318 }
{ "_id" : ObjectId("62ba596511721fa1455eb94b"), "name" : "nier replicant", "company" : "square enix", "price" : 180 }
{ "_id" : ObjectId("62bd39540a71331919b6295e"), "name" : "god of war", "price" : 30 }
$not
$not
表示取反,可用于描述其他运算符,以下代码用来筛选出 不满足对price模3余0的document
db.game.find({"price":{"$not":{"$mod":[3,0]}
}})
代码运行效果如下:
> db.game.find({"price":{... "$not":{... "$mod":[3,0]
... }
... }})
{ "_id" : ObjectId("62ba565711721fa1455eb949"), "name" : "monster hunter: rise", "orderDate" : ISODate("2022-06-28T01:16:07.114Z"), "price" : 298 }
{ "_id" : 1, "name" : "monster hunter: world", "price" : 128, "platform" : [ "steam" ], "comments" : [ { "author" : "xilia", "content" : "hello salta", "hidden" : true }, { "author" : "salta" }, { "author" : "link" }, { "author" : "link" } ] }
{ "_id" : ObjectId("62bd3d9f0a71331919b6297a"), "name" : "Nioh 2", "published" : ISODate("2022-06-30T06:08:30.014Z") }
类型查询
关于null类型的说明
当查询条件设置为{"platform":null}
时,既可以查到platform=null
的文档,也可以查到不存在platform
字段的文档。如果只希望得到platform=null
的文档,可以通过$exits
条件确定该字段存在。
以下代码用来筛选出platform
字段存在,并且为空的文档:
db.game.find({"platform":{"$eq":null,"$exists":true
}})
代码运行效果如下:
> db.game.insertMany([{name:"it takes two",platform:null},{name:"naraka",platform:""},{name:"gta5"}])
{"acknowledged" : true,"insertedIds" : [ObjectId("62be8e82718b6cda80d080aa"),ObjectId("62be8e82718b6cda80d080ab"),ObjectId("62be8e82718b6cda80d080ac")]
}
> db.game.find({"platform":{... "$eq":null,
... "$exists":true
... }})
{ "_id" : ObjectId("62be8e82718b6cda80d080aa"), "name" : "it takes two", "platform" : null }
如果只进行null筛选,效果如下:
> db.game.find({"platform":null})
{ "_id" : ObjectId("62b9515ba8e0e5b80c720d94"), "name" : "salta legend", "orderDate" : ISODate("2022-06-27T06:42:35.640Z"), "price" : 327 }
{ "_id" : ObjectId("62ba565711721fa1455eb949"), "name" : "monster hunter: rise", "orderDate" : ISODate("2022-06-28T01:16:07.114Z"), "price" : 298 }
{ "_id" : ObjectId("62ba596511721fa1455eb94a"), "name" : "bayonetta", "company" : "nintendo", "price" : 318 }
{ "_id" : ObjectId("62ba596511721fa1455eb94b"), "name" : "nier replicant", "company" : "square enix", "price" : 180 }
{ "_id" : ObjectId("62bd39540a71331919b6295e"), "name" : "god of war", "price" : 30 }
{ "_id" : ObjectId("62bd3d9f0a71331919b6297a"), "name" : "Nioh 2", "published" : ISODate("2022-06-30T06:08:30.014Z") }
{ "_id" : ObjectId("62be8e82718b6cda80d080aa"), "name" : "it takes two", "platform" : null }
{ "_id" : ObjectId("62be8e82718b6cda80d080ac"), "name" : "gta5" }
正则表达式
$regex
运算符用来在查询中对字符串进行正则表达时匹配,正则表达式也遵循JavaScript的正则表达式语法。如,以下代码用来查询name字段以n或N开头的文档:
db.game.find({"name":{"$regex":/^n/i
}})
查询结果如下:
> db.game.find({"name":{... "$regex":/^n/i
... }})
{ "_id" : ObjectId("62ba596511721fa1455eb94b"), "name" : "nier replicant", "company" : "square enix", "price" : 180 }
{ "_id" : ObjectId("62bd3d9f0a71331919b6297a"), "name" : "Nioh 2", "published" : ISODate("2022-06-30T06:08:30.014Z") }
{ "_id" : ObjectId("62be8e82718b6cda80d080ab"), "name" : "naraka", "platform" : "" }
数组查询
测试用法之前先新增数据:
db.game.updateOne({name:"naraka"},{$set:["it is so good","it is so pretty","i am a old gamer","it`s my like","i like sanniang"]}
)
数组内容匹配方式:
完全匹配整个数组
db.game.find({"comments":["abc","def"]})
用来筛选出数组comments
字段值为['abc','def']
的文档,这种用法和精确匹配完全一样。匹配单个数组元素
db.game.find({"comments":"it is so good"})
用来筛选出数组comments
字段值中包含it is so good
元素的文档匹配多个数组元素,
$all
db.game.find({"comments":{"all":["it is so good","i am a old gamer"]}})
用来筛选出数组元素中包含it is so good
和i am a old gamer
两个元素的文档。如果$all
的数组中只有一个元素,那么使用$all
和不适用$all
结果一样,即:{"author":{"$all":["apple"]}}
和{"author":"apple"}
的筛选结果完全一样匹配数组长度
$size
运算符用于匹配数组长度,但$size
只能是常数,且$size
不能和$lt
等比较运算符同时使用。如果需要比较数组的长度,可以单独增加一个size
字段,每次修改数组同时修改这个size
字段,这在mongoDB中很容易实现。db.game.find({"comments":{"$size":3}})
用于筛选comments
数组长度为3的文档。匹配数组元素对象的属性
db.game.find({"comments.author":"salta"})
用于筛选comments
数组中元素的author
属性为salta的文档
控制返回值数组的长度
过滤返回值中显示的数组长度可以使用$slice
修饰符,整数n表示返回前n条数据,负数n表示返回后n条数据。以下代码用于返回comments
字段的前3个元素:
db.game.find({"name":"naraka"},{"comments":{"$slice":3}}
)
运行效果如下:
> db.game.find(
... {"name":"naraka"},
... {"comments":{"$slice":3}}
... )
{ "_id" : ObjectId("62be8e82718b6cda80d080ab"), "name" : "naraka", "platform" : "", "comments" : [ "it is so good", "it is so pretty", "i am a old gamer" ] }
此方法也可以返回数组中的一段数据,以下代码会返回comments
字段的第2~第4个元素(忽略前1个元素,从第2个开始返回3个元素)
db.game.find({"name":"naraka"},{"comments":{"$slice":[1,3]}}
)
显示效果如下:
> db.game.find(
... {"name":"naraka"},
... {"comments":{"$slice":[1,3]}}
... )
{ "_id" : ObjectId("62be8e82718b6cda80d080ab"), "name" : "naraka", "platform" : "", "comments" : [ "it is so pretty", "i am a old gamer", "it`s my like" ] }
只返回数组中匹配到的第一个元素
可以通过$
运算符来返回匹配到的元素,这种方法只能返回匹配到的第一个元素,返回值的数组中只有一个元素。以下代码用于筛选comments
数组字段中的元素对象的author
属性,返回值中只有_id
和comments
字段,且comments
字段中只有匹配到的第一个元素。
db.game.find({"comments.author":"salta"},{"comments.$":1}
)
代码运行效果如下:
> db.game.find(
... {"comments.author":"salta"},
... {"comments.$":1}
... )
{ "_id" : 1, "comments" : [ { "author" : "salta" } ] }
{ "_id" : ObjectId("62be8e82718b6cda80d080ab"), "comments" : [ { "author" : "salta", "content" : "i am salta" } ] }
数组与范围查询
有以下数据:
db.number.insertMany([{"num":6},{"num":66},{"num":666},{"num":[6,66]},{"num":[6,666]},{"num":[6,66,666]}
])
有以下查询代码:
db.number.find({"num":{"$gt":10,"$lt":100
}})
此代码目的是查找 10 < n u m < 100 10<num<100 10<num<100的数据,期望能返回的数据为:
{"num":66},
但,实际返回的数据为:
{"num" : 66 }
{"num" : [ 6, 66 ] }
{"num" : [ 6, 666 ] }
{"num" : [ 6, 66, 666 ] }
因为,在数组的比较中,满足6<100
和666>66>10
,所以实际返回值和期望返回值产生了差别,这就使得针对数组的范围查询失效。
这种现象可以通过$elemMatch
表达式解决,但是$elemMatch
表达式只能匹配数组元素,对于非数组元素回被跳过。效果如下:
> db.number.find().pretty()
{ "_id" : ObjectId("62beb40a718b6cda80d080ad"), "num" : 6 }
{ "_id" : ObjectId("62beb40a718b6cda80d080ae"), "num" : 66 }
{ "_id" : ObjectId("62beb40a718b6cda80d080af"), "num" : 666 }
{ "_id" : ObjectId("62beb40a718b6cda80d080b0"), "num" : [ 6, 66 ] }
{ "_id" : ObjectId("62beb40a718b6cda80d080b1"), "num" : [ 6, 666 ] }
{"_id" : ObjectId("62beb40a718b6cda80d080b2"),"num" : [6,66,666]
}
> db.number.find({"num":{"$elemMatch":{"$gt":10,"$lt":100}}})
> //查询结果为空。可见,{"num":66}未参与筛选
对于以上现象,可以通过对num
字段添加索引,之后使用min和max方法将筛选条件的索引范围限制在最大和最小值之间的形式予以解决。具体代码如下:
//已对num字段创建索引
db.number.find({"num":{"$gt":10,"$lt":100
}}).min({"num":10}).max({"num":100})
内嵌文档查询
如果想要通过以下方式查询一个子文档,那么这个被查询必须精确的匹配整个文档(包括各个属性的顺序)。即,如果文档的内容为{"author":"xilia","content":"hello salta"}
,则能匹配成功;但是如果文档中存在更多其他的属性,如{"author":"xilia","content":"hello salta","hidden":true}
则这种匹配方式不能查询出对应的结果。
db.game.find({"comments":{"author":"xilia","content":"hello salta"
}})
更重要的一点是,除了需要匹配整个文档之外,这种查询方式还是强顺序相关的,两个条件的顺序必须符合要求才能成功匹配。
在这中情况下,需要使用点调用1的方式
db.game.find({"comments.author":"xilia","comments.content":"hello salta"
})
效果如下:
> db.game.find({"comments":{... "author":"xilia",
... "content":"hello salta"
... }})
> db.game.find({... "comments.author":"xilia",
... "comments.content":"hello salta"
... })
{ "_id" : 1, "name" : "monster hunter: world", "price" : 128, "platform" : [ "steam" ], "comments" : [ { "author" : "xilia", "content" : "hello salta", "hidden" : true }, { "author" : "salta" }, { "author" : "link" }, { "author" : "link" } ] }
$where
查询
对于有些特殊的查询,无法通过以上任何一种方式表示出来,mongoDB提供了$where子句
这种查询方式。$where子句
允许在查询中执行JavaScript代码,这样就能在JavaScript代码中执行某些特殊的比较逻辑。但是为了安全和效率的要求,应该尽量减少或消除使用$where
,并且禁止普通用户使用。
在$where
查询过程中,每个文档都必须从BSON
转换成JavaScript对象
,而且$where
查询也不能使用索引,所以有很大的性能问题。
具体使用方法如下:
/*** 查询文档中存在两个字段满足以下条件:* 1.其中一个字段是日期类型,并且时间在当前日期之前;* 2.另一个字段是数字类型,并且这个数字小于300。*/
db.game.find({"$where":function(){for(var param_1 in this){for(var param_2 in this){return this[param_1]<new Date() && this[param_2]<300;}}return false;
}}).pretty()
执行效果如下:
> db.game.find({"$where":function(){... for(var param_1 in this){... for(var param_2 in this){... return this[param_1]<new Date() && this[param_2]<300;
... }
... }
... return false;
... }}).pretty()
{"_id" : 1,"name" : "monster hunter: world","price" : 128,"platform" : ["steam"],"comments" : [{"author" : "xilia","content" : "hello salta","hidden" : true},{"author" : "salta"},{"author" : "link"},{"author" : "link"}]
}
游标
游标基础
find()
方法的返回值是一个游标对象,通过对游标对象的各种操作,可以对查询结果进行输出控制。(在mongoDBshell中执行find()
方法的打印操作是shell自动执行的结果)游标对象提供了hasNex()
和next()
两个方法,典型的使用方式为:
var cursor=db.game.find()
while(cursor.hasNext()){var item=cursor.next()print(item.name)
}
同时游标对象也实现了迭代器接口,可以在forEach()
方法中进行操作,使用方式如下:
var cursor=db.game.find();
cursor.forEach(res=>{print(res.name)
})
注意:当调用find()
方法时,shell并不会立即执行查询操作,而是真正开始获取结果时才执行查询操作。所以可以在查询之前进行一些额外的操作,如:排序、分页、限制长度等,这些操作的返回值仍然是一个游标对象。
因此,以下三行代码返回的数据相同:
db.game.find().sort({"price":1}).limit(3).skip(2).pretty();
db.game.find().sort({"price":1}).skip(2).limit(3).pretty();
db.game.find().limit(3).sort({"price":1}).skip(2).pretty();
db.game.find().limit(3).skip(2).sort({"price":1}).pretty();
db.game.find().skip(2).limit(3).sort({"price":1}).pretty();
db.game.find().skip(2).sort({"price":1}).limit(3).pretty();
当hasNext()
方法被调用后,查询条件会发送到服务端,之后shell会获取一段查询结果(这里的一段是100个结果或4MB数据中的较小值),在客户端遍历完这些数据后,shell会再次链接数据库,使用getMore
获取更多数据。
limit()
、skip()
、sort()
这三个函数的使用方法如下:
//限制只返回三个结果
db.game.find().limit(3)//跳过前三条数据
db.game.find().skip(3)//按价格升序排序,-1表示降序
db.game.find().sort({"price":1})
以上三个方法可以结合使用。对于一个字段的数据包含多种类型时,mongoDB预设了排序顺序,从最小值到最大值顺序为:
序号 | 类型 |
---|---|
1 | 最小值 |
2 | null |
3 | 数字 |
4 | 字符串 |
5 | 对象、文档 |
6 | 数组 |
7 | 二进制数据 |
8 | 对象ID |
9 | 布尔 |
10 | 日期 |
11 | 时间戳 |
12 | 正则表达式 |
13 | 最大值 |
注意:当skip()
方法跳过大量数据时会产生性能问题,所以对于大数据量的分页操作,通常不用skip()
方法实现。假设有一个需求,需要使用create_date
字段降序排列,可以通过如下方式操作:
var page_1=db.game.find().sort({"create_date":-1}).limit(20);
var latest=null;
while(page_1.hasNext()){latest=page_1.next();//do sth
}
var page_2=db.game.find({"create_date":{"$lt":latest.create_date}
}).sort({"create_date":-1}).limit(100)
游标的生命周期
一个游标会占用服务器的资源,在满足一定的条件之后,服务端会释放这个游标的资源,这些条件包括:
- 游标遍历完所有的结果
- 客户端要求终止该游标
- 当游标超出客户端的作用域,驱动程序会向数据库发送一条 “可以销毁该游标” 的消息
- 一个游标如果10分钟内没有被使用,数据库会自动销毁
<点调用>这个名称是作者为了方便称呼杜撰出来的,没有官方文档支持 ↩︎
【Mongo】MongoDB文档查询相关推荐
- MongoDB文档查询操作(三)
关于MongoDB中的查询,我们已经连着介绍了两篇文章了,本文我们来介绍另外一个查询概念游标. 本文是MongoDB系列的第七篇文章,了解前面的文章有助于更好的理解本文: 1.Linux上安装Mong ...
- MongoDB文档查询操作(一)
上篇文章我们主要介绍了MongoDB的修改操作,本文我们来看看查询操作. 本文是MongoDB系列的第五篇文章,了解前面的文章有助于更好的理解本文: 1.Linux上安装MongoDB 2.Mongo ...
- 前端学习(1337):mongoDB文档查询
const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/playground', { useUnifie ...
- Python+Streamlit aggrid+MongoDB GridFS构建低代码文档管理应用(文档查询下载实用篇)
1. Sreamlit aggrid简介 Sreamlit aggrid是Streamlit的Ag-Grid组件的实现,在Python Streamlit框架下,更加灵活的使用表格,包括分组.排序.编 ...
- spring mongodb内嵌文档查询
spring mongodb内嵌文档查询 代码示例 简化写法 spring mongodb内嵌文档查询示例. {"name": "zsParent", &quo ...
- MongoDB内嵌文档查询
MongoDB内嵌文档查询 示例数据结构 [{"name": "lisa", "age": 17,"friends": ...
- Mongo Shell 文档搜索
Mongo Shell 文档搜索 MongoDB支持执行字符串内容的文本搜索的查询操作,通过文本索引和 $text 运算符实现. $text 文本搜索 练习用到的数据: db.stores.inser ...
- mongoDB 文档插入
mongoDB文档插入与SQL表insert方式基本相同.在关系数据库中,我们需要先定义表,然后才能将记录插入到文档,而在mongoDB中,由于无需预定义模式,因此,在集合不存在的情形下,直接inse ...
- MongoDB——文档操作(更新文档)
目录 一.更新文档命令的格式 二.更新操作符 三.更新单个文档 3.1.更新单个文档的示例 四.更新多个文档 4.1.更新多个文档的概述 4.2.更新多个文档的示例 五.使用upsert命令 5.1. ...
最新文章
- Voat —— 基于 ASP.NET 的 Reddit 高仿系统
- C#如何把List of Object转换成List of T具体类型
- 2018-07-09--记录一次gitlab迁移事件及遇到的问题
- maven 亲测可用国内镜像 阿里云
- 有意思的前端函数面试题
- 决策树构建算法之—C4.5
- 如何deactivate Material delta download
- DevExrepss的Grid属性设置及常用方法总结
- 中国联通回应5G入网问题:尚未对公众客户开放
- 【报告分享】2021微信视频号生态洞察报告.pdf(附下载链接)
- centos7下扩充swap空间
- 内置模块(time、random、hashlib、os)
- Ubuntu 安装远程桌面
- OptiSystem器件学习(一)
- B站百万粉丝是如何做起来的?解密UP主成长之路
- word中html在哪,如何word中显示部分的域代码在哪
- 商品详情页实现价格区间价
- 学习笔记2 - 利用元数据管理数据质量
- 【error】_smartbi数据集超出最大行数: DataRows > 1000
- 矽力杰SY8088国产代替料RY3408