wallleap / myblogs

blog, Issue
https://myblog.wallleap.cn
4 stars 1 forks source link

MongoDB学习笔记 #52

Open wallleap opened 4 years ago

wallleap commented 4 years ago

title: 在 Node 中使用非关系型数据库 MongoDB date: 2020-06-25 20:00 updated: 2020-06-25 20:00 cover: //cdn.wallleap.cn/img/pic/cover/202302dCEkfN.jpg category: 技术杂谈 tags:

MongoDB 是非关系型数据库的一种,相比起其他数据库而言,对前端开发者更友好

一、数据库简介

二、MongoDB 简介

三、MongoDB 的基本操作

在 MongoDB 中,数据库和集合都不需要我们手动创建,当我们创建文档时,如果文档所在的集合或数据库不存在则会自动创建数据库和集合

MongoDB

1、基本命令

2、数据库的 CRUD 操作

安装图形化工具:MongodbManagerFreeStudio-3T

向数据库中插入文档

当我们向集合中插入文档时,如果没有给文档指定 _id 属性,则数据库会自动为文档添加 _id,该属性用来作为文档的唯一标识,也可以自己指定,如果我们指定了,数据库就不会添加了,如果自己指定 _id 也必须确保它的唯一性

// 向 test 数据库中的 stus 集合中插入一个新的学生对象 {name:"swk",age:18,gender:"man"}
use test
db.stus.insert({name:"swk",age:18,gender:"man"})
db.stus.insert([
{name:"shs",age:38,gender:"man"},
{name:"bgj",age:18,gender:"female"},
{name:"zbj",age:28,gender:"man"}
])
ObjectId() // 根据时间戳、机器码
db.stus.insert({_id:"ts",name:"ts",age:18,gender:"man"}) // 建议不要自己指定

注:MongoDB 中的文档的属性值也可以是一个文档,当一个文档的属性值是文档时,我们称这为内嵌文档。

MongoDB 支持直接通过内嵌文档的属性进行查询,如果要查询内嵌文档,可以通过 . 的形式来匹配。属性名必须用引号括起来

查询

db.stus.find();
db.stus.find({});
db.stus.find({}).count();
db.stus.find({}).length();
db.stus.find({_id:"ts"});
db.stus.find({age: 28,name:"zbj"});
db.stus.find({age: 28})[1];
db.stus.find({age:{$gt:20}})

修改

db.stus.upadate({name:'shs'},{age:29}) // 替换
db.stus.upadate({name:'shs'},{$set{age:29, address:"lsh"}) // 修改,默认值修改一个
db.stus.upadate({name:'shs'},{$set{address:"lsh"})
db.stus.upadate({name:'shs'},{$unset{age:29}) // 删除
db.stus.upadate({name:'shs'},{$set{age:29, address:"lsh"}) // 添加配置项也可以修改多个

删除

db.stus.remove()
db.stus.remove({})   // 清空集合(性能略差)
db.stus.drop()     // 删除集合
db.database()    // 删除数据库
// 一般删除一个文档都是用这种方式
db.stus.inert([
  {
  name: "ts",
  isDel: 0
  },
  {
  name: "zbj",
  isDel: 0
  },
  {
  name:"shs",
  isDel: 0
  }
])
db.stus.updateOne({name:"ts"},{$et:{isDel:1}})
db.stus.find({isDel:0})

四、文档之间的关系

一对一(One to One)

db.wifeAndHusband.insert([
  {
    name:"hr",
    husband:{
      name:"gj"
    }
  },{
    name:"pjl"
    husband:{
        name:"wdl"
    }
  }
])

一对多(One to Many)/多对一(Many to One)

db.users.insert([
  {
  username:"swk"
  },{
  username:"zbj"
  }
])
db.order.insert({
  list:["pg","xj","dyl"],
  user_id:ObjectId("swk_Id")
})
var user_idd = db.users.findOne({username:"swk"})._id
db.order.find({user_id:user_idd})

多对多(Many to Many)

db.teachers.insert([
  {
  name: 'hqg',
  },
  {
  name: 'hys',
  },
  {
  name: "gxr"
  }
])
db.stus.insert([
  {
  name:"gj",
  tech_ids:[
    ObjectID('hqgID'),
    ObjectID("hysID")
  ]
  },
  {
  name:"swk",
  tech_ids: [
    ObjectID('hqgID'),
    ObjectID("hysID"),
    ObjectID("gxr")
  ]
  }
])
db.teachers.find()
db.stus.find()

五、sort 和投影

六、Mongoose 简介

1、简介

2、Mongoose 好处

3、新的对象

七、连接 MongoDB 数据库

  1. 下载安装 Mongoose npm i mongoose --save
  2. 在项目中引入 Mongoose var mongoose = require("mongoose")
  3. 连接数据库 mongoose.connect('mongodb://数据库IP地址:端口号/数据库名');
    • 端口号如果是默认端口号(27017),则可以省略不写
  4. [可选] 监听 MongoDB 数据库的连接状态
    • 在 Mongoose 对象中,有一个属性叫做 connection,该对象表示的就是数据库连接通过监视该对象的状态,可以来监听数据库的连接与断开
      • mongoose.connection.once("open",function(){}) 数据库连接成功的事件
      • mongoose.connection.once("close",function(){}) 数据库断开的事件
  5. 断开数据库连接(一般不需要调用) mongoose.disconnect()
    • MongoDB 数据库,一般情况下只需要连接一次,连接一次以后,除非项目停止、服务器关闭,否接连接一般不会断开
// 例子
var mongoose = require("mongoose")
mongoose.connect("mongodb://127.0.0.1/mongoose_test")
mongoose.connection.once("open",function(){
 console.log("数据库已连接")
})
mongoose.connection.once("close",function(){
 console.log("数据库已断开")
})
mongoose.disconnect()

八、Schema、Model 和 Document

有了 Model,就可以对数据库进行增删改查的操作了

Document 和集合中的文档一一对应,Document 是 Model 的实例,通过 Model 查询到结果都是 Document

Document 的方法

var mongoose = require("mongoose")
mongoose.connect("mongodb://127.0.0.1/mongoose_test", {useMongoClient:true})
// Schema对数据进行约束
// 将mongoose.Schema赋值给一个变量
var Schema = mongoose.Schema
// 创建Schema(模式)对象
var stuSchema = new Schema({
 name: String,
 age: Number,
 gender: {
    type: String,
    default: "male"
  },
  address: String
})

// Model 代表的是数据库中的集合,通过 Model 才能对数据库进行操作
// 通过 Schema 来创建 Model
// mongoose.model("要映射的集合名", 刚创建的Schema对象) Mongoose 会自动将集合名变为复数
var StuModel = mongoose.model("student", stuSchema)
// 向数据库中出入一个文档 model.create(文档, 回调函数)
stuModel.create({
  name: 'swk',
  age: 18,
  gender: 'male',
  address: "hgs"
}, function(err){
  if(!err){
    console.log("插入成功")
  }
})
stuModel.create([{
  name: 'shs',
  age: 28,
  gender: 'male',
  address: "lsh"
},{
  name: "bgj",
  age: 17,
  gender: "female",
  address: "bgd"
}], function(err){
  if(!err){
    console.log("插入成功")
  }
})

// 查询
StuModel.find({name:"shs"},function(err, docs){
  if(!err){
    console.log(docs)
    console.log(docs[0].name)
  }
})
/* StuModel.find({name:"shs"},{name:1, _id:0},function(err, docs){
  if(!err){
    console.log(docs)
  }
}) */
StuModel.find({name:"shs"},"name -_id",function(err, docs){
  if(!err){
    console.log(docs)
  }
})
StuModel.find({name:"shs"},"name -_id",{skip:3, limit:1},function(err, docs){
  if(!err){
    console.log(docs)
  }
})
StuModel.findOne({}, function(err, doc){
  if(!err){
    console.log(doc.name)
  }
})
StuModel.findById("...",function(err,doc){
  if(!err){
    console.log(doc)
  }
})

// Document对象是Model的实例
/* StuModel.findById("...",function(err,doc){
  if(!err){
    console.log(doc instantceof StuModel)
  }
})
 */

StuModel.updateOne({name: 'ts'}, {$set:{age:20}},function(err){
  if(!err){
    console.log("修改成功")
  }
})

StuModel.remove({name:"bgj"}, function(err){
  if(!err){
    console.log("删除成功")
  }
})

StuModel.count({}, function(err, count){
  if(!err){
    console.log(count)
  }
})

// 创建一个Document
var stu = new StuModel({
  name: "blm",
  age: 14,
  gender: "male",
  address: "bbt"
})
stu.save(function(err){
  if(!err){
    console.log("保存成功")
  }
})
StuModel.findOne({}, function(err, doc){
  if(!err){
    /* doc.update({$set:{age: 28}}, function(err){
      if(!err){
        console.log("修改成功")
      }
    }) */
    doc.age = 28
    save()

    /* doc.remove(function(err){
      if(!err){
        console.log("删除成功")
      }
    }) */
    // console.log(doc.get("age"))
    console.log(doc.age)
    // doc.set("name", "zhu")
    doc.name = "zhu"
    // console.log(doc.id)
    console.log(doc._id)
    console.log(doc.toJSON())
    doc = doc.toObject()
    delete doc.address
    console.log(doc)
  }
})

九、Mongoose 的模块化

定义一个模块,用来连接 MongoDB 数据库

// conn_mongo.js
var mongoose = require("mongoose")
mongoose.connect("mongodb://127.0.0.1/mongoose_test")

定义一个 student 的模型,几个模型就可以创建几个 js 文件

// student.js
var mongoose = require("mongoose")
var Schema = mongoose.Schema
var stuSchema = new Schema({
 name: String,
 age: Number,
 gender: {
    type: String,
    default: "male"
  },
  address: String
})
var StuModel = mongoose.model("student", stuSchema)
// exports.model = StuModel
module.exports = StuModel

其他地方引入

require("./conn_mongo")
// var Student = require("./models/student").model
var Student = require("./models/student")
Student.find({}, function(err, docs){
  if(!err){
    console.log(docs)
  }
})

练习

//1.进入my_test数据库
use my_test

//2.向数据库的 user 集合中插入一个文档  
db.users.insert({
    username:"sunwukong"
});

//3.查询user集合中的文档
db.users.find();

//4.向数据库的user集合中插入一个文档   
db.users.insert({
    username:"zhubajie"
});

//5.查询数据库user集合中的文档
db.users.find();

//6.统计数据库user集合中的文档数量
db.users.find().count();

//7.查询数据库user集合中username为sunwukong的文档
db.users.find({username:"sunwukong"});

//8.向数据库user集合中的username为sunwukong的文档,添加一个address属性,属性值为huaguoshan
db.users.update({username:"sunwukong"},{$set:{address:"huaguoshan"}});

//9.使用{username:"tangseng"} 替换 username 为 zhubajie的文档
db.users.replaceOne({username:"zhubajie"},{username:"tangseng"});    

//10.删除username为sunwukong的文档的address属性
db.users.update({username:"sunwukong"},{$unset:{address:1}});

//11.向username为sunwukong的文档中,添加一个hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}
//MongoDB的文档的属性值也可以是一个文档,当一个文档的属性值是一个文档时,我们称这个文档叫做 内嵌文档
db.users.update({username:"sunwukong"},{$set:{hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}}});
db.users.find();

//12.向username为tangseng的文档中,添加一个hobby:{movies:["A Chinese Odyssey","King of comedy"]}
db.users.update({username:"tangseng"},{$set:{hobby:{movies:["A Chinese Odyssey","King of comedy"]}}})

//13.查询喜欢电影hero的文档
//MongoDB支持直接通过内嵌文档的属性进行查询,如果要查询内嵌文档则可以通过.的形式来匹配
//如果要通过内嵌文档来对文档进行查询,此时属性名必须使用引号 
db.users.find({'hobby.movies':"hero"});

//14.向tangseng中添加一个新的电影Interstellar
//$push 用于向数组中添加一个新的元素
//$addToSet 向数组中添加一个新元素 , 如果数组中已经存在了该元素,则不会添加
db.users.update({username:"tangseng"},{$push:{"hobby.movies":"Interstellar"}});
db.users.update({username:"tangseng"},{$addToSet:{"hobby.movies":"Interstellar"}});
db.users.find();

//15.删除喜欢beijing的用户
db.users.remove({"hobby.cities":"beijing"});

//16.删除user集合
db.users.remove({});
db.users.drop();

show dbs;

//17.向numbers中插入20000条数据 7.2s
for(var i=1 ; i<=20000 ; i++){
    db.numbers.insert({num:i});
}

db.numbers.find()
db.numbers.remove({});

//0.4s
var arr = [];

for(var i=1 ; i<=20000 ; i++){
    arr.push({num:i});
}

db.numbers.insert(arr);

//18.查询numbers中num为500的文档
db.numbers.find({num:500})

//19.查询numbers中num大于5000的文档
db.numbers.find({num:{$gt:500}});
db.numbers.find({num:{$eq:500}});

//20.查询numbers中num小于30的文档
db.numbers.find({num:{$lt:30}});

//21.查询numbers中num大于40小于50的文档
db.numbers.find({num:{$gt:40 , $lt:50}});

//22.查询numbers中num大于19996的文档
db.numbers.find({num:{$gt:19996}});

//23.查看numbers集合中的前10条数据
db.numbers.find({num:{$lte:10}});

//limit()设置显示数据的上限
db.numbers.find().limit(10);
//在开发时,我们绝对不会执行不带条件的查询
db.numbers.find();

//24.查看numbers集合中的第11条到20条数据
/*
  分页 每页显示10条
    1-10     0
    11-20    10
    21-30    20
    。。。

    skip((页码-1) * 每页显示的条数).limit(每页显示的条数);

  skip()用于跳过指定数量的数据    

  MongoDB会自动调整skip和limit的位置
*/
db.numbers.find().skip(10).limit(10);

//25.查看numbers集合中的第21条到30条数据
db.numbers.find().skip(20).limit(10);

db.numbers.find().limit(10).skip(10);

//26.将dept和emp集合导入到数据库中
db.dept.find()
db.emp.find()

//27.查询工资小于2000的员工
db.emp.find({sal:{$lt:2000}});

//28.查询工资在1000-2000之间的员工
db.emp.find({sal:{$lt:2000 , $gt:1000}});

//29.查询工资小于1000或大于2500的员工
db.emp.find({$or:[{sal:{$lt:1000}} , {sal:{$gt:2500}}]});

//30.查询财务部的所有员工
//(depno)
var depno = db.dept.findOne({dname:"财务部"}).deptno;
db.emp.find({depno:depno});

//31.查询销售部的所有员工
var depno = db.dept.findOne({dname:"销售部"}).deptno;
db.emp.find({depno:depno});

//32.查询所有mgr为7698的所有员工
db.emp.find({mgr:7698})

//33.为所有薪资低于1000的员工增加工资400元
db.emp.updateMany({sal:{$lte:1000}} , {$inc:{sal:400}});
db.emp.find()
oohirosama commented 5 months ago

最近使用mongoose遇到一个问题,我的文档下面,包含一个子文档,这个子文档是一个数组。 数组中的每一个元素都是一个对象,其中一个键值对是日期。我需要将这个数组按照日期的顺序排序。 我想了个方法,就是把数组读取出来,然后排好顺序,替换掉原来的数据。但是这个方法感觉即麻烦又危险。 mongoose有没有对子文档的排序功能呢?

wallleap commented 5 months ago

@oohirosama 可以使用 $push$each 结合 $sort 来实现对子文档数组的排序。

假设你有一个包含子文档数组的父文档模型如下:

const parentSchema = new mongoose.Schema({
  childDocs: [{
    date: { type: Date },
    // 其他字段...
  }]
});

const ParentModel = mongoose.model('Parent', parentSchema);

现在,如果你想按照子文档数组中的日期字段进行排序,你可以使用以下方法:

ParentModel.updateOne(
  { _id: parentId }, // 根据父文档的ID进行查询
  { $push: { childDocs: { $each: [], $sort: { date: 1 } } } }, // 1表示升序,-1表示降序
  function(err, result) {
    if (err) {
      // 处理错误
    } else {
      // 更新成功
    }
  }
);

在上面的示例中,$push 操作将子文档数组中的所有元素重新插入,使用 $each 将所有元素逐个添加到数组中,然后使用 $sort 按照日期字段对数组进行排序。