mongodb数据库操作

僤鯓⒐⒋嵵緔 提交于 2019-12-02 18:09:53

### 安装在Ubuntu16.04:


```bash
# 创建数据库文件夹,这个是默认存放数据的地方,但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
```


### 卸载


```bash
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.


解决:


```bash
sudo nano /etc/systemd/system/mongdb.service  # 创建服务文件
```


```bash
[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查看


```shell
> 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 自定义查询


```js
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后,即使不提及文档中的其他键同样会返回


    ```mongo
    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:


```javascript
{ "_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


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


#### 12.4、$match`


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


#### 12.5、$project


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


#### 12.6、$sort


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


#### 12.7、$limit,$skip


```json
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命令:


  ```javascript
  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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!