美文网首页我爱编程
MongoDB第四讲 深入MongoDB的查询操作

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

作者: 孔浩 | 来源:发表于2017-11-29 21:06 被阅读0次

    基础查询

    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有了一些基本的认识.

    相关文章

      网友评论

        本文标题:MongoDB第四讲 深入MongoDB的查询操作

        本文链接:https://www.haomeiwen.com/subject/sinpbxtx.html