MongoDB 查询简记

MongoDB 插入概览

插入文档

当执行插入的时候,使用的驱动程序会将数据转换为BSON格式,如果文档中没有定义id,则MongoDB会自动生成id

BSONMongoDB的文档内部数据格式,BSON的优点有:

  • 更快的遍历速度,BSON会将每个元素的长度存在元素头部,增加遍历速度
  • 更简单的操作
  • 更多的数据类型
  • 基本插入(id存在会报错)
db.foo.insert({"bar":"baz"})
  • 存在则更新
db.foo.save({"bar":"baz"})
  • 批量插入
db.foo.insert(
    [
        {"bar":"baz"},
        {"boo":"bzz"}
    ]
)

当批量插入的时候,insertsave没有区别

db.foo.insertMany([
    {"bar":"baz"},
    {"boo":"bzz"}
])

删除文档

MongoDB删除文档会很快,但是如果要清楚整个集合,那么直接删除集合(然后重建索引)会更加快

  • 基础删除
//删除所有文档,但是会保留集合本身
db.users.remove({})
  • 指定条件删除
//默认会删除所有符合条件的
db.users.remove({"_id":12345})

//仅仅删除第一条符合条件的数据
db.users.remove({"_id":12345},true)

更新文档

  • 全量覆盖更新
db.users.update({"_id":12345},newUser)

注意:由于update第一个参数所匹配的元素可能有多个,因此可能导致最后修改的元素不是想要修改的哪一个,此时最好指定一个唯一元素,例如:_id

  • $inc 增加
//将用户年龄增加3岁
db.users.update({"_id":12345},
                {"$inc":{"age":3}
                })
  • $set 设置字段和值/如果字段不存在则添加,如果字段存在则修改
//为users添加一个favorite book 字段
db.users.update(
    {"_id":12345},
    {"set":{"favorite book": "war and peace"}}
 )
//添加内嵌字段    
db.users.update(
    {"_id":12345},
    {"set":{"author":{"name":"joe"}}}
)
  • $unset 删除字段
db.users.update(
    {"_id":12345},
    {"$unset":{"favorite book": 1}}
 )

  • $push 给数组添加一个字段 / 只能用于数组,若数组不存在,则创建

    注意,默认数组不会判重

db.users.update(
    {"_id":12345},
    {"$push":{"comments":"6666"}}
)
  • $addToSet 如果数组不存在此字段则添加,存在则不添加
db.users.update(
    {"_id":12345},
    {"$addToSet":{"comments":"6666"}}
)
  • $each 给数组添加多个值(只能与pushaddToSet组合使用)
db.users.update(
    {"_id":12345},
    {"addToSet":{"comments":"each":["6666","777"]}}
)
  • $pop 删除数组任何一端元素
//从末尾删除
db.users.update(
    {"_id":12345},
    {"pop":{"comments":1}}
)

//从头部删除
db.users.update(
    {"_id":12345},
    {"pop":{"comments":-1}}
)    

只能为1或-1

  • $pull 删除指定元素
db.users.update(
    {"_id":12345},
    {"$pull":{"commentes":"6666"}}
)
  • $ 数组坐标访问
db.users.update(
    {"comments.author":"dcc"},
    {"set":{"comments..author":"dcc2"}}
)

  • upsert 存在则更新,不存在则覆盖
//原本的代码:
user = db.users.findOne({"_id":"1234"});
if(blog){
    blog.times++;
    db.users.save(blog);
}else{
    db.users.save({"_id":"1234","times":1})
}

//使用upsert
db.users.update({"_id":"1234",{"$inc":{"times":1}}},true)

使用upsert可以避免竞态问题

  • 批量更新

默认情况下,update只会更新第一个匹配到的文档,如果想要批量更新,可以将update第四个条件设置为true

db.users.update({"name":dcc},{"$set":{"name":"dcc2"}},false,true)

可以通过db.runCommand({getLastError:1})查看具体修改了多少个数据

注意:当第三个参数为true的时候,批量更新不会生效,而会创建

  • findAndModify

由于update不是一个原子操作,而有些时候需要先查找,然后修改,然后再对返回的数据进行一定的操作,如果需要查找和修改作为一个原子操作的话,可以使用findAndModify

db.mulit.findAndModify({
    query:{"x":12345},
    update:{"$set":{"x":123456}},
    upsert:true,
    new:false
})

可以通过new来控制返回修改前的属性还是修改后的属性

可以通过增加sort选项来确定所修改的元素,findAndModify只会修改并返回第一个文档

查询文档

  • 基础查询
db.users.find({"_id":"123123"})
db.users.find({"_id":"123123","age":18})
  • 只返回指定的键
//需要返回的值包括 username 和 email
db.users.find({},{"username":1,"email":1})

//需要返回的值不包括 username
db.users.find({},{"username":0})
//需要返回值不包括_id
db.users.find({},{"_id":0})    

不管有没有指定_id,它都会直接返回

  • 查询条件

    $lt (less than) $lte (less than equals) $gt (greater than) $gte (greater than equals)

//查找年龄小于18岁的用户
//不包含此字段的元素不会返回
db.users.find({"age":{"$lt",18}})
  • $ne not equals 反向查询
//查找年龄不为17的用户
//不包含此字段的元素也会返回
db.users.find({"age":{"$ne":17}})
  • $in 批量查询
//查找年龄为18 或 12 的用户
db.users.find({"age":{"$in":[12,18]}})
  • $nin批量反向查找
//查找年龄不为18 或 12 的用户
db.users.find({"age":{"$nin":[12,18]}})
  • $or多条件查询
//查找年龄为18 或者名字为dcc的用户
db.users.find({"or":[{"age":18},["name":"dcc"]])
  • $not反向逻辑查找
//查找年龄不大于18的用户
//not 只能与其他元操作符连起来使用
db.users.find({"age":{"not":"gt":18}})
  • 查找字段为null
db.users.find({"name":{"in":[null],"exists":true}})
  • 正则表达式
//查找所有姓名以d开头的用户
//注意正则表达式不用引号
db.users.find({"name":/d.*/})

  • 查询数组

数组中的元素查询可以直接通过任意元素即可匹配

//查找包含dcc的数组
db.users.find({"comments":"dcc"})
  • $all 查询数组同时包含的元素
//查找数组中同时包含`dcc`和 `dcc2`的元素
db.users.find({"comments":{"$all":["dcc","dcc2"]}})

如果不是用all,那么这个数组的元素顺序,数量必须相同

  • .数字 通过数组下标匹配
//查找comments 数组第二个元素为dcc的文档
db.users.find({"comments.2":"dcc")
  • $size 匹配数组元素大小
//查找`comments` 数组大小为3 的文档
db.users.find({"comments":{"$size":3}})
  • $slice 切割数组
//查找`id`为123 的文档的前10条评论
db.users.find({"_id":123},{"comments":{"slice":10}})

//查找`id`为123 的文档的 后10条评论
db.users.find({"_id":123},{"comments":{"slice":-10}})

//查找`id`为123 的文档的 后10-20条评论
db.users.find({"_id":123},{"comments":{"$slice":[10,10]}})    

  • 查询内嵌文档
{
    "name":{
        "first":"Joe",
        "last":"Schmoe"
    }
}

db.users.find({"name":{"first":"Joe","last":"Schmoe"}})
db.users.find({"name.first":"Joe","name.last":"Schmoe"})

注意:内嵌文档需要完全匹配

eg.有如下文档:

{
    "comments": [{
        "name": "dcc1",
        "age": 18
    }, {
        "name": "dcc2",
        "age": 19
    }, {
        "name": "dcc3",
        "age": 20
    }]
}

则如果想要查找某个comments中包含某个元素,年龄小于等于18并且名为dcc

如果使用:

db.users.find({"comments.name":"dcc1","comments.age":{"$lte":18}})

会返回上面的文档,但是上面的文档并不满足要求。

此时可以使用

  • $elemMatch 表示匹配单个内嵌文档的条件
db.user.find({"commnets":{"elemMatch":{"name":"dcc","age":{"lte":18}}})

对返回结果的处理:

  • limit 限制返回的数量
//仅仅返回前3个结果
db.users.find().limit(3)
  • skip跳过元素
//跳过3个元素
db.users.find().skip(3)
  • sort 对结果进行排序,1 升序, -1 降序
db.users.find().sort({"username":1,"age":-1})

注意,使用skip跳过大量文档的时候,会带有性能问题,解决方案可以通过sort()加上比较符进行跳转,也能达到skip的效果,并且没有性能问题