我爱编程

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

2017-11-29  本文已影响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有了一些基本的认识.

上一篇下一篇

猜你喜欢

热点阅读