node - mongodb

305 阅读6分钟

下载mongo

image-20210929162458725

配置mongo

容器命名

打开mongo映像,容器名称推荐mongo-端口名称

image-20210929171556047

创建目录

在 File Station 新建文件夹,并在configdb内放入一个 configdb.conf 的空文档

image-20210929193809615

高级设置

创建mongodb容器 配置存储空间路径

image-20210929193255086

本地端口绑定为容器端口

image-20210929193313366

下方输入命令

mongod --config /data/configdb/mongod.conf

image-20210929194205602

应用并运行

创建超级用户

打开容器,终端机 → 新增 → bash:

ll // 查看数据库目录

mongo // 进入mongo数据库

use admin
db.createUser({user: "test1", pwd: "test1", roles: [{role: "root", db: "admin"}]})

exit // 退出

普通用户:

use test2db
db.createUser({user: "test2", pwd: "test2", roles: [{role: "readWrite", db: "test2db" }]})

创建完超级用户,需要把数据库的安全保障打开,数据库的安全配置,用户权限配置策略,之后找教程了解

更改配置文件

打开mongod.conf

security:
    authorization: enabled

注意层级换行用四个空格,不要用tab,key: value 中间也要有一个空格,不然会报错

mongodb+srv://maxuan:601109@192.168.31.107:27017/admin

安装robot 3t

下载

Robo 3T | Free, open-source MongoDB GUI

连接

安装完成后,点击左上角 Create → 修改Name 和 Address ,第二个选项卡 Authentication

image-20211013204721962

勾选上下两个checkbox,填写用户名 密码,点击左下方 Test,链接成功 Save

image-20211013204913448

新建数据库

连接名右键 → Creat Database → 输入数据库名 → 确定

image-20211013205807238image-20211013205946021

右键Collections → Create Collection 来创建集合

image-20211013210122325image-20211013210209685

grade右键 Insert Document 在文档内插入数据

image-20211013223150484

点击save保存,grade内就被插入了一条数据

image-20211013223313086

Node.js连接mongodb

npm init -y
npm i mongodb -s

server.js

// 获取实例
const MongoClient = require('mongodb').MongoClient;

// 异步变同步自执行函数包裹
(async function() {
  // 设置服务端信息
  const client = new MongoClient('mongodb://maxuan:601109@192.168.31.107:27017',{useNewUrlParser:true})

  // 连接服务端
  await client.connect()
  console.log('数据库连接成功');

  // 获取数据库
  const db = client.db('test')

  // 获取集合
  const grade = db.collection('grade')
  console.log(grade);
  // 下方执行操作--------------------------------------------------
  
  // 先删除所有数据
  grade.deleteMany()

  // 关闭连接
  client.close()

})()

nodemon server.js

数据库连接成功
Collection {
  s: {
    db: Db { s: [Object] },
    options: {
      raw: false,
      promoteLongs: true,
      promoteValues: true,
      promoteBuffers: false,
      ignoreUndefined: false,
      ignoreUndefined: false,
      bsonRegExp: false,
      serializeFunctions: false,
      fieldsAsRaw: {}
    },
    readConcern: undefined,
    writeConcern: undefined,
    slaveOk: false
  }
}

增加

插入一条数据

server.js

const res = await grade.insertOne({
  name: 'Max',
  age: 23,
  hobby:['学习','学习','还是学习'],
  score: 60
})
console.log(res)

/* 注意大版本不同返回值也不一样
{
  acknowledged: true,
  insertedId: new ObjectId("61670a154a298b9852c871ba")
}
*/

数据插入完成后 返回 true 和 新对象的id

image-20211014002856663

插入多条数据

const res = await grade.insertMany([
  {name: 'Max',age: 23,hobby:['学习','学习','还是学习'],score: 60},
  {name: '李四',age: 40,hobby:['吃饭','睡觉'],score: 60},
  {name: '王五',age: 20,hobby:['吃饭','篮球'],score: 60},
  {name: '赵六',age: 16,hobby:['学习'],score: 60},
  {name: '小丽',age: 38,hobby:['学习'],score: 60},
  {name: '小红',age: 40,hobby:['学习'],score: 60},
  {name: '小马',age: 27,hobby:['学习'],score: 60},
  {name: '小王',age: 59,hobby:['学习'],score: 60},
  {name: '小黑',age: 16,hobby:['学习'],score: 60},
  {name: '小二',age: 18,hobby:['学习'],score: 60},
])
console.log(res);

/*
{
  acknowledged: true,
  insertedCount: 10,
  insertedIds: {
    '0': new ObjectId("61670c4e1363298553df7fd2"),
    '1': new ObjectId("61670c4e1363298553df7fd3"),
    '2': new ObjectId("61670c4e1363298553df7fd4"),
    '3': new ObjectId("61670c4e1363298553df7fd5"),
    '4': new ObjectId("61670c4e1363298553df7fd6"),
    '5': new ObjectId("61670c4e1363298553df7fd7"),
    '6': new ObjectId("61670c4e1363298553df7fd8"),
    '7': new ObjectId("61670c4e1363298553df7fd9"),
    '8': new ObjectId("61670c4e1363298553df7fda"),
    '9': new ObjectId("61670c4e1363298553df7fdb")
  }
}
*/

查询

查找一条数据

const res = await grade.findOne({
  name: 'Max'
})
console.log(res);

返回结果

{
  _id: new ObjectId("61670c4e1363298553df7fd2"),
  name: 'Max',
  age: 23,
  hobby: [ '学习', '学习', '还是学习' ],
  score: 60
}

查找聚合全部数据

因为返回的是一个原型对象,需要用toArray() 处理一下

const res = await grade.find().toArray()

console.log(res);

因为返回结果就是所有数据,log不贴了

查找全部数据 附加条件

const res = await grade.find({
  name: '李四'
}).toArray()

console.log(res);

/*
[
  {
    _id: new ObjectId("61670c4e1363298553df7fd3"),
    name: '李四',
    age: 40,
    hobby: [ '吃饭', '睡觉' ],
    score: 60
  }
]
*/

这里打印结果变成了数组,如果条件共有,则会返回多个

查找范围

比较运算符

  • $gt — 大于
  • $lt — 小于
  • $gte — 大于等于
  • $lte — 小于等于
比较运算符
const res = await grade.find({
  age: {
    $gt: 20 // 大于20岁
  }
}).toArray()

逻辑运算符

  • $and — 并且
  • $or — 或者
  • $all — 指定字段中的所有
  • $in — 指定字段中的或者
  • $size — 指定字段的数据的数量
  • $not — 不包含
  • $nor — 或者不
$and 并且

and执行一个逻辑and操作在一个或者多个表达式上,并且查询数组中指定的所有表达式指定的文档document,and执行一个逻辑and操作在一个或者多个表达式上,并且查询数组中指定的所有表达式指定的文档document,and使用短路求值,如果第一个表达式的结果是false,MongoDB将不会执行剩余的表达式;

const res = await grade.find({
  $and: [
    {name: '王五'},
    {age: 20}
  ]
}).toArray()

/*
[
  {
    _id: new ObjectId("61670c4e1363298553df7fd4"),
    name: '王五',
    age: 20,
    hobby: [ '吃饭', '篮球' ],
    score: 60
  }
]
*/

简单的and查询可以隐式省略

const res = await grade.find({
    name: '王五',
    age: 20
  ]
}).toArray()

AND查询使用多个表达式指定相同的操作:

db.inventory.find( {
    $and : [
        { $or : [ { price : 0.99 }, { price : 1.99 } ] },
        { $or : [ { sale : true }, { qty : { $lt : 20 } } ] }
    ]
} )

以上字段将会查询price字段值等于0.99或1.99并且sale字段值为true或者qty小于20的所有文档

使用隐式AND操作无法构建此查询,因为它不止一次使用$or操作

$or 或者
const res = await grade.find({
  $or:[
    {name:'李四'},
    {age: 40}
  ]
}).toArray()

console.log(res);

/*
[
  {
    _id: new ObjectId("61670c4e1363298553df7fd3"),
    name: '李四',
    age: 40,
    hobby: [ '吃饭', '睡觉' ],
    score: 60
  },
  {
    _id: new ObjectId("61670c4e1363298553df7fd7"),
    name: '小红',
    age: 40,
    hobby: [ '学习' ],
    score: 60
  }
]
*/
$all 指定字段的所有

返回爱好字段的数组内所有包含 ‘ 学习 ’ 的人的数据

const res = await grade.find({
  hobby:[
    $all: ['学习'],
  ]
}).toArray()

console.log(res);
$in 指定字段的或者

使用or操作比较字段的值是否等于某个值时可以使用or操作比较字段的值是否等于某个值时可以使用in替换$or操作;

返回爱好字段的数组内所有包含 ‘ 学习 ’ 或 ‘ 睡觉 ’ 的人的数据

const res = await grade.find({
  hobby:[
    $in: ['学习', '睡觉'],
  ]
}).toArray()

console.log(res);
$size 指定字段的数据的数量

返回hobby字段数组内,成员有3个的数据

const res = await grade.find({
  hobby:[
    $size: 3
  ]
}).toArray()

console.log(res);
$not 不包含

$not执行一个逻辑not操作在指定的表达式并查询到不匹配表达式的文档,这包含不包括字段的文档

db.inventory.find( { price: { $not: { $gt: 1.99 } } } )

此查询将会查询inventory集合中的文档,条件如下: price字段小于等于1.99或者price不存在

{ not: { gt: 1.99 } } 不同于lte操作,{ lte: 1.99 }操作只会返回price字段存在并且小于等于1.99的字段。

记住not操作符只会影响其他操作符不能独立检查字段和文档,因此使用not操作符只会影响其他操作符不能独立检查字段和文档,因此使用not做逻辑析取和$ne操作测试字段内容;

使用$not操作时考虑如下操作:

操作$not操作符和其它操作符一致但是会产生一些意想不到的结果,比如数组之类的数据类型;

not操作符不支持not操作符不支持regex正则表达式操作,使用//或者你的驱动接口代替,使用语言的正则表达式功能创建正则表达式对象;

考虑下面的例子使用模式匹配//:

db.inventory.find( { item: { $not: /^p.*/ } } )

此查询将会查询inventory集合中item字段不是以p开头的所有文档

$nor 或者不

这样返回的结果就不包含name为Max和age为40的人了

const res = await grade.find({
  $nor:[
    {name:'Max'},
    {age: 40}
  ]
}).toArray()

下面的例子结合比较运算符

// 查询年龄不大于20或者不小于16的人
const res = await grade.find({
  $nor:[
    {
      age: {
        $gt: 20
      }
    },
    {
      age:{
        $lt: 16
      }
    }
  ]
}).toArray()

console.log(res);

/*
[
  {
    _id: new ObjectId("61670c4e1363298553df7fd4"),
    name: '王五',
    age: 20,
    hobby: [ '吃饭', '篮球' ],
    score: 60
  },
  {
    _id: new ObjectId("61670c4e1363298553df7fd5"),
    name: '赵六',
    age: 16,
    hobby: [ '学习' ],
    score: 60
  },
  {
    _id: new ObjectId("61670c4e1363298553df7fda"),
    _id: new ObjectId("61670c4e1363298553df7fdb"),
    name: '小二',
    age: 18,
    hobby: [ '学习' ],
    score: 60
  }
]
*/
$regex 正则表达式
const res = await grade.find({
  name: {
    $regex: /^李/
  }
}).toArray()

console.log(res);

/*
[
  {
    _id: new ObjectId("61670c4e1363298553df7fd3"),
    name: '李四',
    age: 40,
    hobby: [ '吃饭', '睡觉' ],
    score: 60
  },
  {
    _id: new ObjectId("61670c4e1363298553df7fd7"),
    name: '李红',
    age: 40,
    hobby: [ '学习' ],
    score: 60
  }
]
*/

分页查询

limit() 查询数量

查询当前聚合的前两条数据

const res = await grade.find().limit(2).toArray()

console.log(res);
skip() 查询跳过

跳过前两条数据,获取后2条数据

const res = await grade.find().skip(2).limit(2).toArray()
sort() 查询排序

返回所有数据,按照年龄正序递增,如果参数为 -1,则是倒序递减

const res = await grade.find().sort({
  age: 1
}).toArray()
分页查询示例

索引 - 1 后 × size 获得跳过的数量,第一页为 0,最后一页需要再做处理

const pageIndex = 2 // 当前页面索引
const pageSize = 3 // 一页显示的数据
const res = await grade.find().skip((pageIndex -1)* pageSize).limit(pageSize).toArray()

数据的遍历处理

forEach()

对所有的数据单独处理

res.forEach(ele => {
  console.log(ele)
})
map()

所有名字打包为一个数组返回给前端

let names = res.map(ele => ele.name)
console.log(names)
// ['张三', '李四', '王五']

聚合函数

  • $sum — 当前组求和
  • $avg — 当前组平均值
  • $min — 当前组最小值
  • $max — 当前组最大值
  • $first — 当前组第一条

数组内包裹对象,group进行分组,导出为一个个对象,第一个属性必须id,属性为需要统计的字段前面加上group 进行分组,导出为一个个对象,第一个属性必须为 _id,属性为需要统计的字段前面加上 聚合结果count对象内 $sum 值为1,表示true,进行求和

$sum 求和
const res = await grade.aggregate([
  {
    $group: {
      _id: '$age',
      count: {
        $sum: 1
      }
    }
  }
]).toArray()

console.log(res);

/*
[
  { _id: 40, count: 2 },
  { _id: 59, count: 1 },
  { _id: 20, count: 1 },
  { _id: 27, count: 1 },
  { _id: 18, count: 1 },
  { _id: 38, count: 1 },
  { _id: 23, count: 1 },
  { _id: 16, count: 2 }
]
*/
$avg 求平均值

以age分组,$avg平均值函数,指定为score,返回以年龄分组的分数平均数

const res = await grade.aggregate([
  {
    $group: {
      _id: '$age',
      avgScore: {
        $avg: '$score'
      }
    }
  }
]).toArray()

console.log(res);
/*
[
  { _id: 16, avgScore: 64.66666666666667 },
  { _id: 40, avgScore: 60 },
  { _id: 20, avgScore: 81 },
  { _id: 27, avgScore: 86 },
  { _id: 18, avgScore: 52 },
  { _id: 23, avgScore: 76 }
]
*/
$min 最小值

返回以年龄分组的每组最小分数

const res = await grade.aggregate([
  {
    $group: {
      _id: '$age',
      avgScore: {
        $min: '$score'
      }
    }
  }
]).toArray()

console.log(res);
$max 最大值

形式同 $min,省略

$first 第一条

返回当前年龄分组的,第一个名字

const res = await grade.aggregate([
  {
    $group: {
      _id: '$age',
      avgScore: {
        $first: '$name'
      }
    }
  }
]).toArray()

/*
[
  { _id: 16, avgScore: '赵六' },
  { _id: 40, avgScore: '李四' },
  { _id: 20, avgScore: '王五' },
  { _id: 27, avgScore: '小马' },
  { _id: 18, avgScore: '小二' },
  { _id: 23, avgScore: 'Max' }
]
*/

更改

更新一条name为Max的数据,score 为 80

const res = await grade.updateOne(
  {
    name: "Max"
  },
  {
    $set:{
      score: 80
    }
  }
)
console.log(res);
/*
{
  acknowledged: true, // 承认
  modifiedCount: 1, // 修改次数
  upsertedId: null, // 更新id
  upsertedCount: 0, // 插入计数
  matchedCount: 1 // 匹配数
}
*/

删除

删除一条name为李四的数据

const res = await grade.deleteOne(
  {
    name: "李四"
  }
)
console.log(res);

/*
{ 
	acknowledged: true, 
	deletedCount: 1 
}
*/