MongoDB入门---索引限制&ObjectId&MAP Reduce聚合查询

       前面的知识点里,我们学习了MongoDB中索引的运用。但是我们知道,无论在那个数据库中,索引的使用都是有限制的。接下来我们就看一下,在MongoDB中,索引的限制。首先就是额外的开销。每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。

    再来就是内存(RAM)的使用。由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。

    还有就是通常意义下的查询限制了。在MongoDB中,索引不能被以下的查询使用:

  • 正则表达式及非操作符,如 $nin, $not, 等。
  • 算术运算符,如 $mod, 等。
  • $where 子句

    所以,检测我们的语句是否使用索引是一个好的习惯,可以用explain来查看。

    再来就是索引键限制了。从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。还有插入文档超过索引键限制。如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具类似。

    最后我们再来看一下在MongoDB中,缩印的最大范围:

  • 集合中索引不能超过64个
  • 索引名的长度不能超过128个字符
  • 一个复合索引最多可以有31个字段

    好啦,索引限制到这里就介绍完毕了。接下来我们看一下ObjectId。在前面的记录中,我们已经使用了MongoDB 的对象 Id(ObjectId)。接下来就是看一下ObjectId的结构。首先ObjectId 是一个12字节 BSON 类型数据,有以下格式:

  • 前4个字节表示时间戳
  • 接下来的3个字节是机器标识码
  • 紧接的两个字节由进程id组成(PID)
  • 最后三个字节是随机数。

    我们都知道,在MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个服务器上同步自动增加主键值既费力还费时。接下来,我们就来尝试创建新的ObjectId。使用以下代码生成新的ObjectId:

>newObjectId = ObjectId()

    上面的语句返回以下唯一生成的id:

ObjectId("5349b4ddd2781d08c09890f3")

    我们也可以使用生成的id来取代MongoDB自动生成的ObjectId:

>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")

    然后呢,我们再来尝试创建文档的时间戳。由于 ObjectId 中存储了 4 个字节的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()

    以上代码将返回 ISO 格式的文档创建时间:

ISODate("2014-04-12T21:49:17Z")

    然后呢,在某些情况下,我们可能需要将ObjectId转换为字符串格式。所以我们可以使用下面的代码,尝试将 ObjectId 转换为字符串:

>new ObjectId().str

    以上代码将返回Guid格式的字符串:

5349b4ddd2781d08c09890f3

    最后呢,我们来看一下MAP Reduce。首先呢,Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。废话不多说,我们先来看一下它的基本语法结构:

>db.collection.mapReduce(
   function() {emit(key,value);},  //map 函数
   function(key,values) {return reduceFunction},   //reduce 函数
   {
      out: collection,
      query: document,
      sort: document,
      limit: number
   }
)

    我们要知道,使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value),遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。Map 函数必须调用 emit(key, value) 返回键值对。然后呢,咱们再来看一下参数说明:

  • map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
  • reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
  • out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
  • query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
  • sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
  • limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

    下面有一个实例,在集合 orders 中查找 status:"A" 的数据,并根据 cust_id 来分组,并计算 amount 的总和:


    接下来我们来使用它,看看效果,我们来考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 和文章的 status 字段:

>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "mark",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "luyaran",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "luyaran",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "luyaran,best girl。",
   "user_name": "luyaran",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })

    接下来,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
)

    运行上述 mapReduce 输出结果为:

{
        "result" : "post_total",
        "timeMillis" : 23,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "reduce" : 1,
                "output" : 2
        },
        "ok" : 1
}

    结果表明,共有 5 个符合查询条件(status:"active")的文档,在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。我们来看一下具体参数说明:

  • result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
  • timeMillis:执行花费的时间,毫秒为单位
  • input:满足条件被发送到map函数的文档个数
  • emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
  • ouput:结果集合中的文档个数(count对调试非常有帮助)
  • ok:是否成功,成功为1
  • err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大

    然后呢,我们使用 find 操作符来查看 mapReduce 的查询结果:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
).find()

    以上查询显示如下结果,两个用户 luyaran 和 mark 有两个发布的文章:

{ "_id" : "mark", "value" : 4 }
{ "_id" : "luyaran", "value" : 1 }

    用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。

    好啦,到这里呢,分享内容就结束了,各位如果感觉不错的话,请多多点赞支持哦。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值