mongodb数据库操作

安装在Ubuntu16.04:

# 创建数据库文件夹,这个是默认存放数据的地方,但MongoDB不会自动创建
mkdir -p /data/db
# 增加权限
chown -R $USER:$USER /data/db
wget -qO - https://www.mongodb.org/static/pgp/server-4.0.asc | sudo apt-key add -  # 导入公钥
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list  # 为MongoDB创建列表文件
sudo apt-get update  # 重新加载本地数据包
sudo apt-get install -y mongodb-org=4.0.12 mongodb-org-server=4.0.12 mongodb-org-shell=4.0.12 mongodb-org-mongos=4.0.12 mongodb-org-tools=4.0.12  # 安装软件包
echo "mongodb-org hold" | sudo dpkg --set-selections
echo "mongodb-org-server hold" | sudo dpkg --set-selections
echo "mongodb-org-shell hold" | sudo dpkg --set-selections
echo "mongodb-org-mongos hold" | sudo dpkg --set-selections
echo "mongodb-org-tools hold" | sudo dpkg --set-selections  # 保持目前的版本,防止自动更新
# 启动
sudo service mongodb start #或者
systemctl start mongodb

卸载

sudo apt-get purge mongodb-org*
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb

通过systemctl start mongodb 启动时的错误解决

报错如下:

Failed to start mongodb.service: Unit mongodb.service not found.

解决:

sudo nano /etc/systemd/system/mongdb.service  # 创建服务文件
[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target

[Service]
User=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf
# 制定需要执行的命令的位置,                                #  制定项目运行所需要的配置文件位置
[Install]
WantedBy=multi-user.target
  • 按ctrl + x 保存退出
  • systemctl start mongodb
  • 永久有效: systemctl enable mongodb

1、文档中值的数据类型

在mongodb中数据是以文档的形式存在的

1、每个文档相当于一条记录

2、多个文档组合在一起,就是集合,一个集合就相当于一张数据表

3、多个集合组合在一起,就是数据库,每个数据库就是一个文件夹

  • Object ID: ⽂档ID
  • String: 字符串, 最常⽤, 必须是有效的UTF-8
  • Boolean: 存储⼀个布尔值, true或false
  • Integer: 整数可以是32位或64位, 这取决于服务器
  • Double: 存储浮点值
  • Arrays: 数组或列表, 多个值存储到⼀个键
  • Object: ⽤于嵌⼊式的⽂档, 即⼀个值为⼀个⽂档
  • Null: 存储Null值
  • Timestamp: 时间戳, 表示从1970-1-1到现在的总秒数
  • Date: 存储当前⽇期或时间的UNIX时间格式

2、Mongo shell

  • 使用mongo命令自动启动shell,并连接数据库服务器,该shell能够解析JavaScript的语法
  • 通过shell可以完成一些列的增删改查工作

3、文档的基本增删改查

  • 增加:使用insert()函数,将要插入的数据放到集合中

    • 比如增加一个文档,首先先创建文档变量
post = {"title": "my first mongo"}

  • 插入文档到blog集合中
db.blog.insert(post)

    • 批量插入:同样适用inset方法,将你需要插入的文档全部存放到一个数组中
    • 当我们连接到服务器之后,db会自动指向正在使用的数据库,blog为集合名,没有会自动创建
    • 查找:使用find()或者findOne()

      • find()默认显示20条数据
    db.blog.find()
    
    
    • findOne()只显示一条数据
    db.blog.findOne()
    
    
    • 更新:使用update()方法, 该方法接收两个参数,第一个为需要修改的文档,第二个修改过的文档

    update()方法除了接收前两个参数外,还可以接收另外两个参数,并且这两个参数都是布尔值

    例如:db.blog.update({“title”: “My Blog Post”}, {“title”: “My Blog Get”}, true, true)

    第三个参数表示upsert,能够保证操作的原子性,同时在没有查询到制定的集合情况下,会以查询条件和更新文档为基础新建一个文档,第四个参数默认是true,表示更新所有符合条件的集合,若改为false,则只更新第一个。

    • 第一:先更改文档对象的属性,也就是键值对,比如前文使用到的post变量
    给他增加一个属性:post.comments = []
    
    
    • 找到旧文档,替换为新的文档,根具给出的一个参数中的具体信息可以找到旧文档
    db.blog.update({"title":"my first mongo"}, post)
    
    
    • 删除:remove(),在不给出参数的情况下,删除集合中的所有文档,给出参数后,会删除对应的文档

      • db.blog.remove({“title”:”my first mongo”})

    4、基于shell的其他命令,利用help查看

    > help
    db.help() help on db methods
    db.mycoll.help() help on collection methods
    sh.help() sharding helpers
    rs.help() replica set helpers
    help admin administrative help
    help keys key shortcuts
    help connect connecting to a db help
    help misc misc things to know
    help mr mapreduce
    show dbs show database names
    show collections show collections in current database
    show users show users in current database
    show profile show most recent system.profile entries with time >= 1ms
    show logs show the accessible logger names
    use <db_name> set current database
    db.foo.find() list objects in collection foo
    db.foo.find( { a : 1 } ) list objects in foo where a == 1
    it result of the last line evaluated; use to further iterate
    DBQuery.shellBatchSize = x set default number of items to display on shell
    exit quit the mongo shell

    5、访问集合

    1、当集合名与内置的函数名相同时,是没有办法通过db.集合名来访问的

    2、当集合名名包含时,比如:six-four,同样没有办法通过正常的方法访问集合,引文shell对其的解释是两个变量的相减

    最终的解决办法是通过__getCollection()__来访问,例如:db.getCollection(“version”),db.getCollection(“six-four”)这样才能正常访问

    6、save shell

    save是一个shell函数,可以在文档不存在的时候插入,存在的时候更新,它只有一个参数,即文档本身

    save会调用upsert,即实现文档的自动创建和更新

    var x = db.foo.findOne()

    x.num = 12

    db.foo.save(x)

    7、修改器,以$号开头的特殊字符串

    修改器 用法 作用
    $inc db.x.update({“a”:1}, {$inc: {“b”:1}}) 在原有b的基础上加1
    $set db.x.update({“a”:1}, {$set: {“b”:”a”}}) 将原有b的数据修改为a,若b不存在,则创建一个
    $unset db.x.update({“a”:1}, {$unset: {“b”:1}}) 撤销修改,此时的数值1,表示条件为真的意思
    $push db.x.update({“a”:1}, {$push:{“comments”: {“name”:”json”}}}) 该修改器只作用于数组,可以向文档中已有数组中添加值,当然数组不存在时,创建一个数组并添加对应内容
    $pop db.x.update({“a”:1}, {$pop:{“comments”: 1}}) db.x.update({“a”:1}, {$pop:{“comments”: -1}}) 基于数组位置的删除,1表示从数组的尾部删除一个元素,-1表示从数组的头部删除一个元素
    $addToSet db.x.update({“a”:1}, {$addToSet:{“email”: “aa@163.com”}) 该修改器只作用于数组,可以向数组中添加一个值,若数组中已经有了该值,则添加失败
    $each db.x.update({“a”:1}, {$addToSet:{“email”: {$each: [“1”, “2”, “3”]}}) 可以遍历出数组中的每个值,通常与$addToSet搭配使用,完成多个值得添加,如果存在不添加
    $pull db.x.update({“a”:1}, {$pull:{“email”: “1”}}) 指定数组中的具体元素进行删除,若匹配到多个值,匹配多少删除多少
    $数组定位符 db.x.update({“comments.author”: “jhon”}, {$set:{“comments.$.author”: ‘lili’}}) 定位符只匹配第一个匹配到的文档

    8、查询

    8.1、指定键返回查询

    • 查询使用find函数或者findOne函数
    • 查询返回指定字段,也就是查询指定键
    • db.blog.find({“a”:2}, {“ss”:1, “wo”: 0}),当匹配到包含{“a”:2}的文档后,只返回”_id” 和”ss”两个键,”_id”是默认返回的,其中1表示真,0表示假

    8.2、条件查询

    命令 用法 作用
    $gt db.users.find({“age”: {“$gt”: 20}}) 查询users集合中年龄大于20的文档
    $gte db.users.find({“age”: {“$gte”: 20}}) 查询users集合中年龄大于等于20的文档
    $lt db.users.find({“age”: {“$lt”: 20}}) 查询users集合中年龄小于20的文档
    $lte db.users.find({“age”: {“$lte”: 20}}) 查询users集合中年龄小于等于20的文档
    $ne db.users.find({“username”: {“$ne”: “andy”}}) 查询users集合中用户名不等于andy的文档
    $or db.users.find({“$or”: [{“age”: 20},{“username”: “andy”} ]}) $or 键对应的值是一个数组,满足数组中任何一个条件的文档都会被查找出来,可作用于不同的键
    $in db.users.find({“age”: {“$in”: [12 ,10, 22]}}) $in键对应的值是一个数组,满足数组中任何一个条件的文档都会被查找出来,但是它只作用于同一个键
    $nin db.users.find({“age”: {“$nin”: [12 ,10, 22]}}) $nin键对应的值是一个数组,不满足数组中任何一个条件的文档都会被查找出来,它也只作用于同一个键
    $mod db.users.find({“age”: {“$mod”: [5, 1]}}) $mod表示取模运算,运算完成后在作为查询条件,它对应的值是一个数组,该数组只有两个值且有顺序,第一个表示除数,第二个表示余数,在这里表达的意思是,找出年龄除以5后余数为1的文档
    $not db.users.find({“$not”:{“age”: {“$mod”: [5, 1]}}}) $not是元条件查询,即作用其他条件查询之上,例如他与$mod取模运算结合使用,表示取非
    $existe db.users.find({“x”:{“$existe”: true}}) $existe判断该键是否真实存在,存在则显示出来
    /joy/i db.users.find({“username”: /joy/i}) 这里是利用正则表达式去匹配所查询文档的某个键对应的值,i表示不区分大小写

    $where 自定义查询

    db.stu.find({
        $where:function() {
            return this.age>30;}  // this 代表当前查询的文档,找出年龄大于30岁的文档
    })

    8.3、数组查询

    8.3.1、$all
    • 创建如下集合并加入三条文档:

    db.fruit.insert({“_id”: 1, “fruits”: [“banana”, “peach”, “apple”]})

    db.fruit.insert({“_id”: 2, “fruits”: [“orange”, “kumquat”, “apple”]})

    db.fruit.insert({“_id”: 3, “fruits”: [“banana”, “cherry”, “apple”]})

    • {“$all”: [“banana”, “apple”]},作用于数组,

    使用db.fruit.find({“fruits”: {“$all” : [“banana”, “apple”]}}),执行结果是数值fruits中只要包含有”banana”和”apple”元素的文档,全部返回,忽略数组中元素的顺序

    • 特殊用法

    {“fruits”: {“$all” : [“banana”]}}和{“fruits”:”banana”},这两条查询效果是一样的

    • 基于数组下标的查找

    比如:db.fruit.find({“fruits.2″:”apple”}),只要数组中指定位置的元素匹配正确,就会返回结果

    8.3.2、$size
    • 查询固定数组长度的文档

      • 用法:db.fruit.find({“fruits”: {“$size”: 3}})
      • 不能配合其他条件命令使用,例如$gt,而实际生产中,总是需要我们去查找数组范围的文档
    • 在文档中创建一个size的字段来专门描述数组的长度,变相的通过size的值来范围查找

      • 例如上文:增加一个size字段,db.fruit.update({“_id”: 1}, {“$inc”: {“size”: 3}})
      • 增加数组的内容时,同时增加size的值
    db.fruit.update({"_id": 1}, {"$push" : {"fruits": "pear"}, "$inc" : {"size": 1}})
    
    
    • 最后就可以进行范围查找了
    db.fruit.find({"size": {"$gt": 3}})
    
    
    8.3.3、$slice
    • 对查找到的数组进行切片处理,并返回新的切片后的文档

      • 使用$slice必须配合find函数的第二个参数使用,find的第二参数是指定文档中的键返回的,未提及或者对应值为0,将不会被返回,当使用$slice后,即使不提及文档中的其他键同样会返回
    db.fruit.find({"_id" : 1}, {"fruits": 1})
    >{ "_id" : 1, "fruits" : [ "banana", "peach", "apple", "pear"]}  # 这里_id默认返回
    # 如果将_id的值变为0,则不会被返回
    db.fruit.find({"_id" : 1}, {"fruits": 1, "_id": 0})  # 数字代表真假
    >{"fruits" : [ "banana", "peach", "apple", "pear"]}
    # 当对返回的键使用切片后$slice,文档中所有的键默认都会返回
    db.fruit.find({"_id": 1}, {"fruit": {"$slice": 3}})
    >{ "_id" : 1, "fruits" : [ "banana", "peach", "apple" ], "size" : 4 }
    • 对数组正向切割

    db.fruit.find({“_id”: 1}, {“fruit”: {“$slice”: 3}}), $slcie对应的值是正数即可,得到数组的前三个元素

    • 对数组反向切割

    db.fruit.find({“_id”: 1}, {“fruit”: {“$slice”: -3}}), $slcie对应的值是负数即可,得到数组的后三个元素

    • 对数组指定范围切片

    db.fruit.find({“_id”: 1}, {“fruit”: {“$slice”: [1, 3]}}),$slice对应的值为数组,该数组表示你要操作的数组下标

    ,得到结果:{ “_id” : 1, “fruits” : [ “peach”, “apple”, “pear” ], “size” : 4 },包含起始和结束下标

    8.4、查询内嵌文档

    • 在mongodb中文档的表现形式,像极了javascript中的对象,所以可以通过对象.属性的方法,来访问MongoDB中的内嵌文档,但也可以匹配整个内嵌文档
    • 方法一:如有文档

    {“name”: {“first”: “Joe”, “last”: “Schmoe”}, “age”: 27},在集合people中

    db.people.find({“name”: {“first”: “Joe”, “last”: “Schmoe”}}),这里可以找到有这个内嵌文档的文档,但是有一个缺点,就是查询条件必须匹配整个内嵌文档,否则就会匹配失败,同时顺序也不能够错,也就是要求精确匹配

    • 方法二:通过对象.属性的方法来访问,就不存在限制了

    db.people.find({“name.first”: “Joe”, “name.last”: “Schmoe”}),这里就要求精确匹配了

    • $elemMatch

    将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到

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

    9、排序、分页和跳过

    9.1、排序sort

    • 使用方法:db.collection.find().sort({“username”: 1})
    • 返回结果按升序排列,同时可以设置多个键排序

    db.collection.find().sort({“username”:1, “age”: -1})

    先以username升序排列,若username相同,则按照age降序排列

    9.2、分页limit

    • 使用方法:

    db.collection.find().limit(20),表示每页返回20条数据

    9.3、跳过skip

    • db.collection.find().skip(20),跳过前20个数据

    组合实现分页查询

    • db.collection.find().limit(20).sort({“age”: -1})

    按照年龄从大到小取出前二十条文档

    • db.collection.find().limit(20).skip(20).sort({“age”: -1})

    跳过前20条,取后20条的结果

    10、创建索引

    • 方法:db.users.ensureindex({“username”: 1})

    11、游标对象

    使用find函数返回的对象可以赋值给一个变量,这个变量就可以理解为是一个游标对象

    游标对象属性 :可迭代(iterable)

    游标对象方法 :hasNext()判断是否还有下一条数据

    next()获取下一条数据

    12、聚合函数(aggregate)

    12.1、count函数

    db.stu.count() # 返回fruit集合中的总文档数

    现有如下集合mycol:

    { "_id" : 1, "name" : "tom", "sex" : "男", "score" : 100, "age" : 34 }
    { "_id" : 2, "name" : "jeke", "sex" : "男", "score" : 90, "age" : 24 }
    { "_id" : 3, "name" : "kite", "sex" : "女", "score" : 40, "age" : 36 }
    { "_id" : 4, "name" : "herry", "sex" : "男", "score" : 90, "age" : 56 }
    { "_id" : 5, "name" : "marry", "sex" : "女", "score" : 70, "age" : 18 }
    { "_id" : 6, "name" : "john", "sex" : "男", "score" : 100, "age" : 31 }

    12.2、$group,$sum

    db.stu.aggregate({$group:{_id: “$sex”, Count: {“$sum”: 1}}})

    12.3、$group,$push

    db.stu.aggregate(
         {$group:
             {
                _id:"$gender",
                 name:{$push:"$name"}
             }
         }
    )  把所有的数据放到一起
    $$ROOT, 把整个文档放到一个数组中
    db.stu.aggregate(
         {$group:
             {
                 _id:null,
                 name:{$push:"$$ROOT"}
             }
         }
    )

    12.4、$match`

    db.stu.aggregate(
         {$match:{age:{$gt:20}}
         )

    12.5、$project

    db.stu.aggregate(
         {$project:{_id:0,name:1,age:1}}
         ) //控制显示的文档键

    12.6、$sort

    db.stu.aggregate(
         {$group:{_id:"$gender",counter:{$sum:1}}},
         {$sort:{counter:-1}}
    )

    12.7、$limit,$skip

    db.stu.aggregate(
         {$group:{_id:"$gender",counter:{$sum:1}}},
         {$sort:{counter:-1}},
         {$skip:1},
         {$limit:1}
    )

    13、数据库命令(runCommand)

    13.1、distinct函数

    db.runCommand({“distinct”: “fruit” , “key”: “fruits”})

    runCommand指令表示运行命令,括号内的参数的第一个键为具体的指令,指令所对应的的值是要操作的集合,key对应的是要操作的键。

    distinct所表达的意思是找出集合中某个键对应多少不同的值,也就是去重。

    13.2、group函数

    • 对文档进行分组,stocks集合中有以下文档:

    db.stock.insert({“day” : “2010/10/03”, “time”: “10/03/2010 03:57:01 GMT-400”, “price”: “4.23”})

    db.stock.insert({“day” : “2010/10/04”, “time”: “10/04/2010 11:28:39 GMT-400”, “price”: “4.27”})

    db.stock.insert({“day” : “2010/10/03”, “time”: “10/03/2010 05:00:22 GMT-400”, “price”: “4.10”})

    db.stock.insert({“day” : “2010/10/06”, “time”: “10/06/2010 05:27:58 GMT-400”, “price”: “4.30”})

    db.stock.insert({“day” : “2010/10/04”, “time”: “10/04/2010 08:34:50 GMT-400”, “price”: “4.01”})

    获取到每天最新的股票交易价格

    • 使用数据库命令的模式来执行group命令:
    db.runCommand({"group":{
        "ns": "stock",
        "key": {"day": true},  // 程序执行到这里之后,就已经对集合分组完毕了
        "$keyf": function(X){
            return X.toLowerCase() // 将需要排序的键进行转换,比如进行大小写转换
        },
        "initial":{"time":0},  // 每组文档遍历时候的初始值
        "$reduce":function(doc, prev){
            if(doc.time>prev.time){  // 判断时间大小,更新要显示的最近时间
                prev.time = doc.time;
                prev.price= doc.price;
            }
        },
        "condition": {"day": {"$gt": "2010/10/03"}},// 对分组进行条件限制,必须大于条件时间
        "finalize":function(prev){  //对返回结果做最后的修改,和限定
            ...
        }
    }})

    参数解析

    group:对集合执行的操作,即分组

    ns:需要操作的数据集

    key:以文档中的哪个键进行分组

    $keyf: 对需要分组的键进行条件转换

    initial: 每组文档的初始化值,也就是最终分完组后显示的字段的初始化值

    $reduce: 对分组后的显示文档做最后的操作,在上述案例中,就是找到那一组文档中时间最新的文档,然后把他的时间和股票交易价格显示到我们可以看到的结果

    condition: 对分组进行条件限制

    finalize: 对返回的结果做最后的修改

    #### 13.3、findAndModify

    db.runCommand({“findAndModify”: “stock”, # 操作集合stock

    ​ “query”: {“day”:{“$gt”: “2010/09/30”}}, # 查询条件

    ​ “sort”: {“day”: -1}, # 排序键

    ​ “remove”: true # 是否删除文档true

    })

    #### 13.4、创建固定大小的集合

    db.creatCollection(“my_collection”,{“capped”: true, “size”: 100000})

    参数解析

    my_collection:集合名

    capped:表示是否限值集合的大小,true表示限值,false表示不限制

    size:表示集合大小,这里表示100000个字节

    更多技术资讯可关注:gzitcast

    评论

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注

    这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理