node.js连接mongodb数据库

第一种

安装

在命令行中下载mongoose

npm install mongoose --save

引入mongoose并连接数据库

// 引入第三方包mongoose
const mongoose = require('mongoose');
// 要管理员的账号和密码才能进行数据库操作
// mongoose.connect('mongodb://user:pass@localhost:port/database')
// user表示用户名,pass表hi是密码,port表示数据库的端口mongodb的默认端口27017,database表示数据库的名字
// 连接数据库
mongoose.connect('mongodb://127.0.0.1/blog', {
    useUnifiedTopology: true,
    useNewUrlParser: true
}).then(() => { //这里面的then()方法是如果成功输出成功,如果失败在catch()方法里面输出失败
    console.log('数据库成功');
}).catch(() => {
    console.log('失败');
});

配置Schema(通过require获取)

就是

// 1.引入mongoose模块
const mongoose = require('mongoose');
// 2.创建文章集合类型  定义一个Schema  Schema里面的对象和数据库表里面的字段需要一一对应
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'] //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
    },
    author: {
        type: mongoose.Schema.Types.ObjectId, //要将文章中的作者和用户中的作者进行关联
        ref: 'User', //这个属性就是用来将文章集合和用户集合进行关联’User‘就是集合的名字
        required: [true, '请传递作者'],
    },
    publishDate: {
        type: Date,
        default: Date.now,
    },
    cover: {
        type: Number,
        default: 1      //表示默认参数,如果不写,那么数据库就会默认为1
    },
    content: {
        type: String
    }
});
// 3.根据规则常见集合,定义数据库模型  操作数据库
// model 里面的第一个参数要注意首字母要大写  2,要和数据库表(集合名称对应)
// 第二个参数要和Schema上面的一样
const Article = mongoose.model('Article', articleSchema);
//Article会默认和数据库中的Articles进行连接,我们也可以指定数据名词,设置第三个参数就OK了
//这样那么Article就和artes建立了连接
//const Article = mongoose.model('Article', articleSchema,'artes');
// 4.将集合规则作为模板成员进行导出
module.exports = {
    Article
}

模块化配置

就是通过导入导出的方式来进行模块化

const mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1/blog', {
    useUnifiedTopology: true,
    useNewUrlParser: true
}).then(() => { //这里面的then()方法是如果成功输出成功,如果失败在catch()方法里面输出失败
    console.log('数据库成功');
}).catch(() => {
    console.log('失败');
});
// 导出数据
module.exports=mongoose;

引入数据库

// 1.引入mongoose模块
const mongoose = require('mongoose');
// 2.创建文章集合类型
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'] //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
    },
    author: {
        type: mongoose.Schema.Types.ObjectId, //要将文章中的作者和用户中的作者进行关联
        ref: 'User', //这个属性就是用来将文章集合和用户集合进行关联’User‘就是集合的名字
        required: [true, '请传递作者'],
    },
    publishDate: {
        type: Date,
        default: Date.now,
    },
    cover: {
        type: String,
        default: null
    },
    content: {
        type: String
    }
});
// 3.根据规则常见集合
const Article = mongoose.model('Article', articleSchema);
// 4.将集合规则作为模板成员进行导出
module.exports = {
    Article
}

操作那个数据库引入就可以了

require('./')
操作添加,删除,修改

预定义修饰符

lowercase、uppercase、trim

mongoose提供了预定义模式修饰符,可以对我们的数据进行一些格式化

// 1.引入mongoose模块
const mongoose = require('mongoose');
// 2.创建文章集合类型
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'], //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
        trim:true           //表示的是如果用户输入的数据两边有空格可以通过这个方法取消空格
    }
});

自定义修饰符Getters与Setters

除了mongoose内置的修饰符以外,我们还可以通过set(建议使用)修饰符在增加数据的时候对数据进行格式化

也可以通过get(不建议使用)在实力获取数据的时候对数据进行格式化。

这是set方法

// 1.引入mongoose模块
const mongoose = require('mongoose');
// 2.创建文章集合类型
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'], //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
        trim:true           //表示的是如果用户输入的数据两边有空格可以通过这个方法取消空格
        pic:{
            type:String,
            set(parmas){    //增加数据的时候对pic字段进行处理
                    //parmas可以获取pic的值、返回的数据就是pic在数据库中实际保存的值
                    /**
                    *www.baidu.com              http://www.baidu.com
                    *http://www.baidu.com       http://www.baidu.com
                    */
                    //如果用户没有传入地址
                    if(!parmas){
                     return ''; 
                    }else{
                        if(parmas.indexOf('http://')!=0 $$ parmas.indexOf('http://')!=0){
                            return 'http://'+parmas;
                        }
                        return parmas;
                    }
                    
                }
            }       //表示自定义例如我们必须要http://www.baidu.com,但是有些用户不输入http://,所以需要进行自定义设置
    }
});

这是get方法可以去看一下,不好用

// 1.引入mongoose模块
const mongoose = require('mongoose');
// 2.创建文章集合类型
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'], //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
        trim:true           //表示的是如果用户输入的数据两边有空格可以通过这个方法取消空格
        name:{
        type:String,
        get(params){
                return "a001"+params    //表示的是在获取数据的时候添加一个a001(只有同过什么.什么添加的时候)
            }
        }
    }
});

Mongoose的索引

索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快,MONGODB的索引几乎与传统的关系型数据库一模一样,这其中也包括了一些基础的查询优化技巧。

mongoose中除了以前创建索引的方式,我们也可以在定义Schema的时候指定创建索引。

// 2.创建文章集合类型  定义一个Schema  Schema里面的对象和数据库表里面的字段需要一一对应
const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        // 唯一索引、
        unique:true
    },
     cover: {
        type: String,
         /// 普通索引
         index:true,
        default: null
    },
    publishDate: {
        type: Date,
        default: Date.now,
    },
    content: {
        type: String
    }
});

mongoose内置的CURD方法

[图片上传失败...(image-4b0014-1601889185797)]

就是有的搜索时finByID我们也可以根据属性来自定义一个

const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'] //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
    },
    sn:{
        
    },
    content: {
        type: String
    }
});
//静态方法(要加一个statics)
articleSchema.statics.findBySn=function(sn,cb){
    this.find({"sn":sn},function(err,docs){
        cd(err,docs);
    })
}

//实例方法(基本没用)
articleSchema.methods.print=function(sn,cb){
    console.log(this)
}


// 然后存储一个信息

var user=new articleSchema({
    title:'2312',
    sn:'123456',
    content:'29'
});
user.save();

//静态方法
// 通过Model.findBySn('123456',(){})来查询数据

// 实例方法
//user.print();

数据校验

这是在Schema里面

required:表示这个数据必须传入
max:用于Number类型数据,最大值
min:用于Number类型数据,最小值
enum:枚举类型,要求数据必须满足枚举值   enum:['0','1','2']注意它使用在String类型中的
match:增加的数据必须符合match(正则)的规则 注意它使用在String类型中的
maxlenth:最大长度
minlength:最小长度
default:表示默认值

自定义数据校验

const articleSchema = new mongoose.Schema({
    title: {
        type: String,
        maxlength: 20, //最大长度
        minlength: 4, //最小长度
        required: [true, '请填写文章标题'] //第一个参数是告诉这个是必填字段,第二个参数表示错误信息
    },
    content: {
        type: String,
        //自定义数据校验  任意的类型里面
        validate:function(sn){
            return sn.length>=10;
        }
    }
});

**mongoose中使用aggregate聚合管道(后期可以在看一下)

都可以写多次

$project(筛选指定的列)

修改文档结构,可以用来重命名,增加或删除文档中的字段。

要求查找order只返回文档中的teade_no和all_price字段(order表示数据表)

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $project:{teade_no:1,all_price1}
    }
])

$match(过滤)

作用:用于过滤文档,用法类似于find()方法中的参数。

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $project:{teade_no:1,all_price1}  
    },
    {
    $match:{"all_price":{$gte:90}}  //这个意思时查找总价格大于90的数据
    }
])

$group???没懂

将集合中的文档进行分组,可用于统计结果

统计每个订单的订单数量,按照订单号分组

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $group:{_id:"$order_id",total:{$sum:1}}
    }
])

$sort(排序)

将集合文档进行排序

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $project:{teade_no:1,all_price1}  
    },
    {
    $match:{"all_price":{$gte:90}}  //这个意思时查找总价格大于90的数据
    },
    {
       $sort:{"all_price":-1}  //以 all_price进行排序  -1降序  1升序
    }
])

$limit(查几条数据)

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $project:{teade_no:1,all_price1}  
    },
    {
    $match:{"all_price":{$gte:90}}  //这个意思时查找总价格大于90的数据
    },
    {
       $sort:{"all_price":-1}  //以 all_price进行排序  -1降序  1升序
    },
    {
        $limit:1        //表示只返回一条数据
    }
])

$skip (跳过几条数据)

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $project:{teade_no:1,all_price1}  
    },
    {
    $match:{"all_price":{$gte:90}}  //这个意思时查找总价格大于90的数据
    },
    {
       $sort:{"all_price":-1}  //以 all_price进行排序  -1降序  1升序
    },
    {   
        $skip:1     //跳过几条数据
    }
])

$unwind

$unwind管道以document中的数组类型的字段进行拆分,每条包含数组中的一个值。

比如拆分likes:10这条数据,先来看看整体数据信息吧:

{
    "_id" : ObjectId("5e86e2ad88e64443e448dfd2"),
    "title" : "NoSQL Overview",
    "description" : "No sql database is very fast",
    "by_user" : "runoob.com",
    "url" : "http://www.runoob.com",
    "tags" : [ 
        "mongodb", 
        "database", 
        "NoSQL"
    ],
    "likes" : 10
}
router.get('/getInfo', async (req, res) => {
  let data = await Content.aggregate([
    {
      $match: {
        likes: 10
      }
    },
    {
      $unwind:'$tags'
    },
    {
      $project: { _id: 1, by_user: 1, title: 1, title: 1, description: 1, url: 1, tags: 1, likes: 1 }
    },
  ])
  res.json({
    data
  })
})
img

$lookup(表关联操作)

order.aggregate([   //每一个管道是一个对象,对象里面实现具体的功能
    {
        $lookup:{
            from:"order_item",                      //就是你这个表要和那个表进行关联
            localField:"order_id",                  //表示你order表关联的字段
            foreignField:"order_id",                //表示你order_item表与order表关联的id
            as:"items"                              //as表示你要关联的数据要放在哪里order的那个属性里面
        }
    }
])



//想获取的这样的数据
[
    {
        Order_id:"",
        Trade_no:"",
        items:[
            {
                title:"鼠标",
                name:"名字"
            },
            {
                title:"键盘",
                name:"名字"
            }
        ]
    }
]
//要观察谁关联谁
db.order.insert([
  { order_id: "1", uid: 10, trade_no: "111", all_price: 100, all_num: 2 },
  { order_id: "2", uid: 7, trade_no: "222", all_price: 90, all_num: 2 },
  { order_id: "3", uid: 9, trade_no: "333", all_price: 20, all_num: 6 }
]);
//=================================

db.order_item.insert([
  { order_id: "1", title: "商品鼠标1", price: 50, num: 1 },
  { order_id: "1", title: "商品鼠标2", price: 50, num: 1 },
  { order_id: "1", title: "商品鼠标3", price: 0, num: 1 },
  { order_id: "2", title: "牛奶", price: 50, num: 1 },
  { order_id: "2", title: "酸奶", price: 40, num: 1 },
  { order_id: "3", title: "矿泉水", price: 2, num: 5 },
  { order_id: "3", title: "毛巾", price: 10, num: 1 }
]);
//=================================

db.order.aggregate([
    {
        $project:{
            order_id:1,
            uid:1,
            trade_no:1,
            all_price:1,
            all_num:1
        }
    },
    {
        $match:{
            all_price:{
                $gte:90
            }
        }
    },
    {
        $sort:{
            all_price:-1
        }
    },
    {
        $limit:2
    },
    {
        $skip:1
    },
     {
        $lookup:{
            from:'order_item',         // 要关联的表
            localField:'order_id',     //order表中的order_id
            foreignField:'order_id',   // order_item表中的order_id
            as:'items'
        }
    } 
])
//=================================

db.order_item.aggregate([
    {
        $group:{
            _id:'$order_id',total:{
                $sum:'$num'
            }
        }
    },
])

//=================================

db.order.aggregate([
    {
        $lookup:{
            from:'order_item',         // 要关联的表
            localField:'order_id',     //order表中的order_id
            foreignField:'order_id',   // order_item表中的order_id
            as:'items'
        }
    }    
])

查询一个表,找出商品名称是酸奶的商品,酸奶这个商品对应的订单的订单号以及订单总价格。

//先查一个表,然后获取出数据,再通过获取的数据查下一个表
OrderItemModel.find({"_ id":"5b743da92c327f8d1b360546"}, function(err,docs){
    // console. log(docs);
    var order_item=JSON. parse( JSON. stringify(docs));
    var order_id=order_item[0].order_ id; 
    OrderModel. find({"order_ id":order_ id} ,function(err, order){
    //
    console. log(order); 
    order_ item[0] . order. info=order[0];
    console. log(order_ item )
    })
}

多个表的关联存储

这里就是通过输入id来进行关联

[图片上传失败...(image-b2baf1-1601889185798)]

//article.js文件
// 文章表,表结构
var Schrma=mongoose.Schrma;
var ArticleSchema=new Schema({
    title:{
        type:String,
        unique:true
    },
    cid:{
        type:{
            type:Schema.Types.ObjectId
        },//分类ID
    },
    author_id:{
        type:Schema.Types.ObjectId
    },//用户的id
    author_name:{
        type:String
    },
    descripton:String,
    content:String
})


//文章分类

var ArticleSchema=new mongoose.Schema({
    title:{
        type:String,
        unique:true
    },
    addtime:{
        type:Date
    },
    descripton:String,
})

//用户名
var ArticleSchema=new mongoose.Schema({
    username:{
        type:String,unique:true
    },
    password:String,
    name:String,
    age:Number,
    sex:String,
    tel:Number,
    status:{
        type:Number,
        default:1
    }
})


//分类增加
var cate=new 文章分类({
    title:'国内行文',
    descripton:'国内行文'
})
cate.save();

//增加用户
var cate=new 用户表({
    username:'zhangfa ',
    password:'nfusdhfk',
    name:'sfsdaf',
    age:20,
    sex:'nan',
    tel:20,
    status:1
})
cate.save();


//文章表
var cate=new 文章表({
    title:'习近平访问',//国际新新闻
    cid:'查找国际新闻的id与他关联',
    author_id:'获取用户的id与他关联',//用户的id
    author_name:"获取用户的名与之关联",
    descripton:'我的描述’',
    content:'这里面时内容详情'
})
cate.save();

mongoose实现多个表查询Populate

mongoose使用关联查询的时候,你首先要知道主键与外键

要使用他之前要在Schema里面定义ref那个表的key有外键那个就是用ref

const articleSchema = new mongoose.Schema({
    author: {
        type: mongoose.Schema.Types.ObjectId, //要将文章中的作者和用户中的作者进行关联
        ref: 'User', //这个属性就是用来将文章集合和用户集合进行关联’User‘就是集合的名字
        required: [true, '请传递作者'],
    }
});


//查询的方法

// 注意要是用populate需要引入用到的Moudel
//说建议用聚合管道查询
    let articles = await pagination(Article)//pagination分页,
        .find()//查询
        .page(page)//客户端传递过来的页码
        .size(2)//显示几个
        .display(3)//最多
        .populate("author") //populate('')这个方法里面存放的就是你要查询的字段信息
        .exec();

增加数据

//实例化Model   通过实例化User 的Molde创建添加数据
var admin = new User({
    username: 'admin',
    passworld: '123456',
    email: 'admin@admin.com'
});
admin.save(function(err, ret) {//里面的两个方法用来查看是否将数据存进去
    if (err) {
        console.log('失败');
    } else {
        console.log('成功');
        console.log(ret);
    }
})

一次性写法:

    new student(req.query).save(function(err, user) {
        if (err) {
            return next(err)
        }
        res.status(200).json({
            err_code: 0,
            message: 'OK',
            user: user
        })
    })

查询数据

​ 查询所有数据:

// ***********************
// #region 查询数据所有数据
// ***********************
User.find(function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})

按照条件查询数据(查询出来的是数组里面存放的是对象):

User.find({
    username: '张三'
    //年龄大于18去API查
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})

按照条件查询数据(查询出来的是数据直接存放在对象中()):

User.findOne({ //如果第一个没有参数,那么查询的就是第一个数据
    username: '张三', //多个数据直接在后面跟就好了
    passworld: '123456'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})
分析查询
User.find(function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
}).limit(1)//表示输出第一行数据
user.getIndexes();

查询方法

mongoose查询使用最基础的方法就是find、findOne方法,前者查询所有满足条件的值,后者取满足条件的某一个值。

2、查询条件

mongoose查询条件其实就是在find方法的基础上添加mongodb条件操作符,如Thing.find().gt('age', 21)就等同于Thing.find({age: {$gt: 21}}),mongodb条件操作符如下:

    $or    或关系db.collection_name.find({$or: [{key1: value1}, {key2: value2}]})

  $nor    或关系取反 

  $gt    大于

  $gte    大于等于

  $lt     小于

  $lte     小于等于

  $ne            不等于

  $in             在多个值范围内

  $nin           不在多个值范围内

  $all            匹配数组中多个值

  $regex  正则,用于模糊查询

  $size   匹配数组大小

  $maxDistance  范围查询,距离(基于LBS)

  $mod     取模运算

  $near   邻域查询,查询附近的位置(基于LBS)

  $exists    字段是否存在

  $elemMatch  匹配内数组内的元素

  $within  范围查询(基于LBS)

  $box    范围查询,矩形范围(基于LBS)

  $center       范围醒询,圆形范围(基于LBS)

  $centerSphere  范围查询,球形范围(基于LBS)

  $slice    查询字段集合中的元素(比如从第几个之后,第N到第M个元素)

例如

tb_user.findOne({$or:[{user_name:req.body.user_name},{email:req.body.email}]},function(err,ret){
            if(err){
                console.log(err);
            }else{
                console.log(ret);
            }
        })

3、填充对象

查询对象时,对象中存在其他对象的引用,查询出来的引用对象默认是显示引用对象的id,如果需要引用对象的其他属性就需要使用populate方法填充引用对象。

如果对以上知识点不太了解可以参考:

查询实例

schema.js

var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

var UserSchema = new Schema({
    name  : { type: String, unique: true },
    posts : [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});
var User = mongoose.model('User', UserSchema);

var PostSchema = new Schema({
    poster   : { type: Schema.Types.ObjectId, ref: 'User' },
    comments : [{ type: Schema.Types.ObjectId, ref: 'Comment' }],
    title    : String,
    content  : String
});
var Post = mongoose.model('Post', PostSchema);

var CommentSchema = new Schema({
    post      : { type: Schema.Types.ObjectId, ref: "Post" },
    commenter : { type: Schema.Types.ObjectId, ref: 'User' },
    content   : {
        main: String,
        label: String
    },
    points: [
        point: [{type: Schema.Types.ObjectId, ref: 'Point'}]
    ]
});
var Comment = mongoose.model('Comment', CommentSchema);

var PointSchema = new mongoose.Schema({
  name: String,
  parent: {type: Schema.Types.ObjectId, ref: 'point'},
  children: [{type: Schema.Types.ObjectId, ref: 'point'}]
})
var Point = mongoose.model('Point', PointSchema);
1、深层属性查询

有些对象结构比较复杂,属性可能存在多层嵌套关系,有时需要通过对象属性下属的属性查询对象,如通过content的label的值查询Comment

Comment.find({'content.label': value}, function (err, comment) {
    console.log(comment)
})

2、二维数组查询

如果二维数组结构为[[]],这样的数组是可以查询,但是填充数组里对象时会有问题

Comment.find({'points': value}).populate('points').exec(function (err, comment) {
    console.log(comment) // 无法填充points
})

所以需要填充二维数组里的对象时,不能使用这种结构,而应该如schema.js中一样,将里面的数组先作为对象保存

Comment.find({'points': value}).populate('points.point').exec(function (err, comment) {
    console.log(comment) // 无法填充points
})

3、循环填充

结构如Point,读取point时,需要填充children,而childern的childern也需要填充,使用populate只能填充当前的childern,在schema.js添加:

PointSchema.pre('find', function(next) {
  this.populate('children')
  next()
})

这样每次查询时,自动为point填充childern

4、多表联合查询

mongoose其实没有多表联合查询的方法,不过我们可以通过多次查询来实现。
通过user的name、post的content查询post:

User.find({name: name}, function (err, users) {
    Post.find({poster: {$in: users}, content: content}, function (err, posts) {
        console.log(posts)
    })
})

有时我们也需要对取出来的数据进行再次过滤,而不是通过查询语句查询
通过user的name、post的content、comment的content.main查询post:

User.find({name: name}, function (err, users) {
    Post.find({poster: {$in: users}, content: content}).populate('commenter').exec(function (err, posts) {
        posts.filter(function(post) {
            return post.commenter.content.main === value
        })
    })
})

删除数据

根据条件删除所有

User.remove({
    username: '张三'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    }
    console.log('成功');
    console.log(ret);//所有的张三都删除了

})

根据条件删除一个

Model.findOneAndRemove(conditions,[options],[callback])

根据id删除一个

Model.findByIdAndRemove(id,[options],[callback])

删除的方法

Model.deleteOne(id,[options],[callback])

更新数据

根据条件更新所有:

//model就是数据表,你引入的表名
Model.update(conditions,doc,[options],[callback])

Model.updateOne(conditions,doc,[options],[callback])

根据指定条件更新一个:

Model.findOneAndUpdate([conditions],[update],[options],[callback])

根据id更新一个

// 第一个参数是他的MongoDB的id,第二个参数是你所想要修改的值,第三个是回调函数是否成功的
User.findByIdAndUpdate('5e0749619ca65b30b05a69ca', {
    passworld: '123'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log('成功');

    }
})

Mongodb 对内嵌数组的增删改查操作

先做一个初始化,设置一个User类,其初始数据如下:

{ arr: [ 1, 2 ],
  _id: 5ac5ee12a79131259413c40f,
  name: 'scy',
  __v: 0 }

每次以初始数据为基,进行操作。

1、向内嵌数组添加数据

使用操作符 $push,向数组末尾添加数据 ,可重复

//第一个参数是匹配条件 第二个参数是具体操作
    User.update({name:"scy"},{$push:{"arr":3}});//向user里面的arr末尾追加元素3

结果如下:

{ arr: [ 1, 2, 3 ],
  _id: 5ac5f0d3db343b1888a8969d, name: 'scy',__v: 0 }

一次添加多个数据

User.update({name:"scy"},{$push:{"arr":{$each:[2,3]}}});
router.get('/tijiao', function(req, res, next) {
    student1.find(function(err, ret) {
        if (err) {
            console.log('失败');
        } else {
            var now = new Date();
            var nowStr = now.toLocaleString('chinese', { hour12: false });
            req.query.data = nowStr;
            req.query.changdu = ret.length + 1
            new student1(req.query).save(function(err, ret) { //里面的两个方法用来查看是否将数据存进去
                if (err) {
                    console.log('失败');
                } else {
                    console.log(req.query);
                    console.log(ret._id);
//注意就是这里将上一个''引号可以转换
                    req.query.sid = ret._id + '';
                    student.update({ _id: req.query.id }, { $push: { 'shuzu': req.query } }, function(err, ret) {
                        if (err) {
                            console.log('失败');
                        } else {
                            console.log('成功');

                        }
                    })
                    res.status(200).json({
                        err_code: 0,
                        message: 'OK',
                        ret: ret,
                    })

                }
            })
        }
    })
})

2、删除内嵌数组指定数据

注意添加的时候加一个''引号这样可以将id转为字符串

使用操作符 $pull

//删除arr所有数据为2的元素
  User.update({name:"scy"},{$pull:{"arr":2}});

执行结果:

{ arr: [ 1 ], _id: 5ac5f39fdad94e23e8de9aee, name: 'scy', __v: 0 }

如果数组元素是对象,可以根据对象属性操作:

{
  name:"scy",
  mArray:[{age:13,weight:50},{age:13,weight:30}]
}
User.update({name:"scy"},{$pull:{"mArray":{"weight":30}}});//删除所有weight属性值为30的对象

MongoDB,从数组中删除对象

{
   _id: 5150a1199fac0e6910000002,
   name: 'some name,
   items: [{
      id: 23,
      name: 'item name 23'
   },{
      id: 24,
      name: 'item name 24'
   }]
}

删除代码
User.update(
    {'_id': ObjectId("5150a1199fac0e6910000002")},
    { $pull: { "items" : { id: 23 } } }
);

3、修改内嵌数组指定数据

  1. 数据截图:

    [图片上传失败...(image-e19f7e-1601889185798)]

  1. 我想更新arr_1数组中,a = 1 的对象,更新为 {a:11,b:12} 运行更新代码,如下:

    [
    复制代码

    ](javascript:void(0);)

    db.nestedUpdate.updateMany({
        'arr_1.a': 1
    }, {
        $set: {
            'arr_1.$.a': 11,
            'arr_1.$.b': 12,
        }
    })
    
    
    也可以:继续存 $set: {
                        "shuzu.$.sun8": [{
                            sun9: req.query.xuehao,
                            sun10: req.query.xuehao,
                            sun11: req.query.xuehao,
                        }, {
                            sun9: req.query.xuehao,
                            sun10: req.query.xuehao,
                            sun11: req.query.xuehao,
                            sun12: '7',
                        }]
                    }
    

    [
    复制代码

    ](javascript:void(0);)

我暂时还没找到能批量修改数组元素的方法

1
3、修改内嵌数组指定数据
我暂时还没找到能批量修改数组元素的方法

//将数组里面的第一个元素1修改为3
User.update({"arr":{$all:[1]}},{$set:{"arr.$":2}});
//也可以根据下标
User.update({$set:{"arr.1":22}});//将arr下标为1的元素修改为22

如果数组的元素是对象,如下:

1
2
如果数组的元素是对象,如下:

{
  name:"scy",
  mArray:[{age:13,weight:50},{age:13,weight:30}]
}

修改操作如下:

User.update({"mArray.age":13},{$set:{"mArray.$.age":22}});//将第一个age为13的值修改为22
//还可以这样 mArray.1.age 其中1是下标 
User.update({$set:{"mArray.1.age":22}});//将arr第二个元素对象的age改为22

批量修改内嵌数组对象

//computer1数据表
//data.$[].network数组下面的数据
computer1.update({'roomNumber': req.query.w}, {$set: {'data.$[].network': 10}}, {multi: true},function(a,s){
            console.log(a);
            console.log(s);
            
        })

4、查询内嵌数组并返回指定的数据

使用$size 返回指定数组长度的数据

//$size限制比较大 下面表示查询数组长度为2的数据
User.find({arr:{$size:2}})

$slice,这个操作符还是比较强大的适合查询数组中的数据

//匹配到的user 将其数组截取第一个返回 如[1,1,2]返回[1]
User.findOne({name:"scy"},{arr:{$slice:1}});
//将匹配到的user的数组 截取返回后面两个元素  如[1,1,2]返回[1,2]
User.findOne({name:"scy"},{arr:{$slice:-2}});
//从数组的下表为1的元素开始 返回两个 如[1,3,2]返回[3,2]
User.findOne({name:"scy"},{arr:{$slice:[1,2]}});

$elemMatch查询的是某一个数据

用法如下:

User.findOne({name:"scy"},{arr:{$elemMatch:{key:val}});

总的代码案例如下:

在当前的目录下的demo.js文件下

导出导入数据库

首先要开启数据库连接通过mongod --dbpath 数据存储目录路径 我的数据库目录地址好像在D:\mongoDB\two

然后在命令行中输入

导出:

//-h表示主机  -d表示要导出的数据库  -o输出的目录
mongodump -h (主机127.0.0.1) -d (到处的数据库)  -o (地址)  
//例如
mongodump -h 127.0.0.1 -d  user  -o   C:\data

导入:

mongorestore -h (主机地址) -d (导入数据库名)  导入的地址

第二种

8.MongoDB数据库

8.1 关系型数据库的非关系型数据库

表就是关系

或者说表于表之间存在关系。

  • 所有的关系型数据库都需要通过sql语言来操作
  • 所有的关系型数据库都需要设计表结构
  • 而且数据表还支持约束
    • 唯一的
    • 主键
    • 默认值
    • 非空
  • 非关系型数据库可以说非常的灵活
  • 有的非关系型数据库就是key-value队
  • 在mingoDB是长得最像关系型数据库的非关系型数据库
    • 数据库-》数据库
    • 数据表-》集合(数组)
    • 表记录-》(文档对象)
  • MongoDB不需要设计表结构
  • 也就是说你可以任意的往里面存数据,没有结构性这么一说

8.2安装

8.3启动和关闭数据库

启动:

# mongodb 默认使用执行 mongdb 命令所处盘符根目录下的/data/db 作为自己的数据存储目录
# 启动的时候出现问题下面这张图的问题
#需要在第一次执行该命令之前先手动见一个 [c,d,盘]/data/db
在cmd中输入mongodb

[图片上传失败...(image-421fc9-1601889185798)]

如果想要修改默认数据存储目录,可以:

mongod --dbpath=数据存储目录路径

停止:

在开启服务的控制台,直接Ctrl+c即可停止
或者直接关闭开启服务的控制台也可以。

8.4连接数据库

#该命令默认连接本机的MongoDB服务
mongo

退出连接:

#在连接状态输入exit退出连接
exit

8.5基本命令

  • show dbs(查看显示所有的数据库)
  • db (查看当前操作的数据库)
  • use 数据库名称 (切换到指定数据【如果没有会新建】)
  • 插入数据

8.6在Node中如何操作MongoDB数据

8.6.1使用官方的mongodb包来操作

https://github.com/mongodb/node-mongodb-native#installation

8.6.2使用第三方的mongodb来操作Mongodb数据库

第三方包:Mongodb基于Mongodb官方的mongodb包再一次做了封装

https://mongoosejs.com/

第三方包连接数据库

安装:

npm i mongoose
#注意先下载npm i -y (如果有就不需要下载了)然后下载mongoose第三方的包
const mongoose = require('mongoose'); //引入mongoose包
//连接MongoDB数据库
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
// 这句先不了解
mongoose.Promise = global.Promise;
//创建一个模型
// 就是在设计数据库
// MongoDB是动态的,非常灵活,只需要在代码中设计你的数据库就可以了
// mongodb这个包就可以让你的设计编写过程变的非常简单
const Cat = mongoose.model('Cat', { name: String });
// 实例化一个Kitty
const kitty = new Cat({ name: 'Zildjian' });
// 持久化保存kitty实例
kitty.save().then(() => console.log('meow'));

持久化存储for:

const mongoose = require('mongoose'); //引入mongoose包
// //连接MongoDB数据库
mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
// // 这句先不了解
mongoose.Promise = global.Promise;
var Cat = mongoose.model('Cat', { name: String });
for (var i = 0; i < 50; i++) {
    var kitty = new Cat({ name: '喵喵' + i });
    kitty.save(function(err) {
        if (err) {
            console.log(err);
        }
        console.log('meow');

    })
}

1.MongoDB数据库的基本概念

  • 数据库
  • 一个数据库中有多个集合(表)
  • 一个集合中可以有多个文档(表记录)
  • 文档结构灵活,没有任何限制
  • MongoDB非常灵活,不像MySQL一样要先创建数据库,表,设计表结构
    • 在这里:当你需要插入数据的时候,只需要指定往那个数据库那个集合操作就可以
    • 一切都由MongoDB来帮你自动完成建库建表这件事
{
    qq:{
        //users:[{'这里存的是每一条记录'对象}],//一个对象教文档,要有一些约定,要不然乱
        users:[
            {},
            {}
        ]
        products:[
            {name:'张三',age:15},
            {name:'李四',age:16},
            {name:'王五',age:17},
            {name:'张三123',age:18},
            ...
        ]
    },
    taobao:{
        
    },
    baidu:{
        
    }
}

mongoDB的案例构建(案例案例)

设计Scheme发布Model

这些增删改查的方法都是根据引入的包是var mongoose = require('mongoose');方法进行的

与菜鸟联盟的方法不同的原因是引入的包不同

var mongoose = require('mongoose');
// 获取他的结构
var Schema = mongoose.Schema;
// 指定连接的数据库不需要存在,当你插入第一条数据之后就会自动被创建出来
// 1.连接数据库
mongoose.connect('mongodb://localhost:27017/itcast', { useNewUrlParser: true });
// 2.设计集合结构
// 字段名称就是表结构中的属性名称
// 值
// 约束的目的是为了保证数据的完整性,不要有脏数据
// 架构就是设置他的类型,必须要有的数据
var userSchema = new Schema({
        username: {
            type: String,
            required: true //必须有不能为空
        },
        passworld: {
            type: String,
            required: true
        },
        email: {
            type: String,
            default:写什么都可以//default表示默认的
            enum:[0,1]//这个表示可选的必须在这两个中选择一个
        }
    })
    // 3.将文档结构发布为模型
    // mongoose.model()方法就是用来将一个架构发布为model
    // 第一个参数:传入一个一个大写名词单数字符串用来表示你的数据库名称
    //             mongoose会自动将大写名词的字符串生成小写复数的集合名称
    //              例如这里的User最终会变为users集合名称
    //              第二个参数:模型构造函数
var User = mongoose.model('User', userSchema); //第一个参数是字符串,第二个参数是一个架构
// 4.当我们有了模型构造韩素华之后,就可以使用这个构造函数对users集合中的数据进行(增删改查)

增加数据

var admin = new User({
    username: 'admin',
    passworld: '123456',
    email: 'admin@admin.com'
});
admin.save(function(err, ret) {//里面的两个方法用来查看是否将数据存进去
    if (err) {
        console.log('失败');
    } else {
        console.log('成功');
        console.log(ret);
    }
})

一次性写法:

    new student(req.query).save(function(err, user) {
        if (err) {
            return next(err)
        }
        res.status(200).json({
            err_code: 0,
            message: 'OK',
            user: user
        })
    })

查询数据

​ 查询所有数据:

// ***********************
// #region 查询数据所有数据
// ***********************
User.find(function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})

按照条件查询数据(查询出来的是数组里面存放的是对象):

User.find({
    username: '张三'
    //年龄大于18去API查
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})

按照条件查询数据(查询出来的是数据直接存放在对象中()):

User.findOne({ //如果第一个没有参数,那么查询的就是第一个数据
    username: '张三', //多个数据直接在后面跟就好了
    passworld: '123456'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
})
分析查询
User.find(function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log(ret);
    }
}).limit(1)//表示输出第一行数据
user.getIndexes();

删除数据

根据条件删除所有

User.remove({
    username: '张三'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    }
    console.log('成功');
    console.log(ret);//所有的张三都删除了

})

根据条件删除一个

Model.findOneAndRemove(conditions,[options],[callback])

根据id删除一个

Model.findByIdAndRemove(id,[options],[callback])

更新数据

根据条件更新所有:

Model.update(conditions,doc,[options],[callback])

根据指定条件更新一个:

Model.findOneAndUpdate([conditions],[update],[options],[callback])

根据id更新一个

// 第一个参数是他的MongoDB的id,第二个参数是你所想要修改的值,第三个是回调函数是否成功的
User.findByIdAndUpdate('5e0749619ca65b30b05a69ca', {
    passworld: '123'
}, function(err, ret) {
    if (err) {
        console.log('失败');
    } else {
        console.log('成功');

    }
})

Mongodb 对内嵌数组的增删改查操作

先做一个初始化,设置一个User类,其初始数据如下:

{ arr: [ 1, 2 ],
  _id: 5ac5ee12a79131259413c40f,
  name: 'scy',
  __v: 0 }

每次以初始数据为基,进行操作。

1、向内嵌数组添加数据

使用操作符 $push,向数组末尾添加数据 ,可重复

//第一个参数是匹配条件 第二个参数是具体操作
    User.update({name:"scy"},{$push:{"arr":3}});//向user里面的arr末尾追加元素3

结果如下:

{ arr: [ 1, 2, 3 ],
  _id: 5ac5f0d3db343b1888a8969d, name: 'scy',__v: 0 }

一次添加多个数据

User.update({name:"scy"},{$push:{"arr":{$each:[2,3]}}});
router.get('/tijiao', function(req, res, next) {
    student1.find(function(err, ret) {
        if (err) {
            console.log('失败');
        } else {
            var now = new Date();
            var nowStr = now.toLocaleString('chinese', { hour12: false });
            req.query.data = nowStr;
            req.query.changdu = ret.length + 1
            new student1(req.query).save(function(err, ret) { //里面的两个方法用来查看是否将数据存进去
                if (err) {
                    console.log('失败');
                } else {
                    console.log(req.query);
                    console.log(ret._id);
//注意就是这里将上一个''引号可以转换
                    req.query.sid = ret._id + '';
                    student.update({ _id: req.query.id }, { $push: { 'shuzu': req.query } }, function(err, ret) {
                        if (err) {
                            console.log('失败');
                        } else {
                            console.log('成功');

                        }
                    })
                    res.status(200).json({
                        err_code: 0,
                        message: 'OK',
                        ret: ret,
                    })

                }
            })
        }
    })
})

2、删除内嵌数组指定数据

注意添加的时候加一个''引号这样可以将id转为字符串

使用操作符 $pull

//删除arr所有数据为2的元素
  User.update({name:"scy"},{$pull:{"arr":2}});

执行结果:

{ arr: [ 1 ], _id: 5ac5f39fdad94e23e8de9aee, name: 'scy', __v: 0 }

如果数组元素是对象,可以根据对象属性操作:

{
  name:"scy",
  mArray:[{age:13,weight:50},{age:13,weight:30}]
}
User.update({name:"scy"},{$pull:{"mArray":{"weight":30}}});//删除所有weight属性值为30的对象

MongoDB,从数组中删除对象

{
   _id: 5150a1199fac0e6910000002,
   name: 'some name,
   items: [{
      id: 23,
      name: 'item name 23'
   },{
      id: 24,
      name: 'item name 24'
   }]
}

删除代码
User.update(
    {'_id': ObjectId("5150a1199fac0e6910000002")},
    { $pull: { "items" : { id: 23 } } }
);

3、修改内嵌数组指定数据

  1. 数据截图:

    [图片上传失败...(image-614047-1601889185798)]

  1. 我想更新arr_1数组中,a = 1 的对象,更新为 {a:11,b:12} 运行更新代码,如下:

    [
    复制代码

    ](javascript:void(0);)

    db.nestedUpdate.updateMany({
        'arr_1.a': 1
    }, {
        $set: {
            'arr_1.$.a': 11,
            'arr_1.$.b': 12,
        }
    })
    
    
    也可以:继续存 $set: {
                        "shuzu.$.sun8": [{
                            sun9: req.query.xuehao,
                            sun10: req.query.xuehao,
                            sun11: req.query.xuehao,
                        }, {
                            sun9: req.query.xuehao,
                            sun10: req.query.xuehao,
                            sun11: req.query.xuehao,
                            sun12: '7',
                        }]
                    }
    

    [
    复制代码

    ](javascript:void(0);)

我暂时还没找到能批量修改数组元素的方法

1
3、修改内嵌数组指定数据
我暂时还没找到能批量修改数组元素的方法

//将数组里面的第一个元素1修改为3
User.update({"arr":{$all:[1]}},{$set:{"arr.$":2}});
//也可以根据下标
User.update({$set:{"arr.1":22}});//将arr下标为1的元素修改为22

如果数组的元素是对象,如下:

1
2
如果数组的元素是对象,如下:

{
  name:"scy",
  mArray:[{age:13,weight:50},{age:13,weight:30}]
}

修改操作如下:

User.update({"mArray.age":13},{$set:{"mArray.$.age":22}});//将第一个age为13的值修改为22
//还可以这样 mArray.1.age 其中1是下标 
User.update({$set:{"mArray.1.age":22}});//将arr第二个元素对象的age改为22

批量修改内嵌数组对象

//computer1数据表
//data.$[].network数组下面的数据
computer1.update({'roomNumber': req.query.w}, {$set: {'data.$[].network': 10}}, {multi: true},function(a,s){
            console.log(a);
            console.log(s);
            
        })

4、查询内嵌数组并返回指定的数据

使用$size 返回指定数组长度的数据

//$size限制比较大 下面表示查询数组长度为2的数据
User.find({arr:{$size:2}})

$slice,这个操作符还是比较强大的适合查询数组中的数据

//匹配到的user 将其数组截取第一个返回 如[1,1,2]返回[1]
User.findOne({name:"scy"},{arr:{$slice:1}});
//将匹配到的user的数组 截取返回后面两个元素  如[1,1,2]返回[1,2]
User.findOne({name:"scy"},{arr:{$slice:-2}});
//从数组的下表为1的元素开始 返回两个 如[1,3,2]返回[3,2]
User.findOne({name:"scy"},{arr:{$slice:[1,2]}});

$elemMatch查询的是某一个数据

用法如下:

User.findOne({name:"scy"},{arr:{$elemMatch:{key:val}});

总的代码案例如下:

在当前的目录下的demo.js文件下

嵌套管道查询

const SurveySchema = new Schema({
_id:{ type: Schema.ObjectId, auto: true },
name: String,
enabled: {type: Boolean, Default: true},
created_date:{type: Date, Default: Date.now},
company: {type: Schema.Types.ObjectId, ref: 'Company'},});
const GroupSchema = new Schema({
  _id:{ type: Schema.ObjectId, auto: true },
  name: String,
  order: String,
  created_date:{type: Date, Default: Date.now},
  questions: [{type: Schema.Types.ObjectId, ref: 'Question'}],
  survey: {type: Schema.Types.ObjectId, ref: 'Survey'}
});
const ResponseSchema = new Schema({
  _id:{ type: Schema.ObjectId, auto: true },
  response_text: String,
  order: String,
  created_date:{type: Date, Default: Date.now},
  question:{type: Schema.Types.ObjectId, ref: 'Question'}
});
Survey.aggregate([
  { $match: {} },
  { $lookup: {
    from: 'groups',
    localField: '_id',
    foreignField: 'survey',
    as: 'groupsofquestions',
  }},
  { $unwind: {
    path: "$groupsofquestions",
    preserveNullAndEmptyArrays: true
  }},
  { $lookup: {
    from: 'questions',
    localField: 'groupsofquestions._id',
    foreignField: 'group',
    as: 'questionsofgroup',
  }},
  { $lookup: {
    from: 'response',
    localField: 'questionsofgroup._id',
    foreignField: 'question',
    as: 'responses',
  }},
  { $group: {
    _id: "$_id",
    name: {$first: "$name"},
    groups: {$push: {
      id: "$groupsofquestions._id",
      name: "$groupsofquestions.name",
      questions: "$questionsofgroup",
      reponses: "$responses"
    }}
  }}
])

aggregate()

语 法 \color{red}{语法}语法

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION);
1

数 据 \color{red}{数据}数据

数据使用菜鸟教程的数据啦🤪🤪

/* 1 */
{
    "_id" : ObjectId("5e86e29788e64443e448dfc0"),
    "title" : "MongoDB Overview",
    "description" : "MongoDB is no sql database",
    "by_user" : "runoob.com",
    "url" : "http://www.runoob.com",
    "tags" : [ 
        "mongodb", 
        "database", 
        "NoSQL"
    ],
    "likes" : 100
}

/* 2 */
{
    "_id" : ObjectId("5e86e2ad88e64443e448dfd2"),
    "title" : "NoSQL Overview",
    "description" : "No sql database is very fast",
    "by_user" : "runoob.com",
    "url" : "http://www.runoob.com",
    "tags" : [ 
        "mongodb", 
        "database", 
        "NoSQL"
    ],
    "likes" : 10
}

/* 3 */
{
    "_id" : ObjectId("5e86e2bc88e64443e448dfd7"),
    "title" : "Neo4j Overview",
    "description" : "Neo4j is no sql database",
    "by_user" : "Neo4j",
    "url" : "http://www.neo4j.com",
    "tags" : [ 
        "neo4j", 
        "database", 
        "NoSQL"
    ],
    "likes" : 750
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344

管 道 操 作 符 \color{red}{管道操作符}管道操作符

操作符 含义
$group 将collection中的document分组,可用于统计结果
$match 过滤数据,只输出符合结果的文档
$project 修改输入文档的结构(例如重命名,增加、删除字段,创建结算结果等)
$sort 将结果进行排序后输出
$limit 限制管道输出的结果个数
$skip 跳过制定数量的结果,并且返回剩下的结果
$unwind 将数组类型的字段进行拆分

表 达 式 操 作 符 \color{red}{表达式操作符}表达式操作符

操作符 含义 实例
$sum 计算总和,{$sum: 1}表示返回总和×1的值(即总和的数量),使用{$sum: '$制定字段'}也能直接获取制定字段的值的总和 db.collection.aggregate([{$group : {_id : "$by_user", content_sum : {$sum : "$likes"}}}])
$avg 平均值 db.collection.aggregate([{$group : {_id : "$by_user", content_sum : {$avg : "$likes"}}}])
$min 获取集合中所有文档对应值得最小值 db.collection.aggregate([{$group : {_id : "$by_user", content_sum : {$min : "$likes"}}}])
$max 获取集合中所有文档对应值得最大值 db.collection.aggregate([{$group : {_id : "$by_user", content_sum : {$max : "$likes"}}}])
$push 在结果文档中插入值到一个数组中 db.collection.aggregate([{$group : {_id : "$by_user", url : {$push : "$url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本 db.collection.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根据资源文档的排序获取第一个文档数据 db.collection.aggregate([{$group : {_id : "$by_user", url : {$first : "$url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.collection.aggregate([{$group : {_id : "$by_user", url : {$last : "$url"}}}])

具 体 例 子 \color{skyblue}{具体例子}具体例子

  • $group

    • 简单阐述

      //将document分组,用作统计结果
      db.collection.aggregate([       // aggregate方法接收的是一个数组
          {
              $group: {
                  // _id字段表示要基于哪个字段来进行分组(即制定字段值相同的为一组)
                  // $by_user表示要基于$by_user字段来进行分组
                  _id: '$by_user', 
                  // content_sum字段的值$sum: 1表示的是获取满足by_user字段相同的这一组的数量乘以后面给定的值(本例为1,那么就是同组的数量)。
                  content_sum: {$sum: 1}
              }
          }
      ])
      123456789101112
      
    • 具体案例

      通过以上集合计算每个作者所写的文章数(通过字段by_user 字段对数据进行分组,并计算by_user字段相同值的总和),使用aggregate()计算结果如下:

      router.get('/getInfo',async(req, res)=>{
      let data=await Content.aggregate([
        {
          $group:{
            _id:'$by_user',
            content_sum:{$sum:1}
          }
        }
      ])
      res.json({data})
      })
      1234567891011
      
      在这里插入图片描述
  • $match

    获取likes的值在50-200之间的数据:

    router.get('/getInfo', async (req, res) => {
      let data = await Content.aggregate([{
          $match: {
            likes: {
              $gt: 50,
              $lte: 200
            }
          }
        },
        {
          $group: {
            _id: '$_id',
            content_sum: {
              $sum: 1
            }
          }
        }
      ])
      res.json({
        data
      })
    })
    12345678910111213141516171819202122
    
    在这里插入图片描述

    从图中可以看出likes的值在50-200之间的数据只有1条,现在我们只知道这条数据的_id,如果想知道这条数据的具体信息时应该如何操作呢❓上面的表格中提到$project修改输入文档的结构(例如重命名,增加、删除字段,创建结算结果等),所以一起来看看吧👇👇👇

  • $project

    router.get('/getInfo', async (req, res) => {
      let data = await Content.aggregate([
        {
          $match: { likes: { $gt: 50, $lte: 200 } }
        },
        {
        //以下的值可以写$+字段,也可以使用0 和1来表示,若要显示字段则为1,否则为0
         
         //$project:{_id:'$_id',title:"$title",description:"$description",by_user:"$by_user",url:'$ulr',tags:'$tags',likes:'$likes'}
         $project:{_id:1,title:1,description:1,by_user:1,url:1,tags:1,likes:1}
        }
      ])
      res.json({
        data
      })
    })
    12345678910111213141516
    
    在这里插入图片描述
  • 以上3个操作符的综合使用

    如果想拿到所有likes>=10的document的by_user字段可以把管道搭配起来用:

    router.get('/getInfo', async (req, res) => {
      let data = await Content.aggregate([{
          $match: {
            likes: {
              $gt: 10
            }
          }
        },
        // 注意$project与$group的顺序,换位置后数据为空
        {
          $project: {
            _id: 0, //_id不显示
            by_user: 1 //by_user显示
          }
        },
        {
          $group: {
            _id: null,
            gameName: {
              $push: '$by_user'
            }
          }
        }
      ])
      res.json({
        data
      })
    })
    12345678910111213141516171819202122232425262728
    
    在这里插入图片描述
  • $sort

    • 根据likes进行降序排序

      router.get('/getInfo', async (req, res) => {
        let data = await Content.aggregate([
          {
            $project: { _id: 1, by_user: 1, title: 1, title: 1, description: 1, url: 1, tags: 1, likes: 1 }
          },
          {
            $sort: { likes: -1 }
          },
        ])
        res.json({
          data
        })
      })
      12345678910111213
      
      在这里插入图片描述
    • 根据likes进行升序排序

      router.get('/getInfo', async (req, res) => {
        let data = await Content.aggregate([
          {
            $project: { _id: 1, by_user: 1, title: 1, title: 1, description: 1, url: 1, tags: 1, likes: 1 }
          },
          {
            $sort: { likes: 1 }
          },
        ])
        res.json({
          data
        })
      })
      12345678910111213
      
      在这里插入图片描述
  • limit andskip

    router.get('/getInfo', async (req, res) => {
      let data = await Content.aggregate([
        {
          $project: { _id: 1, by_user: 1, title: 1, title: 1, description: 1, url: 1, tags: 1, likes: 1 }
        },
        {
          $sort: { likes: 1 }
        },
        {
          $skip:1
        },
        {
          $limit:1
        }
      ]);
      
      res.json({
        data
      })
    })
    1234567891011121314151617181920
    
    在这里插入图片描述
  • $unwind

    $unwind管道以document中的数组类型的字段进行拆分,每条包含数组中的一个值。

    比如拆分likes:10这条数据,先来看看整体数据信息吧:

    {
        "_id" : ObjectId("5e86e2ad88e64443e448dfd2"),
        "title" : "NoSQL Overview",
        "description" : "No sql database is very fast",
        "by_user" : "runoob.com",
        "url" : "http://www.runoob.com",
        "tags" : [ 
            "mongodb", 
            "database", 
            "NoSQL"
        ],
        "likes" : 10
    }
    12345678910111213
    

    tags数组中有3条数据,所以拆分后会显示3条数据,看看具体实现吧:

    router.get('/getInfo', async (req, res) => {
      let data = await Content.aggregate([
        {
          $match: {
            likes: 10
          }
        },
        {
          $unwind:'$tags'
        },
        {
          $project: { _id: 1, by_user: 1, title: 1, title: 1, description: 1, url: 1, tags: 1, likes: 1 }
        },
      ])
      res.json({
        data
      })
    })
    123456789101112131415161718
    
    在这里插入图片描述
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352