MongoDB总结

70 阅读6分钟

MongoDB重点

比较运算符

比较运算符有以下几个:

比较运算符含义简称
$gtgreater than的缩写,大于。
$gtegreater than equal的缩写,大于等于。
$ltless than的缩写,小于。
$lteless than equal的缩写,小于等于。
$eqequal的缩写,等于。
$nenot equal的缩写,不等于。
$inin的缩写,筛选出的字段在指定数组中。
$ninot in的缩写,筛选出的字段不在指定数组中。

逻辑运算符

逻辑运算符含义
$and用于连接多个查询条件,且。
$or用于连接多个查询条件,或。
$nor用于连接多个查询条件,都不。
$not只能用于一个查询条件,表示查询的文档不符合该条件。

正则表达式

直接看例子:

db.product.find({
   name:{
      $regex: '运动'
   }
});

索引

单字段索引

db.ykd_product.createIndex({crossedPrice: 1});

创建了单字段索引,里面1代表正序,-1代表负序。

复合索引

db.ykd_product.createIndex({name: -1,crossedPrice: 1});

这个查询会先根据name进行降序查询,如果数值相同会按照crossedPrice进行升序查询。

如果name为字符串类型,则会根据字典顺序查询,即a,b,c,d...查询。

全文本索引查询

db.ykd_product.createIndex(
{"description": "text","name":"text"},
{"weights": { "description": 1, "name": 2 }
}
);
db.ykd_product.find({
 $text:{
 $search:'夏季气质碎花裙子夏装'
 }
});

设置权值是可以提高查询效率,因为name名称更方便查询,权值越大,查询越靠前。

这个可不是模糊查询,不要跟正则表达式搞混了。

查询诊断优化

我们可以在查询语句后面跟上explain()关键字。里面也可以添加表达语句,如:executionStats。

db.ykd_bank_accounts.find(
    {
        age: { $lt: 45 }
    }
).explain('executionStats')

结果如下:

{
    "queryPlanner": {
        "namespace": "draftdb.ykd_bank_accounts",//代表的是该查询所扫描的表
        "indexFilterSet": false,//表示MongoDB在查询中是否使用索引过滤

    "executionStats": {
        "executionSuccess": true,
        "nReturned": 6330,//返回当前查询的文档数量
        "executionTimeMillis": 39,//整体执行的时间
        "totalKeysExamined": 6330,//索引扫描的次数
        "totalDocsExamined": 6330,//document扫描次数
        "executionStages": {
            "stage": "FETCH",
            ------------------
            "nReturned": 6330,//返回当前查询的文档数量
             "indexName": "age_1",//使用的索引的名字,
              "inputStage": {
                "stage": "IXSCAN",//stage的子 stage,此处是IXSCAN,表示进行的是索引扫描
                -------------------
                }
            }
        }
    },

    "ok": 1
}

里面的划线字段最重要,stage的值为fetch则意味着采用了索引查询。

如果我们删除了mongoDB里面的数据但是硬盘里面的空间占用是不会消失的,这主要是因为mongoDB的写时复制和垃圾收集策略。主要是为了避免了碎片化处理和i/o操作。从而提高了效率。

如果想删除硬盘空间则就需要db.runCommand({closeAllDatabases:1})操作来删除。

地理空间索引

1.创建集合,插入数据。

首先我们插入集合数据,如下:

db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d53e6b9e7a03e87c60d2"),"name":"杭州东站","address":"杭州","location":{"type":"Point","coordinates":[120.212599,30.290846]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d57a6b9e7a03e87c60d3"),"name":"西广场","address":"杭州","location":{"type":"Point","coordinates":[120.210411,30.289484]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d5916b9e7a03e87c60d4"),"name":"派出所","address":"杭州","location":{"type":"Point","coordinates":[120.212331,30.288854]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d5a86b9e7a03e87c60d5"),"name":"锦江都城酒店","address":"杭州","location":{"type":"Point","coordinates":[120.217932,30.290124]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d5c06b9e7a03e87c60d6"),"name":"新凤尚街","address":"杭州","location":{"type":"Point","coordinates":[120.209794,30.291439]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d5e76b9e7a03e87c60d7"),"name":"东赞球馆","address":"杭州","location":{"type":"Point","coordinates":[120.216885,30.291624]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d6036b9e7a03e87c60d8"),"name":"职工文化中心","address":"杭州","location":{"type":"Point","coordinates":[120.21886,30.287669]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d62b6b9e7a03e87c60d9"),"name":"维也纳国际酒店","address":"杭州","location":{"type":"Point","coordinates":[120.21739,30.290682]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d6446b9e7a03e87c60da"),"name":"中豪国际大酒店","address":"杭州","location":{"type":"Point","coordinates":[120.223076,30.289848]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d65f6b9e7a03e87c60db"),"name":"东汇大厦","address":"杭州","location":{"type":"Point","coordinates":[120.22048,30.293165]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d67e6b9e7a03e87c60dc"),"name":"新国利花园酒店","address":"杭州","location":{"type":"Point","coordinates":[120.219879,30.284697]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d69a6b9e7a03e87c60dd"),"name":"钱威商务楼","address":"杭州","location":{"type":"Point","coordinates":[120.215222,30.281306]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d6c56b9e7a03e87c60de"),"name":"红街天城","address":"杭州","location":{"type":"Point","coordinates":[120.20812,30.292275]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d6dd6b9e7a03e87c60df"),"name":"迈达商业中心","address":"杭州","location":{"type":"Point","coordinates":[120.206897,30.2897]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d6ef6b9e7a03e87c60e0"),"name":"如家酒店","address":"杭州","location":{"type":"Point","coordinates":[120.203077,30.286865]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d7446b9e7a03e87c60e1"),"name":"东恒大厦","address":"杭州","location":{"type":"Point","coordinates":[120.203077,30.286865]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d75f6b9e7a03e87c60e2"),"name":"太平洋银座","address":"杭州","location":{"type":"Point","coordinates":[120.211875,30.296499]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d7756b9e7a03e87c60e3"),"name":"十足","address":"杭州","location":{"type":"Point","coordinates":[120.214697,30.294869]}});  
db.ykd_gd_map.insertOne({"_id":ObjectId("5ec8d7926b9e7a03e87c60e4"),"name":"中国移动","address":"杭州","location":{"type":"Point","coordinates":[120.206811,30.292433]}});

里面的location内嵌数据其实是GeoJson对象,如下:

location: {
      type: "Point",//对象类型
      coordinates: [120.212599, 30.290846]//经纬度
}

2.创建地理空间索引

db.表名.createIndex({location:"2dsphere"})

3.查找目标之间距离或者范围之内的其他目标。

db.ykd_gd_map.find(
   {
     location:
       { $near:
          {
            $geometry: { type: "Point",  coordinates: [ 120.212599, 30.290846 ]//建筑物坐标 },
            $minDistance: 100, //最近距离
            $maxDistance: 500000 //最远距离
          }
       }
   }
)

上面是指查询指定经纬度目标的周围100——500000米内的其他目标。

db.ykd_gd_map.aggregate(
[
   {
      $geoNear: {
         near: { type: "Point", coordinates: [ 120.212599, 30.290846 ] },
         spherical: true,
         query: { name: "东恒大厦" },
         distanceField: "dist.location"//这个参数可以计算两者之间的距离
      }
   }
]

)

上面是指查询指定的建筑物目标和东恒大厦之间的距离。

数据聚合

count函数:

db.ykd_product.count(); //查找集合的所有数据条数

里面也可以写查询条件,例如:

db.ykd_action_data.count({
  date: ISODate('2020-05-08T00:00:00.000+08:00'),
  process: { $gte: 100 }
});
db.ykd_action_data
  .find({
    date: ISODate('2020-05-08T00:00:00.000+08:00'),
    process: { $gte: 100 }
  })
  .count();

这个同上面效果是一样的。

distinct函数

这个函数执行的操作就是先查询符合我们查询条件的数据然后再进行去重操作。

db.ykd_accounts.distinct('info.college', {
  'info.project': '电子与通信工程'
});

大致如这个:db.ykd_accounts.distinct(<field>,<query>)

聚合管道

聚合管道的标准写法就是db.表名.aggregate.([])。例如我们可以来个分页查询:

第一页数据
db.ykd_action_data.aggregate([{ $skip: 0 }, { $limit: 5 }]);
第二页数据
db.ykd_action_data.aggregate([{ $skip: 5 }, { $limit: 5 }]);
第三页数据
db.ykd_action_data.aggregate([{ $skip: 10 }, { $limit: 5 }]);

里面的skip操作符是跳过当前number个数,而limit则是限制返回数据个数。

$project

它可以改变原来的文档数据,然后处理成自己想要的文档数据。

举例:

db.ykd_accounts.aggregate([{ $project: { name: 1, _id: 0 } }]);

每当我们调用$project的时候都会返回_id的数据,因此不想要的话必须将其隐藏。这样一来,上面只会得到name的文档数据。

另外,$project操作符还可以改变键的名称,举例如下:

db.ykd_accounts.aggregate([{ $project: { studentName: '$name', _id: 0 } }]);//主键会默认显示,因此我们屏蔽主键即可。

我们还可以在里面添加运算操作符,如下:

db.ykd_action_data.aggregate([
  {
    $project: {
      studyTime: { $subtract: ['$endTime', '$startTime'] },
      _id: 0,
      accountId: 1
    }
  }
]);

$match

它的作用和之前的find查询作用相当,主要是为了连同其他的聚合操作符一同使用。

db.ykd_action_data.aggregate([
  {
    $match: { date: ISODate('2020-05-08T00:00:00+08:00') }
  },
  {
    $project: {
      studyTime: { $subtract: ['$endTime', '$startTime'] },
      _id: 0,
      accountId: 1,
      date: 1
    }
  }
]);

$group

根据他的中文意思就可以看出是分组的用意,具体的使用是跟每组的_id联合使用,看代码:

db.ykd_product.aggregate([
  {
    $group: {
      _id: '$productType',
      mostExpensivePrice: { $max: '$currentPrice' }
    }
  }
]); //这个是同$max操作符联合使用,用意是获得每组的当前价格最大值。

当然了,除了和max操作符联合使用,还可以同max操作符联合使用,还可以同push操作符。

db.ykd_product.aggregate([
  {
    $group: {
      _id: '$productType',
      names: { $push: '$name' }
    }
  }
]);//将每组产品类型中不同的名称放在一个数组中。
group的_id使用拼接字符串
db.ykd_product.aggregate([
  { $sort: { currentPrice: 1 } },
  {
    $group: {
      _id: { $concat: ['$productType', 'TEST'] },
      cheapest: { $first: '$currentPrice' }
    }
  }
]);
group的_id使用多个属性
db.ykd_action_data.aggregate([
  {
    $group: {
      _id: { date: '$date', accountId: '$accountId' },
      process: { $push: '$process' }
    }
  }
]);
group的_id为null
db.ykd_product.aggregate([
  {
    $group: {
      _id: null,
      amount: { $sum: 1 }
    }
  }
]);为null不就相当于没有分组,也就是获得所有数量,相当于count()

上面的sum操作符还可以指定表达式。

db.ykd_product.aggregate([
  { $group: { _id: '$productType', total: { $sum: '$currentPrice' } } }
]);

//大家可以尝试把 $sum 换成 $avg 表示求当前分类价格的平均值(记得将 total 字段名也换掉哦,只要是表示平均值就行了)

$count的使用

这个就更简单了,直接看例子,两个效果是一样的。

db.ykd_product.aggregate([
  {
    $match: { currentPrice: { $gt: 200 } }
  },
  { $count: 'number' }
]);
//number为自定义变量,自己可以自定义
db.ykd_product.count({ currentPrice: { $gt: 200 } });

$lookup

{
   $lookup:
     {
       from: <被关联的表>,   //右集合
       localField: <当前表的关联属性>,  //左集合 join字段
       foreignField: <被关联表的关联属性>, //右集合 join字段
       as: <输出属性名>   //新生成字段(类型array)
     }
}

这里的前提是localFieldforeignField必须相对应。比如这里的两张表分别是:ykd_action_dataykd_accounts。正确代码如下:

db.ykd_action_data.aggregate([
  {
    $lookup: {
      from: 'ykd_accouts',
      localField: 'accountId',
      foreignField: '_id',
      as: 'account'
    }
  }
]);

img

$unwind

这个可以将指定的字段进行数组分片,例如我们指定的是上图的account,如下:

db.ykd_action_data.aggregate([
  {
    $lookup: {
      from: 'ykd_accounts',
      localField: 'accountId',
      foreignField: '_id',
      as: 'account'
    }
  },
  { $unwind: '$account' }
]);

那么它的结果就如下所示:

watermark,image_d2F0ZXJtYXNrLnBuZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSx3XzEwMA==,t_60,g_se,x_10,y_10

MapReduce

MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行处理,通过将任务分解成多个小任务在集群上并行执行,来简化分布式计算的处理过程。

基本语法如下所示:

db.collection.mapReduce(
   function() {emit(key,value);}, //map 函数
   function(key,values) {return reduceFunction}, //reduce 函数
   {
      out: <输出的临时表>,
      query: <查询条件>,
      sort: <排序条件>,
      limit: <限制数量>,
      finalize: <最后执行的函数>
   }
)

上面的query,sort,limit都是用于map函数之前的。

比如说我们要获得一个学生表里面的每个班级的期末成绩的平均值,那么具体操作就如下所示了。

db.ykd_student.mapReduce(
   function map() {
      emit(this.classNum,this.finalExam);
   },
   function reduce(key,value) {
      let avg = 0;
      for (let i = 0; i < value.length; i++) {
         avg += value[i];
      }
      avg /= value.length;
      return avg;
   },
   {
      out: 'ykd_rank_avg'
   }
)find({});

下面我们要求每个不同的衣服种类的降价力度,如下:

db.ykd_product.mapReduce(
   function map() {
      emit(this.productType, {crossedPrice: this.crossedPrice,currentPrice: this.currentPrice});
   },
   function reduce(key,value) {
      let total = 0;
      for (let i = 0; i < value.length; i++) {
         total += value[i].crossedPrice - value[i].currentPrice;
      }
      total /= value.length;
      return Math.round(total);
   },
   {
      out: 'ykd_rank_avg'
   }
)find({});