【mongo系列】 三、mongo聚合和索引

一、聚合查询

聚合(aggregation ),可以定义一系列行为,按照顺序(pipeline)依次执行

先创建一组测试数据如下:

db.people.insertMany([{
  name:"Tom",
  age:12,
  gender:"M",
  score:72,
  type:"A"
},
{
  name:"Jeck",
  age:23,
  gender:"M",
  score:81,
  type:"B"
},
{
  name:"Rose",
  age:14,
  gender:"F",
  score:74,
  type:"A"
},
{
  name:"Sali",
  age:24,
  gender:"F",
  score:69,
  type:"B"
},
{
  name:"Harry",
  age:32,
  gender:"F",
  score:82,
  type:"A"
}])

1. 匹配分组

查询类型(type)是A的人之中,男女(gender)的总分分别是多少,并按照总分排序。

db.people.aggregate([
    // 匹配type是A的人
    {$match:{type:"A"}},
    // 按照gender分组,计算score的和,结果是生成新的集合,所以需要吧gender作为新集合的_id字段
    {$group:{_id:"$gender",total:{$sum:"$score"}}},
    // 按照total排序
    {$sort:{total:1}}
])

结果如下:

> db.people.aggregate([
...     {$match:{type:"A"}},
...     {$group:{_id:"$gender",total:{$sum:"$score"}}},
...     {$sort:{total:1}}
... ])
{ "_id" : "M", "total" : 72 }
{ "_id" : "F", "total" : 156 }
> 

2. project操作符

project用于可用于指定哪些字段显示,生成新的字段,字段拼接,格式转换等

  • 指定显示字段,生成新字段

    查看type类型是A,仅显示name和age字段,生成一个新字段new_f包含name和gender,这里的new_f是一个数组,并不是拼接

    db.people.aggregate([
        {$match:{type:"A"}},
        {$project:{name:1,age:1,new_f:["$name","$gender"]}}
        ])
    
  • 字符转换,条件语句

    $toUpper   // 转换为大写
    $concat    // 字符串拼接
    $toString  // 转换为字符串
    $cond      // 条件语句,里面可以写if,then,else
    

    例:

    db.people.aggregate([
        {$match:{type:"A"}},
        {$project:
          {
            // name转换为大写
            name:{$toUpper:"$name"},
            // 不显示_id字段
            _id:0,
            // 新的字段new,是name和age拼接起来,age需要转换为字符串后拼接
            new:{
                $concat:["$name"," age is ",{$toString: "$age"}]
            },
            // 新的字段flag,score大于80则good,否则bad
            flag: {
                $cond: {
                    if: {$gt:["$score",80]},
                    then: "good",
                    else: "bad"
                }
            }
        }}
    ])
    

    结果如下:

    > db.people.aggregate([
    ...     {$match:{type:"A"}},
    ...     {$project:
    ...       {
    ...         name:{$toUpper:"$name"},
    ...         _id:0,
    ...         new:{
    ...             $concat:["$name"," age is ",{$toString: "$age"}]
    ...         },
    ...         flag: {
    ...             $cond: {
    ...                 if: {$gt:["$score",80]},
    ...                 then: "good",
    ...                 else: "bad"
    ...             }
    ...         }
    ...     }}
    ... ])
    { "name" : "TOM", "new" : "Tom age is 12", "flag" : "bad" }
    { "name" : "ROSE", "new" : "Rose age is 14", "flag" : "bad" }
    { "name" : "HARRY", "new" : "Harry age is 32", "flag" : "good" }
    > 
    
  • 数组操作

    $size :获取数组长度
    $slice :数组切片
    
    // 获取hobby的长度
    {$size: "$hobby_list"}
    // 从hobby_list的第一个元素开始,取三个
    {$slice: ["$hobby_list",1,3]}
    

3. bucket操作符

bucket操作符用于区间分组

db.people.aggregate([
    {
        $bucket:{
            // 按照score分组
            groupBy:"$score",
            // 分组的区间60-70,70-80,80-90,每个区间含头不含尾
            boundaries: [60,70,80,90],
            // 输出的内容
            output: {
                // 计算总数 
                total:{$sum:1},
                // 计算分数的均值
                avg:{$avg:"$score"}
            }
        }
    }
])

4. skip和limit

与find行为一样,限制返回行数和跳过行

skip必须放在limit前

db.people.aggregate([
    // 按照gender分组,计算平均分
    {
        $group:{
            _id:"$gender",
            avg_score:{
                $avg: "$score"
            }
        }
    },
    // 按分数排序
    {$sort:{avg_score:1}},// 跳过第一条,仅显示一条(实际就是显示第二条数据)
    {$skip:1},
    {$limit:1}
])

5. out操作符

out把聚合的结果存储在新的collection中

db.people.aggregate([
    // 按照gender分组,计算平均分
    {
        $group:{
            _id:"$gender",
            avg_score:{
                $avg: "$score"
            }
        }
    },
    // 结果存储在gender_col这个collection中
    {
        $out:"gender_col"
    }
])

执行完成后后查询gender_col可以看到刚才统计的结果

> db.gender_col.find()
{ "_id" : "M", "avg_score" : 76.5 }
{ "_id" : "F", "avg_score" : 75 }
> 

二、索引

1. 基本命令

// 创建索引
db.testcol.createIndex({a:1})
// 查看索引
db.testcol.getIndexes()
// 删除索引(括号中是索引名,可以通过getIndexes看到)
db.testcol.dropIndex("a_1")

2. 单一索引

可以创建正序或是倒叙索引

// 1正序索引,-1倒叙索引
db.testcol.createIndex({a:1})

3. 唯一索引

唯一索引插入重复值会报错

db.testcol.createIndex({a:1},{unique:true})

4. 设置索引过期

可以在索引上设置过期时间,自动删除过期的数据(删除的是数据而不是索引)

比如可以用来记录日志,自动清理过期的日志

超时索引必须设置在一个时间类型的字段上,设置在其他类型字段上不生效。

// 插入一个包含时间类型的字段
db.testcol.insertOne({a:10,dt:new Date()})
// 创建索引,设置10秒过期
db.testcol.createIndex({dt:1},{expireAfterSeconds:10})

注:过期时间是从索引建立开始计算,后插入数据是从数据插入开始计算,并不是以日期字段存储的时间开始计算

5. 联合索引

联合索引中最多放32个

db.testcol.createIndex({a:1,b:-1})

联合索引第一个索引为主索引,单独使用主索引可以走索引,单独使用非主索引不会使用该索引

6. 文本索引

插入些测试数据

db.testcol.insertMany([{
  a:"hello world good night"
},
{
  a:"hello room yes ok"
},
{
  a:"good world hello cool"
}])

文本索引创建之前,不能使用文本搜索

// 查找文本中包含hello关键字的(没有建立文本索引,会报错)
db.testcol.find({$text:{$search:"hello"}})

创建文本索引

db.testcol.createIndex({a:"text"})
  • 单关键字查找

    查找文本中包含world关键字的

    > db.testcol.find({$text:{$search:"world"}})
    { "_id" : ObjectId("5e7d881052a8aaded835f104"), "a" : "good world hello cool" }
    { "_id" : ObjectId("5e7d881052a8aaded835f102"), "a" : "hello world good night" }
    > 
    
  • 多关键字查找

    查找包含hello或者world关键字的

    > db.testcol.find({$text:{$search:"hello world"}})
    { "_id" : ObjectId("5e7d881052a8aaded835f104"), "a" : "good world hello cool" }
    { "_id" : ObjectId("5e7d881052a8aaded835f103"), "a" : "hello room yes ok" }
    { "_id" : ObjectId("5e7d881052a8aaded835f102"), "a" : "hello world good night" }
    > 
    
  • 查找词组

    查找包含hello world这个词组的记录

    > db.testcol.find({$text:{$search:"\"hello world\""}})
    { "_id" : ObjectId("5e7d881052a8aaded835f102"), "a" : "hello world good night" }
    > 
    
  • 大小写敏感

    默认查找大小写不敏感

    // 默认不区分大小写,可以查到3条记录
    > db.testcol.find({$text:{$search:"Hello"}})
    { "_id" : ObjectId("5e7d881052a8aaded835f104"), "a" : "good world hello cool" }
    { "_id" : ObjectId("5e7d881052a8aaded835f103"), "a" : "hello room yes ok" }
    { "_id" : ObjectId("5e7d881052a8aaded835f102"), "a" : "hello world good night" }
    // 大小写敏感,没有匹配的记录
    > db.testcol.find({$text:{$search:"Hello",$caseSensitive:true}})
    > 
    

7. 后台创建索引

对数据量较大的collection创建index可能需要较长时间,测试前台shell无法执行其他操作

可以把创建index的操作放在后台

db.testcol.createIndex({a:1},{background:true})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值