MongoDB 学习笔记十一 ObjectId、MapReduce、全文检索、正则表达式

MongoDB 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

MongoDB Map Reduce

Map-Reduce 是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。

MongoDB 提供的 Map-Reduce 非常灵活,对于大规模数据分析也相当实用。

MapReduce 命令

以下是 MapReduce 的基本语法:

>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 中查找 statis.A 的数据,并根据 cust_id 来分组,并计算 amount 的总和。

在这里插入图片描述

使用 MapReduce

考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 华为文章的 status 字段:

>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "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" 
      }
).find()

以上查询显示如下结果:

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

用类似的方式,MapReduce 可以被用来构建大型的聚合查询。

Map 函数 和 Reduce 函数可以使用 JavaScript 来实现,使得 MapReduce 的使用非常灵活和强大。

MongoDB 全文检索

全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。

这个过程类似于通过字典中的检索字表查字的过程。

MongoDB 从 2。4 版本开始支持全文检索,目前支持 15 种语言的全文检索。

danish
dutch
english
finnish
french
german
hungarian
italian
norwegian
portuguese
romanian
russian
spanish
swedish
turkish

启用全文检索

MongoDB 在 2.6 版本以后是默认开启全文检索的,如果你使用之前的版本,你需要使用以下代码来启用全文检索

>db.adminCommand({setParameter:true,textSearchEnabled:true})

或者使用命令:

mongod --setParameter textSearchEnabled=true

创建全文索引

考虑以下 posts 集合的文档数据,包含了文章内容(post_text)及标签(tags):

{
   "post_text": "enjoy the mongodb articles on Runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
}

我们可以对 post_text 字段建立全文索引,这样我们可以搜索文章内的内容:

>db.posts.ensureIndex({post_text:"text"})

使用全文索引

现在我们已经对 post_text 建立了全文索引,我们可以搜索文章中的关键词 runoob:

>db.posts.find({$text:{$search:"runoob"}})

以下命令返回了如下包含 runoob 关键词的文档数据:

{ 
   "_id" : ObjectId("53493d14d852429c10000002"), 
   "post_text" : "enjoy the mongodb articles on Runoob", 
   "tags" : [ "mongodb", "runoob" ]
}

如果你使用的是旧版本的 MongoDB ,你可以使用以下命令:

>db.posts.runCommand("text",{search:"runoob"})

使用全文索引可以提高搜索效率。

删除全文索引

删除已存在的全文索引,可以使用 find 命令查找索引名:

>db.posts.getIndexes()

通过以上命令获取索引名,本例的索引名为 post_text_text,执行以下命令过来删除索引:

>db.posts.dropIndex("post_text_text")

MongoDB 正则表达式

正则表达式是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。

许多程序设计语言都支持利用正则表达式进行字符串操作。

MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。

MongoDB 使用 PCRE(Perl Compatible Regular Expression)作为正则表达式语言。

不同于全文检索,我们只用正则表达式不需要做任何配置。

考虑以下 posts 集合的文档结构,该文档包含了文章内容和标签:

{
   "post_text": "enjoy the mongodb articles on runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
}

使用正则表达式

以下命令使用正则表达式查找包含 runoob 字符串的文章:

>db.posts.find({post_text:{$regex:"runoob"}})

以上查询也可以写为:

>db.posts.find({post_text:/runoob/})

不区分大小写的正则表达式

如果检索需要不区分大小写,我们可以设置 $options 为 $i 。

以下命令将查找不区分大小写的字符串 runoob :

>db.posts.find({post_text:{$regex:"runoob",$options:"$i"}})

集合中会返回所有包含字符串 runoob 的数据,且不区分大小写:

{
   "_id" : ObjectId("53493d37d852429c10000004"),
   "post_text" : "hey! this is my post on  runoob", 
   "tags" : [ "runoob" ]
} 

数组元素使用正则表达式

我们还可以在数组字段中使用正则表达式来查找内容。这在标签的实现上非常有用,如果你需要查找包含 以 run 开头的标签数据(ru 或 run 或 runoob),你可以使用以下代码:

>db.posts.find({tags:{$regex:"run"}})

优化正则表达式查询

  • 如果你的文档中字段设置了索引,那么使用索引相比于正则表达式匹配查找所有的数据查询速度更快。
  • 如果正则表达式是前缀表达式,所有匹配的数据将以指定的前缀字符串为开始。例如:如果正则表达式为 ^tut,查询语句将查找 以 tut 为开头的字符串。

这里面使用正则表达式有两点需要注意:

正则表达式中使用变量。一定要使用 eval 将组合的字符串进行转换,不能直接将字符串拼接后传入给表达式。否则没有报错信息,只是结果为空!

实例如下:

var name=eval("/" + 变量值key +"/i"); 

以下是模糊查询包含 title 关键词,且不区分大小写:

title:eval("/"+title+"/i")    // 等同于 title:{$regex:title,$Option:"$i"}   

regex 操作符的介绍

MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式,使用PCRE(Pert Compatible Regular Expression)作为正则表达式语言。

regex操作符:

{<field>:{$regex:/pattern/,$options:’<options>’}}
{<field>:{$regex:’pattern’,$options:’<options>’}}
{<field>:{$regex:/pattern/<options>}}

正则表达式对象

{<field>: /pattern/<options>}

$regex与正则表达式对象的区别:

- 在$in操作符中只能使用正则表达式对象,例如:{name:{$in:[/^joe/i,/^jack/]}}
- 在使用隐式的$and操作符中,只能使用$regex,例如:{name:{$regex:/^jo/i, $nin:['john']}}
- 当option选项中包含X或S选项时,只能使用$regex,例如:{name:{$regex:/m.*line/,$options:"si"}}

$ regex 操作符的使用

$regex操作符中的option选项可以改变正则匹配的默认行为,它包括i, m, x以及S四个选项,其含义如下

i 忽略大小写,{<field>{$regex/pattern/i}},设置i选项后,模式中的字母会进行大小写不敏感匹配。
m 多行匹配模式,{<field>{$regex/pattern/,$options:'m'},m选项会更改^和$元字符的默认行为,分别使用与行的开头和结尾匹配,而不是与输入字符串的开头和结尾匹配。
x 忽略非转义的空白字符,{<field>:{$regex:/pattern/,$options:'m'},设置x选项后,正则表达式中的非转义的空白字符将被忽略,同时井号(#)被解释为注释的开头注,只能显式位于option选项中。
s 单行匹配模式{<field>:{$regex:/pattern/,$options:'s'},设置s选项后,会改变模式中的点号(.)元字符的默认行为,它会匹配所有字符,包括换行符(\n),只能显式位于option选项中。

使用 $regex 操作符时,需要注意下面几个问题:

i,m,x,s可以组合使用,例如:{name:{$regex:/j*k/,$options:"si"}}
在设置索弓}的字段上进行正则匹配可以提高查询速度,而且当正则表达式使用的是前缀表达式时,查询速度会进一步提高,例如:{name:{$regex: /^joe/}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值