MongoDB高级查询详细

1. 前言

前几篇,老玩家绕道即可,新手晚上闲着也是蛋疼,不如把命令敲一边,这样你就会对MongoDB有一定的掌握啦。如果没有安装MongoDB去看我的上一篇博客 MongoDB下载安装与简单增删改查

前奏:启动mongdb数据库服务,并进入shell界面

> mongo   --进入shell界面

2. 常用命令

> show dbs    -- 查看数据库列表

> use admin   --创建admin数据库,如果存在admin数据库则使用admin数据库

> db   ---显示当前使用的数据库名称

> db.getName()  ---显示当前使用的数据库名称

> db.dropDatabase()  --删当前使用的数据库

> db.repairDatabase()  --修复当前数据库

> db.version()   --当前数据库版本

> db.getMongo()  --查看当前数据库的链接机器地址 

> db.stats() 显示当前数据库状态,包含数据库名称,集合个数,当前数据库大小 ...

> db.getCollectionNames()   --查看数据库中有那些个集合(表)

> show collections    --查看数据库中有那些个集合(表)

> db.person.drop()  --删除当前集合(表)person

3. MongoDB接入Javascrip风格语法,for,while,next,hasNext,forEach,toArray,findOne,limit

> db.getCollectionNames()
[ "papers", "person", "persons", "products" ]
> p={name:"张龙豪", age:18}
{ "name" : "张龙豪", "age" : 18 }
> db.person.insert(p)
WriteResult({ "nInserted" : 1 })
> db.person.save(p)
WriteResult({ "nInserted" : 1 })
> db.person.find()
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
> 

Note:

1、p={name:“张龙豪”,age:18} 这个不是规范的json格式,使用这种类似javascript语法,对象会自动补全为规范的json格式{“name”:“张龙豪”,“age”:18}.

2、我在这里插入2条数据,一条是insert语法,一条是save语法,这里的insert与save是一样的功能。

> for(var i=1;i<5;i++) db.person.insert({"name":"张龙豪"+i,"age":i});
WriteResult({ "nInserted" : 1 })
> db.person.find()
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc7"), "name" : "张龙豪3", "age" : 3 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
> 

Note:这里我主要用啦一个for语法,循环插入啦4条数据,是不是有点小激动。哈哈,mongodb就是这么任性。

> var cursor = db.person.find();
> while(cursor.hasNext()) printjson(cursor.next())
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc7"), "name" : "张龙豪3", "age" : 3 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
> 

Note:这里是吧查询出来的

1、while:作为程序员应该都不陌生他是个循环。

2、hasNext: cursor集合遍历,是否还有数据。

3、printjson:输出集合中的文档

4、next:当前文档,并向下遍历。

> db.person.find().forEach(printjson);
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc7"), "name" : "张龙豪3", "age" : 3 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
> 

Note:forEach循环输入,必须定义一个函数供每个游标元素调用。

> var cursor = db.person.find()
> printjson(cursor[5])
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
> printjson(cursor[3])
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
> 

Note:游标也可以当作数组来用。

> var arr = db.person.find().toArray();
> arr[3]
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
> 

Note:游标转换为真实的数组类型,使用。

> db.person.findOne({"name":"张龙豪"})
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
> db.person.find().limit(3)
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
> 

Note:findOne,返回结果集中的第一条数据。limit(3),返回结果集中的前三条数据。

4. MongoDB中的高级查询

面向文档的NoSql数据库重要解决的问题不是高性能的并发读写问题,而是保证海量数据存储的同时,具有比一般数据库更加良好的查询性能。

4.1 指定需要返回的键

有时并不需要将文档中所有键/值对都返回。遇到这种情况,可以通过 find (或者findOne) 的第二个参数来指定想要的键。这样既会节省传输的数据量,又能节省客户端解码文档的时间和内存消耗。

例如,如果只对用户集合的 “username” 和 email 键感兴趣,可以使用如下查询返回这些键:

> db.users.find({}, {"username": 1, "email": 1})
{
    "_id": ObjectId("4ba0f0dfd22aa494fd523620"),
    "username": "joe",
    "email": "joe@example.com"
}

可以看到,默认情况下 “_id” 这个键总是被返回,即便是没有指定要返回这个键。

也可以用第二个参数来剔除查询结果中的某些键/值对。例如,文档中有很多键,但是我们不希望结果中含有 "fatal_weakness"键:

db.users.find({}, {"username" : 1, "_id" : 0})

使用这种方式,也可以把 “_id” 键剔除掉:

> db.users.find({}, {"username" : 1, "_id" : 0})
{
    "username" : "joe"
}

4.2 OR 查询

MongoDB 中有两种方式进行 OR 查询:“ i n " 可 以 用 来 查 询 一 个 键 的 多 个 值 , " in" 可以用来查询一个键的多个值," in""or” 更通用一些,可以在多个键中查询任意的给定值。

如果一个键需要与多个值进行匹配的话,就要用到 “$in” 操作符,再加一个条件数组。例如,抽奖活动的中奖号码是 725、542 和 390。要找出全部的中奖文档的话,可以构建如下查询:

> db.raffle.find({"ticket_no" : {"$in" : [725, 542, 390]}})

i n " 能 对 单 个 键 做 O R 查 询 , 但 要 是 想 找 到 " t i c k e t n o " 为 725 或 者 " w i n n e r " 为 t r u e 的 文 档 该 怎 么 办 呢 ? 对 于 这 种 情 况 , 应 该 使 用 " in" 能对单个键做 OR 查询,但要是想找到 "ticket_no" 为 725 或者 "winner" 为 true 的文档该怎么办呢?对于这种情况,应该使用 " in"OR"ticketno"725"winner"true使"or” 。

> db.raffle.find({"$or" : [{"ticket_no" : "725"}, {"winner" : true}])

“$or” 可以包含其他条件。例如,如果希望匹配到中奖的 “ticket_no”,或者 “winner” 键的值为 true 的文档,就可以这么做:

> db.raffle.find({"$or" : [{"ticket_no" : {"$in" : [725, 542, 390]}},
                           {"winner" : true}]})

4.3 特定类型的查询

4.3.1 null

null 类型的行为有点奇怪。他确实能匹配自身,所以要是有一个包含如下文档的集合:

> db.c.find()
{"_id" : "1", "y" : null}
{"_id" : "2", "y" : 1}
{"_id" : "3", "y" : 2}

就可以按照预期的方式查询 “y” 键为 null 的文档:

> db.c.find({"y" : null})
{"_id" : "1", "y" : null}

但是,null 不仅会匹配某个键的值为 null 的文档,而且还会匹配不包含这个键的文档。所以,这种匹配还会返回缺少这个键的所有文档:

> db.c.find({"z" : null})
{"_id" : "1", "y" : null}
{"_id" : "2", "y" : 1}
{"_id" : "3", "y" : 2}

如果仅想匹配键值为 null 的文档,既要检查该键的值是否为 null,还要通过 “$exists” 条件判定键值已存在:

> db.c.find({"z" : {"$in" : [null], "$exists" : true}})
4.3.2 查询数组
  1. $all 操作符

a l l 匹 配 所 有 , 类 似 t − s q l 中 的 i n , 但 是 t − s q l 中 的 i n 是 满 足 括 号 里 面 的 任 何 一 个 都 能 出 数 据 , 而 m o n g o d b 中 的 all匹配所有,类似t-sql中的in,但是t-sql中的in是满足括号里面的任何一个都能出数据,而mongodb中的 alltsqlintsqlinmongodball则必须满足[]中的所有值。

> var p = {name:"Tom",age:[7,8,9,10],wife:"Jerry"}
> db.person.save(p)
WriteResult({ "nInserted" : 1 })
> db.person.find({age:{$all:[7,9]}})
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
> db.person.find({age:{$all:[7,19]}})
> 

{age:{$all:[7,9]}}:age数组中只要有7和9就满足条件。如果只有7,没有9则不符合条件。

  1. $in

i n 包 含 , in包含, innin不包含。跟t-sql中的in,not in一样。

> db.person.find({age:{$in:[10,11]}})
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
> 

{age:{$in:[10,11]}} : 如果age是数组的话,只要数组包含in中条件的任何一条数据,都能被检索出来。不是数组,则只要满足in中的任何一个条件数据,也可以被检索出来。

  1. $size

数组元素个数。

> db.person.find({age:{$size:4}})
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
> 

{age:{$size:4}}: age数组元素个数为4的数据结果集。

  1. $slice 操作符

前面已经提及, find 的第二个参数是可选的,可以指定需要返回的键。这个特别的 “$slice” 操作符可以返回某个键匹配的数组元素的一个子集。

# 返回前10条评论
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : 10}})

# 返回最后10条评论
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : -10}})

“$slice” 也可以指定偏移值以及希望返回的元素数量,来返回元素集合中间位置的某些结果:

# 跳过前23个元素,返回第 24 ~ 33 个元素
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : [23, 10]}})

除非特别声明,否则使用 “$slice” 时将返回文档中的所有键。与别的键说明符不返回未提及的键有点不太一样。

有如下文章及评论数据:

> db.blog.posts.findOne()
{
    "id" : "122",
    "title" : "A blog post",
    "content" : "...",
    "comments" : [
        {
            "name" : "joe",
            "email" : "joe@example.com",
            "content" : "nice post."
        },
        {
            "name" : "bob",
            "email" : "bob@example.com",
            "content" : "good post."
        }
    ]
}

用 “$slice” 来获取最后一条评论,可以这样:

> db.blog.posts.findOne({}, {"comments" : {"$slice" : -1}})
{
    "_id" : ObjectId("5e4002d6b3db76a386695bcb"),
    "id" : "122",
    "title" : "A blog post",
    "content" : "...",
    "comments" : [
        {
            "name" : "bob",
            "email" : "bob@example.com",
            "content" : "good post."
        }
    ]
}
> 

title 和 content 都返回了,即便没有显式地出现在说明符中。

当我们不知道元素的下标时,可以使用 $ 操作符得到一个匹配的元素。对于上面的示例,可以用如下的方式得到 Bob 的评论:

> db.blog.posts.findOne({"comments.name" : "bob"}, {"comments.$" : 1})
{
    "_id" : ObjectId("5e4002d6b3db76a386695bcb"),
    "comments" : [
        {
            "name" : "bob",
            "email" : "bob@example.com",
            "content" : "good post."
        }
    ]
}
> 

注意:这样只会返回第一个匹配的文档。只有 “comments” 数组中的第一条评论会被返回。 除了 comments 属性和 _id 属性,其它属性都不会返回

  1. 数组和范围查询的相互作用

对于下面的文档,找出 “x” 键的值位于 10 和 20 之间的所有文档。

{"x" : 5 }
{"x" : 15 }
{"x" : 25 }
{"x" : [ 5, 25 ] }

使用 db.test.find({“x” : {“ g t " : 10 , " gt" : 10, " gt":10,"lt” : 20}}) 去查询,期望的返回的文档是 {“x” : 15},但是却返回了两个文档。

> db.test.find({"x" : {"$gt" : 10, "$lt" : 20}})
{ "_id" : ObjectId("5e40082db3db76a386695bcd"), "x" : 15 }
{ "_id" : ObjectId("5e40082db3db76a386695bcf"), "x" : [ 5, 25 ] }
> 

这是因为 25 与查询条件中的第一个语句(大于10)相匹配,5 与查询条件中的第二个语句(小于20)相匹配。

这使对数组使用范围查询没有用:范围会匹配任务多元素数组。可以使用 “$elemMatch” 可以使用多个查询条件对一个数组元素进行匹配。

> db.test.find({"x" : {"$elemMatch" : {"$gt" : 10, "$lt" : 20}}})
> db.test.find({"x" : {"$elemMatch" : {"$gt" : 10, "$lt" : 26}}})
{ "_id" : ObjectId("5e40082db3db76a386695bcf"), "x" : [ 5, 25 ] }
> 

4.3.3 查询嵌套文档

假设有博客文章若干,要找到由 Joe 发表的 5 分以上的评论。博客文章的结构如下所示:

> db.blog.posts.findOne()
{
    "_id" : ObjectId("5e4002d6b3db76a386695bcb"),
    "title" : "A blog post",
    "content" : "...",
    "comments" : [
        {
            "name" : "joe",
            "email" : "joe@example.com",
            "content" : "nice post.",
            "score" : 3
        },
        {
            "name" : "bob",
            "email" : "bob@example.com",
            "content" : "good post.",
            "socre" : 6   
        }
    ]
}
> 

不能直接使用 db.blog.posts.find({“comments” : {“name” : “joe”, “score” : {“KaTeX parse error: Expected 'EOF', got '}' at position 9: gte" : 5}̲}}) 来找寻。内嵌文档的匹配…gte” : 5}}) 也不行,因为符合 name 条件的评论和符合 score 条件可能不是同一条评论。

可以使用 “$elemMatch” 来正确匹配数组的一组条件。

> db.blog.posts.find({"comments" : {"$elemMatch" : {"name" : "joe", "score" : {"$gte" : 5}}}})
> db.blog.posts.find({"comments" : {"$elemMatch" : {"name" : "joe", "score" : {"$gte" : 3}}}})
{ "_id" : ObjectId("5e4002d6b3db76a386695bcb"), "title" : "A blog post", "content" : "...", "comments" : [ { "name" : "joe", "email" : "joe@example.com", "content" : "nice post.", "score" : 3 }, { "name" : "bob", "email" : "bob@example.com", "content" : "good post.", "score" : 6 } ] }
> 

4.4 其它查询

> db.person.find({"age":{$gt:17}})
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
> db.person.find({"age":{$lt:2}})
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
> db.person.find({"age":{$gte:18}})
{ "_id" : ObjectId("5e3f75cfb3db76a386695bc3"), "name" : "张龙豪", "age" : 18 }
{ "_id" : ObjectId("5e3f75d6b3db76a386695bc4"), "name" : "张龙豪", "age" : 18 }
> db.person.find({"age":{$lte:1}})
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
> db.person.find({"age":{$gt:16,$lt:18}})
> db.person.find({"age":{$gt:1,$lt:3}})
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
> 

Note:条件操作符号: > 、 < 、 >= 、<=

  • g t / / 大 于 > , gt //大于 > , gt//>lt //小于 < , g t e / / 大 于 等 于 > = , gte //大于等于 >= , gte//>=,lte //小于等于
  • {“filed”:{KaTeX parse error: Expected 'EOF', got '}' at position 9: op,value}̲} //filed字段 ,op条件操作符号,value值。
  • {“age”:{$gt:1}} : person集合中年龄大于1的所有数据文档
  1. $exists 判断字段是否存在
> var p = {name:"Jim", age:8, city:"usa"}
> db.person.save(p)
WriteResult({ "nInserted" : 1 })
> db.person.find({city:{$exists:true}})
{ "_id" : ObjectId("5e3f8041b3db76a386695bca"), "name" : "Jim", "age" : 8, "city" : "usa" }
> 

{city:{$exists:true}}: 集合中存在city这个字段的数据

  1. $mod 取模运算
> db.person.find({age:{$mod:[7,1]}})
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
{ "_id" : ObjectId("5e3f8041b3db76a386695bca"), "name" : "Jim", "age" : 8, "city" : "usa" }
> 

{age:{$mod:[7,1]}}:集合中模7余1的数据

  1. $ne不等于
> db.person.find({name:{$ne:"张龙豪"}})
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc5"), "name" : "张龙豪1", "age" : 1 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc6"), "name" : "张龙豪2", "age" : 2 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc7"), "name" : "张龙豪3", "age" : 3 }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
{ "_id" : ObjectId("5e3f8041b3db76a386695bca"), "name" : "Jim", "age" : 8, "city" : "usa" }
> 
  1. $not
> db.person.find({name:{$not:/^张.*/}})
{ "_id" : ObjectId("5e3f7fb6b3db76a386695bc9"), "name" : "Tom", "age" : [ 7, 8, 9, 10 ], "wife" : "Jerry" }
{ "_id" : ObjectId("5e3f8041b3db76a386695bca"), "name" : "Jim", "age" : 8, "city" : "usa" }
> 

$not正则匹配,不包含以张开头的数据。

db.users.find({"id_num" : {"$not" : {"$mod" : [5, 1]}}})

“$not” 后面也可以包含其它条件。

  1. skip,limit,sort,count
> db.person.find().skip(3).limit(2).sort({age:-1})
{ "_id" : ObjectId("5e3f8041b3db76a386695bca"), "name" : "Jim", "age" : 8, "city" : "usa" }
{ "_id" : ObjectId("5e3f77b3b3db76a386695bc8"), "name" : "张龙豪4", "age" : 4 }
> 
  • skip(2):从数据集的第二条开始查询
  • limit(2) : 依次查出2条数据。
  • sort({age:1}) : 1.正序查询,-1倒叙查询。
  • count():结果集总数。

本文转自:MongoDB高级查询详细

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值