南宁百度网站设计,wordpress主题chuxia,品牌学习网站,网络规划设计师2024年考试#x1f601; 作者简介#xff1a;一名大四的学生#xff0c;致力学习前端开发技术 ⭐️个人主页#xff1a;夜宵饽饽的主页 ❔ 系列专栏#xff1a;MongoDB数据库 #x1f450;学习格言#xff1a;成功不是终点#xff0c;失败也并非末日#xff0c;最重要的是继续前… 作者简介一名大四的学生致力学习前端开发技术 ⭐️个人主页夜宵饽饽的主页 ❔ 系列专栏MongoDB数据库 学习格言成功不是终点失败也并非末日最重要的是继续前进的勇气 前言 这里是关于MongoDB中查询语句find的使用其中对于特定类型的查询非常特别还有游标的使用可以加快我们的查询速度这是我的学习MongoDB笔记希望可以帮助到大家欢迎大家的补充和纠正 文章目录 第4章 查询4.1 find简介4.1.1 指定要返回的键 4.3 特定类型查询4.3.3 查询数组之$slice操作符4.3.5 查询数组之数组与范围查询的相互作用 4.4 $where查询4.5 游标4.5.1 limit,skip和sort4.5.2 避免略过大量结果4.5.3 游标生命周期 第4章 查询
4.1 find简介
4.1.1 指定要返回的键
概念find方法中的第二个参数就是投影条件投影条件是一个文档其中键是要包含或排除的字段对应的值为 1 表示包含0 表示排除。默认情况下如果不指定投影条件MongoDB 会返回文档中的所有字段 案例
默认情况下如果不指定投影条件MongoDB 会返回文档中的所有字段 db.users.find({}, {username : 1, email : 1})
{_id : ObjectId(4ba0f0dfd22aa494fd523620),username : joe,email : joeexample.com
}2.可能在文档中有很多键而你不希望结果中包含 “fatal_weakness” 键 db.users.find({}, {fatal_weakness : 0})3.要将“_id“键从返回结果剔除 db.users.find({}, {username : 1, _id : 0})
{username : joe
}4.3 特定类型查询
4.3.3 查询数组之$slice操作符
❗注意点除非特别指定否则在使用 “$slice” 时会返回文档中的所有键。这与其他的键指定符不同后者不会返回未指定的键。例如有这样一个关于博客文章的文档 例子
{_id : ObjectId(4b2d75476cc613d5ee930164),title : A blog post,content : ...,comments : [{name : joe,email : joeexample.com,content : nice post.},{name : bob,email : bobexample.com,content : good post.}]
} db.blog.posts.findOne(criteria, {comments : {$slice : -1}})
{_id : ObjectId(4b2d75476cc613d5ee930164),title : A blog post,content : ...,comments : [{name : bob,email : bobexample.com,content : good post.}]
}即使title和content没有显式地被包含在键指定符中但它们依然被返回了
4.3.5 查询数组之数组与范围查询的相互作用
文档中的标量非数组元素必须与查询条件中的每一条子句相匹配。如果使用 {“x” : {“ g t : 10 , gt : 10, gt:10,lt” : 20}} 进行查询那么 “x” 必须同时满足大于 10 且小于 20。然而如果文档中的 “x” 字段是一个数组那么当 “x” 键的某一个元素与查询条件的任意一条语句相匹配查询条件中的每条语句可以匹配不同的数组元素时此文档也会被返回。 接下来我们可以来看一个例子 现在有一个文档
{x : 5}
{x : 15}
{x : 25}
{x : [5, 25]}如果想找出 “x” 的值在 10 和 20 之间的所有文档那么你可能会本能地构建这样的查询即 db.test.find({“x” : {“ g t : 10 , gt : 10, gt:10,lt” : 20}})然后期望它会返回一个文档{“x” : 15}。然而当实际运行时我们得到了两个文档如下所示 db.test.find({x : {$gt : 10, $lt : 20}})
{x : 15}
{x : [5, 25]}5 和 25 都不在 10 和 20 之间但由于 25 与查询条件中的第一个子句x的值大于 10相匹配5 与查询条件中的第二个子句“x” 的值小于 20相匹配因此这个文档会被返回。 这样就使得针对数组的范围查询基本失去了作用一个范围会匹配任何多元素数组有几种方法可以获得预期的行为 1.可以使用 e l e m M a t h 强制 M o n g o D B 将这两个子句与单个数组元素进行比较不过 elemMath强制MongoDB将这两个子句与单个数组元素进行比较不过 elemMath强制MongoDB将这两个子句与单个数组元素进行比较不过elemMath不会匹配非数组元素 db.test.find({x : {$elemMatch : {$gt : 10, $lt : 20}}})// 没有结果文档 {“x” : 15} 不再与查询条件匹配了因为它的 “x” 字段不是一个数组。也就是说你应该有充分的理由在一个字段中混合数组和标量值而这在很多场景中并不需要。对于这样的情况“ e l e m M a t c h 为数组元素的范围查询提供了一个很好的解决方案。 ∗ ∗ 2. 如果在要查询的字段上有索引参见第 5 章那么可以使用 m i n 和 m a x 将查询条件遍历的索引范围限制为 elemMatch 为数组元素的范围查询提供了一个很好的解决方案。 **2.如果在要查询的字段上有索引参见第 5 章那么可以使用 min 和 max 将查询条件遍历的索引范围限制为 elemMatch为数组元素的范围查询提供了一个很好的解决方案。∗∗2.如果在要查询的字段上有索引参见第5章那么可以使用min和max将查询条件遍历的索引范围限制为gt” 和 “$lt” 的值** db.test.find({x : {$gt : 10, $lt : 20}}).min({x : 10}).max({x : 20})
{x : 15}现在这条查询语句只会遍历值在 10 和 20 之间的索引不会与值为 5 和 25的这两个条目进行比较。但是只有在要查询的字段上存在索引时才能使用min 和 max并且必须将索引的所有字段传递给 min 和 max 在查询可能包含数组的文档的范围时使用 min 和 max 通常是一个好主意。在整个索引范围内对数组使用 “ g t / gt/ gt/lt” 进行查询是非常低效的。它基本上接受任何值因此会搜索每个索引项而不仅仅是索引范围内的值。
4.4 $where查询
概念 允许你在查询中执行任意的 JavaScript 代码
例子 db.foo.find({$where : function () {
... for (var current in this) {
... for (var other in this) {
... if (current ! other this[current] this[other]) {
... return true;
... }
... }
... }
... return false;
... }});注意除非绝对必要否则不应该使用 “$where” 查询它们比常规查询慢得多
4.5 游标
概念游标Cursor是用于遍历查询结果的对象你执行查询时MongoDB 返回一个游标它指向查询结果的初始位置。通过游标你可以逐步获取结果集中的文档而不需要一次性将整个结果集加载到内存中。 案例 for(i0; i100; i) {
... db.collection.insertOne({x : i});
... }var cursor db.collection.find(); while (cursor.hasNext()) {
... obj cursor.next();
... // 执行任务
... }//还有一种用法游标cursor类还实现了Js的迭代器接口因此可以使用forEachvar cursor db.people.find();cursor.forEach(function(x) {
... print(x.name);
... });
adam
matt
zak上述案例中find方法就返回一个游标cursor.hasNext() 会检查是否有后续结果存在而 cursor.next() 用来对其进行获取。 游标的工作机制
**查询请求**当客户端发起一个查询请求MongoDB服务器接受请求返回游标服务器会返回一个包含初始查询结果的游标给客户端这是并不会立刻返回所有匹配的文档而是返回一个包含部分结果的游标对象这个部分结果一般来说是100个结果或者4MB的数据两者中较小者分批获取客户端可以使用游标的方法如next来逐批获取文档。当客户端请求下一批文档时服务器会在需要时继续执行查询获取更多的文档并返回给客户端。**游标生命周期**获取的过程会一直持续知道游标耗尽或者结果被全部返回
4.5.1 limit,skip和sort
limit会限制返回结果的数量传入的参数只能是正数负数会报错skip会跳过指定数量的文档然后返回剩下的文档参数也是正数sort会接受一个对象作为参数这个对象是一组键–值对键对应文档的键名值对应排序的方向。排序方向可以是 1升序或 -1降序。如果指定了多个键则结果会按照这些键被指定的顺序进行排序。例如要按照 “username” 升序及 “age” 降序排列
案例实现分页假设你正在运营一个在线商店有人想搜索 mp3。如果想每页返回 50 个结果并按照价格从高到低排序 db.stock.find({desc : mp3}).limit(50).sort({price : -1})//如果顾客单机下一页db.stock.find({desc : mp3}).limit(50).skip(50).sort({price : -1})然而略过大量的结果会导致性能问题 比较顺序 MongoDB 对于类型的比较有一个层次结构。有时一个键的值可能有多种类型整型和布尔型或者字符串和 null。如果对混合类型的键进行排序那么会有一个预定义的排序顺序。从最小值到最大值顺序如下。
最小值null数字整型、长整型、双精度浮点型、小数型字符串对象/文档数组二进制数据对象 ID布尔型日期时间戳正则表达式最大值
4.5.2 避免略过大量结果
使用 skip 来略过少量的文档是可以的。但对于结果非常多的情况skip 会非常慢因为需要先找到被略过的结果然后再丢弃这些数据。大多数数据库会在索引中保存更多的元数据以处理 skip但 MongoDB 目前还不支持这样做所以应该避免略过大量的数据 1.不使用skip对结果进行分页 最简单的方式就是使用limit和skip互相配合用偏移量来进行返回 // 不要这么做略过大量数据会非常慢var page1 db.foo.find(criteria).limit(100)var page2 db.foo.find(criteria).skip(100).limit(100)var page3 db.foo.find(criteria).skip(200).limit(100)
...使用skip查询很多结果时会非常慢所以我们有一种不使用skip来进行分页的方法就是使用排序和条件查询 假设要按照date**降序显式文档**可以使用以下方式获取第一页 var page1 db.foo.find().sort({date : -1}).limit(100)然后假设日期是唯一的可以使用最后一个文档的 “date” 值作为获取下一页的查询条件
var latest null;// 显示第1页
while (page1.hasNext()) {latest page1.next();display(latest);
}// 获取下一页
var page2 db.foo.find({date : {$lt : latest.date}});
page2.sort({date : -1}).limit(100);2.查询一个随机文档 我们首先也会翻译使用skip来随机跳过数据来获取 // 不要这样做var total db.foo.count()var random Math.floor(Math.random()*total)db.foo.find().skip(random).limit(1)以这种方式获取随机元素实际上非常低效必须先计算总数如果使用查询条件这会是一个昂贵的操作并且略过大量的元素也会非常耗时 我们还有一种比较好的方法不过需要提前计划在文档中设置random键 db.people.insertOne({name : joe, random : Math.random()})db.people.insertOne({name : john, random : Math.random()})db.people.insertOne({name : jim, random : Math.random()})这样我们就可以使用正常的条件查询来查询而不用使用skip var random Math.random()result db.people.findOne({random : {$gt : random}})random 可能会大于集合中的任何 “random” 值并且不会返回任何结果。可以简单地从另一个方向返回文档以避免这种情况 if (result null) {
... result db.people.findOne({random : {$lte : random}})
... }如果集合中没有任何文档那么这种方式会返回 null这也是合理的。
4.5.3 游标生命周期
在服务器游标会占用内存和资源所以我们需要释放游标有以下几种情况会释放游标
当游标遍历完结果之后它会清除自身当游标超出客户端的作用域驱动程序会向数据库发送一条特殊的消息让数据库知道它可以“杀死”该游标用户没有遍历完所有结果而且游标仍在作用域内如果 10 分钟没有被使用的话数据库游标也将自动“销毁”。
有时候我们需要一个游标维持很长时间这种情况下我们应该怎么办呢 许多驱动程序实现了一个称为 immortal 的函数或者类似的机制它告诉数据库不要让游标超时。 如果关闭了游标超时则必须遍历完所有结果或主动将其销毁以确保游标被关闭。否则它会一直占用数据库的资源直到服务器重新启动