MongoDB第四讲 深入MongoDB的查询操作

基础查询

MongoDB的查询操作非常重要,使用find和findOne进行查询,通过{}来设定查询条件,如果什么都不设置就是查询所有信息。

db.users.find() ##查询所有信息
db.users.findOne()##查询第一条记录

可以在find中使用逗号来分割多个查询条件,相当于关系数据库中的and操作

db.users.find({age:33})##查询年龄等于23的用户
> db.users.findOne({name:"foo",age:33})##查询名字为foo并且年龄为33的用户
{
        "_id" : ObjectId("5a1971ff15143821a5de24c7"),
        "name" : "foo",
        "age" : 33,
        "email" : "foo@example.com",
        "gender" : "male"
}

我们可以通过find的第二个条件来指定显示哪些key的信息

> db.users.findOne({name:"foo",age:33},{email:1,name:1})##仅仅显示email和name的信息
{
        "_id" : ObjectId("5a1971ff15143821a5de24c7"),
        "name" : "foo",
        "email" : "foo@example.com"
}

通过实例可以发现不管怎么设定都会显示_id,同样我们可以通过第二参数把key设置为0不显示信息

> db.users.findOne({name:"foo",age:33},{email:0})##排除email其他都显示
{
        "_id" : ObjectId("5a1971ff15143821a5de24c7"),
        "name" : "foo",
        "age" : 33,
        "gender" : "male"
}
>

通过下面的例子可以排除_id

> db.users.findOne({name:"foo",age:33},{_id:0,name:1})##仅仅显示name的信息
{ "name" : "foo" }
>

条件查询

MongoDB的查询同样可以像关系数据库一样加入>,<,=,!=的这些条件查询,只是使用的方式有些不一样而已

db.users.find({"age":{"$gte":20,"$lt":33}}).pretty() ##查询年龄大于等于20并且小于33的人
{
        "_id" : ObjectId("5a19740415143821a5de24c8"),
        "name" : "bar",
        "age" : 23,
        "email" : "bar@example.com",
        "gender" : "male"
}
{
        "_id" : ObjectId("5a19742115143821a5de24ca"),
        "name" : "world",
        "age" : 31,
        "email" : "world@example.com",
        "gender" : "male"
}

对于日期的处理也基本类似,使用new Date("yyyy-mm-dd")可以创建一个日期(详细日期:<YYYY-mm-ddTHH:MM:ss>),使用Date()可以插入当前日期。

##插入一条数据,日期是2017-12-22使用new Date()指定日期,使用Date()指定当前日期
> db.posts.insertOne({title:"first",content:".....",author:"foo",create:new Date("2017-12-22")})
> var d = new Date("2017-09-30")##创建一个日期对象
> db.posts.find({"create":{"$gt":d}}).pretty()##查询满足条件的日期对象
{
        "_id" : ObjectId("5a1977a815143821a5de24d3"),
        "title" : "first",
        "content" : ".....",
        "author" : "foo",
        "create" : ISODate("2017-12-22T00:00:00Z")
}

使用ne表示查询不等于某个的值

> db.users.find({name:{$ne:"foo"}},{name:1}).pretty()##查询name不等于foo的所有users
{ "_id" : ObjectId("5a19740415143821a5de24c8"), "name" : "bar" }
{ "_id" : ObjectId("5a19741115143821a5de24c9"), "name" : "hello" }
{ "_id" : ObjectId("5a19742115143821a5de24ca"), "name" : "world" }

使用逗号分割查询是以AND来进行条件的合并,如果要进行OR的查询,MongoDB提供了in和or两种方式,in表示查询的内容在某个范围内,in表示在某个范围内,而nin表示在不在范围内

db.users.find({name:{$in:["foo","bar"]}},{_id:0})##name在某个范围内
{ "name" : "foo", "age" : 33, "email" : "foo@example.com", "gender" : "male" }
{ "name" : "bar", "age" : 23, "email" : "bar@example.com", "gender" : "male" }
> db.users.find({name:{$nin:["foo","bar"]}},{_id:0})##name不在某个范围内
{ "name" : "hello", "age" : 25, "email" : "hello@example.com", "gender" : "male" }
{ "name" : "world", "age" : 31, "email" : "world@example.com", "gender" : "male" }

or的使用方式也类似,下例展示了如何查询AND和OR

> db.users.find( 
    {age:{$gt:22}, 
    $or:[{name:"foo"},{email:"hello@example.com"}] },
    {_id:0}
  )##查询,年龄大于22并且name为foo或者email为hello@example.com的所有users
{ "name" : "foo", "age" : 33, "email" : "foo@example.com", "gender" : "male" }
{ "name" : "hello", "age" : 25, "email" : "hello@example.com", "gender" : "male" }

特定类型的查询

首先看一下如何查询null的值,在MongoDB中null的值和关系数据库不太一样,关系数据库中,由于schema是固定的一般只会查询某个值为null,但是在MongoDB中可能存在没有这个Document的值,所以就会存在不同的需求。数据模型如下

> db.c.find()
{ "_id" : ObjectId("5a1b7bab29b9c4cdcc29a639"), "y" : 1 }
{ "_id" : ObjectId("5a1b7bad29b9c4cdcc29a63a"), "y" : 2 }
{ "_id" : ObjectId("5a1b7bb029b9c4cdcc29a63b"), "y" : null }
{ "_id" : ObjectId("5a1b7bb729b9c4cdcc29a63c"), "x" : 1 }
{ "_id" : ObjectId("5a1b7bb929b9c4cdcc29a63d"), "x" : 2 }

首先使用null来进行查询

> db.c.find({y:null})
{ "_id" : ObjectId("5a1b7bb029b9c4cdcc29a63b"), "y" : null }
{ "_id" : ObjectId("5a1b7bb729b9c4cdcc29a63c"), "x" : 1 }
{ "_id" : ObjectId("5a1b7bb929b9c4cdcc29a63d"), "x" : 2 }

我们发现查询出来的结果不仅仅包含了null值,还包含了不存在的值,此时如果希望查询是否包含需要使用exists来操作,需要强调的是exists是在查询的结果中过滤,所以并不是一个具体的条件,所以需要使用eq来配合。

db.c.find({y:{$eq:null,$exists:true}})##查询包含了y元素的并且y为null的
{ "_id" : ObjectId("5a1b7bb029b9c4cdcc29a63b"), "y" : null }
> db.c.find({y:{$eq:null,$exists:false}})##查询了不包含y元素的
{ "_id" : ObjectId("5a1b7bb729b9c4cdcc29a63c"), "x" : 1 }
{ "_id" : ObjectId("5a1b7bb929b9c4cdcc29a63d"), "x" : 2 }

MongoDB提供了正则表达式的查询,使用正则表达式可以组合出各种不同需求的查询,需要注意的是正则表达式的值使用/ 作为开始的和结束,如果要忽略大小写同样可以使用/i结尾,另外正则表达式的值不用加引号。

 db.users.find({name:/^f+/},{name:1,_id:0})##通过正则表达式匹配name是以f开头的
{ "name" : "foo" }
{ "name" : "fok" }

正则表达式和not配合起来就更加的好用

> db.users.find({name:{$not:/f+/}},{name:1,_id:0})##匹配不存在f的所有值
{ "name" : "bar" }
{ "name" : "hello" }
{ "name" : "world" }

数组查询

匹配数组非常简单

> db.food.find({fruit:"apple"})))##此时会匹配包含apple的数组
{ "_id" : ObjectId("5a1c2a6929b9c4cdcc29a640"), "fruit" : [ "apple", "banana", "orange" ] }
{ "_id" : ObjectId("5a1c2b4f29b9c4cdcc29a641"), "fruit" : [ "apple", "peach", "orange" ] }

db.food.find({fruit:"apple"}))) 这个会匹配所有包含了apple的数组,如果希望进行数组匹配使用all修饰符

>db.food.find({fruit:{$all:["apple","peach"]}},{_id:0})##匹配数组中包含有apple和peach的数据
{ "fruit" : [ "apple", "peach", "orange" ] }

以上匹配中apple和peach的顺序不会影响结果,通过下面的例子可以进行精确匹配,这个就连顺序也必须一样

db.food.find({fruit:["apple","peach","orange"]},{_id:0})##精确匹配,顺序要一致
{ "fruit" : [ "apple", "peach", "orange" ] }
> db.food.find({fruit:["apple","orange","peach"]},{_id:0})##顺序不一样无法找到数据

还可以指定位置来进行查询,如果要使用这种方式,需要注意的是key这个值必须加上"",下标是从0开始的

>db.food.find({"fruit.2":"orange"},{_id:0})##查询第三个元素是orange的数据
{ "fruit" : [ "apple", "banana", "orange" ] }
{ "fruit" : [ "apple", "peach", "orange" ] }

通过size可以查询数组长度等于某个值的数据,但是无法使用gt或者lt

> db.food.find({fruit:{$size:3}},{_id:0})##查询等于3的数据
{ "fruit" : [ "apple", "banana", "orange" ] }
{ "fruit" : [ "apple", "peach", "orange" ] }
> db.food.find({fruit:{$size:{$gte:3}}},{_id:0})##$size无法使用gt之类的条件
Error: error: {

如果希望做上面的操作,可以考虑插入一个size来保存,这个操作对性能的影响微乎其微,只是在插入数据的时候通过inc增加一条即可。对于find的第二个参数而言,如果时候数组可以通过slice来返回访问第几个元素

>db.food.find({fruit:"apple"},{fruit:{$slice:2},_id:0})##访问数组的前两个元素
{ "fruit" : [ "apple", "banana" ], "size" : 1 }
{ "fruit" : [ "apple", "peach" ] }
> db.food.find({fruit:"apple"},{fruit:{$slice:-2},_id:0})##访问后两个元素
{ "fruit" : [ "orange", "berry" ], "size" : 1 }
{ "fruit" : [ "peach", "orange" ] }
> db.food.find({fruit:"apple"},{fruit:{$slice:[2,3]},_id:0})##从第三个开始访问后三个元素
{ "fruit" : [ "orange", "berry" ], "size" : 1 }
{ "fruit" : [ "orange" ] }

下面将来演示几种内嵌文档的查询,首先看一下数据模型

> db.blog.findOne()
{
        "_id" : ObjectId("5a1c339b29b9c4cdcc29a642"),
        "title" : "first",
        "content" : "...",
        "author" : {
                "name" : "leon",
                "age" : 32
        },
        "comments" : [
                {
                        "content" : "c1",
                        "author" : "joe",
                        "score" : 4
                },
                {
                        "content" : "c2",
                        "author" : "jake",
                        "score" : 6
                }
        ]
}

要查询作者为leon的文档非常简单,通过.可以引导到关联对象中

> db.blog.find({"author.name":"leon"},{title:1,author:1,_id:0})
{ "title" : "first", "author" : { "name" : "leon", "age" : 32 } }

如果要查询
comments中作者为joe并且score大于等于5分的文章,这个需求如果按照常规的方式会把两条comments都查询出来,因为第二条comments的分数满足要求

> db.blog.find({"comments.author":"joe","comments.score":{$gte:5}},{title:1,author:1,_id:0})
{ "title" : "first", "author" : { "name" : "leon", "age" : 32 } }

这种情况需要使用elemMatch来解决

 db.blog.find({"comments":{$elemMatch:{"author":"joe","score":{$gte:5}}}})

最后,还有一种where查询,这种查询的效率稍微有些低,但是基本可以实现所有的查询,它的查询思路是将BSON转换为javascript对象来处理,还是首先看看数据模型

> db.point.find({},{_id:0})
{ "x" : 10, "y" : 30 }
{ "x" : 20, "y" : 20 }
{ "x" : 60, "y" : 60 }

如果希望查询x+y的值为40的数据,使用普通的方式就不太好查询,此时可以通过where来完成,使用where之后可以通过this来引用对象

 db.point.find(
... {$where:
... "function()
...  {
...     if(this.x+this.y==40) 
...         return true; 
...     else 
...         return false}
... "
... }) ##基于where的查询
{ "_id" : ObjectId("5a1c366429b9c4cdcc29a643"), "x" : 10, "y" : 30 }
{ "_id" : ObjectId("5a1c366b29b9c4cdcc29a644"), "x" : 20, "y" : 20 }

可以对上述的查询进行简单的转换,将function这些省略,只要写结果就行

> db.point.find({$where:"this.x+this.y==40"})
{ "_id" : ObjectId("5a1c366429b9c4cdcc29a643"), "x" : 10, "y" : 30 }
{ "_id" : ObjectId("5a1c366b29b9c4cdcc29a644"), "x" : 20, "y" : 20 }

最后再强调一下:where查询效率不高,一般非逼不得已不使用这个查询。

这一部分就讲这么多,通过这几部分的内容,基本对MongoDB有了一些基本的认识.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容