1.$where 查询
注:从MongoDB 4.4开始,官方已经不再支持 $where
键/值对是一种表达能力非常好的查询方式,但是依然有些需求他无法解决,这个时候就轮到 $where 子句登场了,用他可以执行任意的JavaScript,这样可以在查询中几乎可以做任何事情,但是为了安全起见,应该严格限制或消除$where语句的使用,应该禁止终端用户使用任意的 $where 。
$where 语句最常见的应用就是比较文档中的两个键的值是否相等,例如我们有以下文档
db.foo.insertOne({"apple" : 1, "banana" : 6, "peach" : 3})
db.foo.insertOne({"apple" : 8, "spinach" : 4, "watermelon" : 4})
我们希望返回两个键具有相同值的文档,第二个文档中”spinach” 和 “watermelon” 的值相同,所以需要返回该文档,mongodb从来没有提供过一个$条件语句来做这种查询,所以只能用 $where
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;
}})
如果函数返回true,文档就作为结果集的一部分返回,如果为false,就不返回。
不是非常必要时,一定要避免使用 $where 查询,因为他们在速度上要比常规的查询慢很多,每个文档都要从BSON转换成JavaScript对象,然后通过 $where 表达式来运行,而且 $where 语句不能使用索引,所以只在走投无路时才考虑 $where 这种用法,先使用常规查询进行过滤,然后再使用 $where 语句,这样组合使用可以降低性能损失。如果可能的话,使用 $where 语句前应该先使用索引进行过滤, $where 只用于对结果进行进一步过滤。
进行复杂查询的另一种方法是使用聚合工具。
(1).在mongodb4.4使用 $expr 替代 $where
插入基础数据
db.players.insertMany([
{ _id: 12378, name: "Steve", username: "steveisawesome", first_login: "2017-01-01" },
{ _id: 2, name: "Anya", username: "anya", first_login: "2001-02-02" }
])
使用 $where 进行查询
wang> db.players.find( { $where: function() {
... return (hex_md5(this.name) == "9b53e667f30cd329dca1ec9e6a83e994")
... } } )
{ "_id" : 2, "name" : "Anya", "username" : "anya", "first_login" : "2001-02-02" }
wang>
使用 $expr 进行查询
db.players.find( {$expr: { $function: {
body: function(name) { return hex_md5(name) == "9b53e667f30cd329dca1ec9e6a83e994"; },
args: [ "$name" ],
lang: "js"
} } } )
注意:如果必须创建自定义表达式,则 function优先于where。
2.游标
数据库使用游标返回find的执行结果,客户端对游标的实现通常能够对最终结果进行有效控制,可以限制结果的数量,略过部分结果,根据任意键按任意顺序的组合对结果进行各种排序,或者执行一些强大的操作。首先插入基础数据:
for (i=0;i
这样做的好处就是一次可以查看一条结果。要迭代结果,可以使用游标的next方法。也可以使用hasNext来查看游标中是否还有其他结果,具体如下所示:
while (cursor.hasNext()){
obj=cursor.next()
print(obj.x)
}
cursor.hasNext() 检查是否有后续结果存在,然后用cursor.next()获取它。
游标类还实现了JavaScript的迭代器接口,所以可以在forEach循环中使用
方法1:
var cursor = db.test2.find().limit(5)
cursor.forEach(function(cur) {
print(cur.x)
})
方法2:
db.test2.find().limit(5).forEach(function(cur) {
print(cur.x)
})
当您调用find时,shell不会立即查询数据库,它会等到您启动请求结果以发送查询,这允许您将其他选项链接到查询上几乎每个cursor对象上的方法都返回游标本身,以便您可以按任何顺序链接选项。例如,以下所有选项都是等效的:
> var cursor = db.foo.find().sort({"x" : 1}).limit(1).skip(10);
> var cursor = db.foo.find().limit(1).sort({"x" : 1}).skip(10);
> var cursor = db.foo.find().skip(10).limit(1).sort({"x" : 1});
此时,查询还没有执行,所有这些函数都只是构建查询。现在假设我们执行如下操作。
cursor.hasNext()
此时,查询将被发送到服务器,shell获取前100个结果或前4个结果MB的结果(以较小者为准),以便下一次调用next或hasNext不必访问服务器。在客户端运行完第一组结果后shell将再次与数据库联系,并通过getMore请求请求更多结果请求基本上包含查询的标识符,并询问数据库是否还有标识符结果,如果有,则返回下一批。此过程将继续,直到光标用完为止所有结果都已返回。
其他方法查看官方手册:https://docs.mongodb.com/manual/reference/method/js-cursor/index.html
(1).limits, skips, sorts
最常见的查询选项是限制返回的结果数,跳过一个数字所有这些选项都必须在查询发送到数据库之前添加。
要限制结果数量,可在find后使用limit函数。例如,只返回3个结果
db.c.find().limit(3)
skip跳过前3条
db.c.find().skip(3)
sort排序,排序方向可以是1(升序)或−1(降序)
db.c.find().sort({username : 1, age : -1})
可以使用sort进行分页
第一页
db.stock.find({"desc" : "mp3"}).limit(50).sort({"price" : 1})
第二页
db.stock.find({"desc" : "mp3"}).limit(50).skip(50).sort({"price" : 1})
然而,略过过多的结果会导致性能问题,下一小节会讲述如何避免略过大量的结果。
比较顺序:
mongodb处理不同类型的数据是有一定顺序的,有时一个键值可能是多种类型的,整型和布尔型,或者字符型和null,如果对这种混合类型的键排序,其实排序顺序是预先定义好的。优先级从小到大,其顺序如下:
1. Minimum value #最小值
2. null
3. Numbers (integers, longs, doubles) #数字(整型,长整型,双精度)
4. Strings
5. Object/document
6. Array
7. Binary data
8. Object ID
9. Boolean
10. Date
11. Timestamp
12. Regular expression #正则表达式
13. Maximum value #最大值
(2).避免使用skip略过大量的结果
用skip略过少量的文档还是不错的。但是要是数量非常多的话,skip就会变的很慢,因为要先找到需要被略过的数据,然后再抛弃这些数据。
1).不用skip对结果分页
分页最简单的方法是使用limit返回结果的第一页,然后返回从开头开始偏移的每一页:
> // 不要这么用,略过的数据比较多时,速度会变的很慢
> 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的情况下实现分页,这取决于查询本身,例如,按照date降序显示文档列表,可以用如下方式获取结果的第一页:
var page1 = db.foo.find().sort({"date" : 1}).limit(100)
然后,可以利用最后一个文档中date的值作为查询条件,来获取下一页:
var latest = null;
// display first page
while (page1.hasNext()) {
latest = page1.next();
display(latest);
}
// get next page
var page2 = db.foo.find({"date" : {"$lt" : latest.date}});
page2.sort({"date" : 1}).limit(100);
这个查询中就没有skip了
2).随机选取文档
从集合里面随机挑选一个文档算是个常见问题,最笨的(也很慢的)做法就是先计算文档总数,然后选择一个0到文档数量之间的随机数,利用find做一次查询
> // do not use
> var total = db.foo.count()
> var random = Math.floor(Math.random()*total)
> db.foo.find().skip(random).limit(1)
这种选取随机文档的做法效率太低,首先得计算总数,然后用skip略过大量结果也会非常耗时。
略微动动脑筋,从集合里面查找一个随机元素还是有好多得多的办法,秘诀就是在插入文档时给每个文档添加一个额外的随机键,例如在shell 中,可以用Math.random() 产生一个0~1的随机数:
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}})
偶尔也会遇到产生的随机数比集合中所有随机值都大的情况,这时就没有结果返回了,遇到这种情况,那就将条件操作符换一个方向:
if (result == null) {
result = db.foo.findOne({"random" : {"$lte" : random}})
}
要是集合里面没有文档,则会返回null,这是没有问题的。
这种技巧还可以和其他各种复杂的查询一同使用,仅需要确保有包含随机键的索引即可,例如,想在加州随机找一个水暖工,可以对profession,state,random字段建立索引
db.people.ensureIndex({"profession" : 1, "state" : 1, "random" : 1})
这使我们能够快速地找到一个随机结果(有关索引的更多信息,请参阅第5章)
(3).高级查询选项
有2种类型的查询:wrapped(简单查询)和plain(封装查询),简单的查询就像下面这样
var cursor = db.foo.find({"foo" : "bar"})
有一些选项可以用于对查询进行封装,例如我们执行排序
var cursor = db.foo.find({"foo" : "bar"}).sort({"x" : 1})
实际情况不是将{“foo” : “bar”}作为查询直接发生给数据库,而是将查询封装在一个更大的文档中,shell会吧查询从{“foo” : “bar”}转换成 {“query”:”foo”:”bar”,”
orderby” : {“x” : 1}}
绝大多数驱动程序都提供了辅助函数,用于向查询中添加各种选项,下面列举了其他一些有用的选项(目前这些选项已经过时)。
$maxScan
$min
$max
$showDiskLoc
(4).数据库命令
有一种非常特殊的查询类型叫做数据库命令( database command),前面已经介绍了文档的创建、更新、删除以及查询。这些都是数据库命令的使用范畴,包括管理性的任务(比如关闭数据库和克隆数据库)、统计集合内的文档数量以及执行聚合等。
本节主要讲述数据库命令,在数据库操作、管理以及监控中。数据库命令都是非常有用的。例如,删除集合是使用drop数据库命令完成的。
wang> db.runCommand({"drop":"nn"})
{ "nIndexesWas" : 1, "ns" : "wang.nn", "ok" : 1 }
也许你对shell辅助函数比较熟悉,这些辅助函数封装数据库命令,并提供更加简单的接口
db.nn.drop()
通常,只使用shell辅助函数就可以了,使用数据库命令对了解底层命令很有帮助,尤其是当使用旧版本的shell连接到新版本的数据库上时,这个shell可能不支持新版数据库的一些命令,这时候就不得不使用
runCommand()
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net