MongoDB 的关系表示多个文档之间在逻辑上的相互联系。php
文档间能够经过嵌入和引用来创建联系。正则表达式
MongoDB 中的关系能够是:mongodb
接下来咱们来考虑下用户与用户地址的关系。数据库
一个用户能够有多个地址,因此是一对多的关系。编程
如下是 user 文档的简单结构:数组
{ "_id":ObjectId("52ffc33cd85242f436000001"), "name": "Tom Hanks", "contact": "987654321", "dob": "01-01-1991" }
如下是 address 文档的简单结构:浏览器
{ "_id":ObjectId("52ffc4a5d85242602e000000"), "building": "22 A, Indiana Apt", "pincode": 123456, "city": "Los Angeles", "state": "California" }
使用嵌入式方法,咱们能够把用户地址嵌入到用户的文档中:缓存
"_id":ObjectId("52ffc33cd85242f436000001"), "contact": "987654321", "dob": "01-01-1991", "name": "Tom Benzamin", "address": [ { "building": "22 A, Indiana Apt", "pincode": 123456, "city": "Los Angeles", "state": "California" }, { "building": "170 A, Acropolis Apt", "pincode": 456789, "city": "Chicago", "state": "Illinois" }] }
以上数据保存在单一的文档中,能够比较容易的获取和维护数据。 你能够这样查询用户的地址:安全
>db.users.findOne({"name":"Tom Benzamin"},{"address":1})
注意:以上查询中 db 和 users 表示数据库和集合。服务器
这种数据结构的缺点是,若是用户和用户地址在不断增长,数据量不断变大,会影响读写性能。
引用式关系是设计数据库时常常用到的方法,这种方法把用户数据文档和用户地址数据文档分开,经过引用文档的 id 字段来创建关系。
{ "_id":ObjectId("52ffc33cd85242f436000001"), "contact": "987654321", "dob": "01-01-1991", "name": "Tom Benzamin", "address_ids": [ ObjectId("52ffc4a5d85242602e000000"), ObjectId("52ffc4a5d85242602e000001") ] }
以上实例中,用户文档的 address_ids 字段包含用户地址的对象id(ObjectId)数组。
咱们能够读取这些用户地址的对象id(ObjectId)来获取用户的详细地址信息。
这种方法须要两次查询,第一次查询用户地址的对象id(ObjectId),第二次经过查询的id获取用户的详细地址信息。
>var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1}) >var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})
在上一章节MongoDB关系中咱们提到了MongoDB的引用来规范数据结构文档。
MongoDB 引用有两种:
考虑这样的一个场景,咱们在不一样的集合中 (address_home, address_office, address_mailing, 等)存储不一样的地址(住址,办公室地址,邮件地址等)。
这样,咱们在调用不一样地址时,也须要指定集合,一个文档从多个集合引用文档,咱们应该使用 DBRefs。
DBRef的形式:
{ $ref : , $id : , $db : }
三个字段表示的意义为:
如下实例中用户数据文档使用了 DBRef, 字段 address:
{ "_id":ObjectId("53402597d852426020000002"), "address": { "$ref": "address_home", "$id": ObjectId("534009e4d852427820000002"), "$db": "w3cschoolcc"}, "contact": "987654321", "dob": "01-01-1991", "name": "Tom Benzamin" }
address DBRef 字段指定了引用的地址文档是在 address_home 集合下的 w3cschoolcc 数据库,id 为 534009e4d852427820000002。
如下代码中,咱们经过指定 $ref 参数(address_home 集合)来查找集合中指定id的用户地址信息:
>var user = db.users.findOne({"name":"Tom Benzamin"}) >var dbRef = user.address >db[dbRef.$ref].findOne({"_id":(dbRef.$id)})
以上实例返回了 address_home 集合中的地址数据:
{ "_id" : ObjectId("534009e4d852427820000002"), "building" : "22 A, Indiana Apt", "pincode" : 123456, "city" : "Los Angeles", "state" : "California" }
官方的MongoDB的文档中说明,覆盖查询是如下的查询:
因为全部出如今查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。
由于索引存在于RAM中,从索引中获取数据比经过扫描文档读取数据要快得多。
为了测试盖索引查询,使用如下 users 集合:
{ "_id": ObjectId("53402597d852426020000002"), "contact": "987654321", "dob": "01-01-1991", "gender": "M", "name": "Tom Benzamin", "user_name": "tombenzamin" }
咱们在 users 集合中建立联合索引,字段为 gender 和 user_name :
>db.users.ensureIndex({gender:1,user_name:1})
如今,该索引会覆盖如下查询:
>db.users.find({gender:"M"},{user_name:1,_id:0})
也就是说,对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是很是快速的数据查询。
因为咱们的索引中不包括 _id 字段,_id在查询中会默认返回,咱们能够在MongoDB的查询结果集中排除它。
下面的实例没有排除_id,查询就不会被覆盖:
>db.users.find({gender:"M"},{user_name:1})
最后,若是是如下的查询,不能使用覆盖索引查询:
MongoDB 查询分析能够确保咱们建议的索引是否有效,是查询语句性能分析的重要工具。
MongoDB 查询分析经常使用函数有:explain() 和 hint()。
explain 操做提供了查询信息,使用索引及查询统计等。有利于咱们对索引的优化。
接下来咱们在 users 集合中建立 gender 和 user_name 的索引:
>db.users.ensureIndex({gender:1,user_name:1}) </p> <p>如今在查询语句中使用 explain :</p> <pre> >db.users.find({gender:"M"},{user_name:1,_id:0}).explain()
以上的 explain() 查询返回以下结果:
{ "cursor" : "BtreeCursor gender_1_user_name_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 0, "nscanned" : 1, "nscannedObjectsAllPlans" : 0, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : true, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "gender" : [ [ "M", "M" ] ], "user_name" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ] } }
如今,咱们看看这个结果集的字段:
虽然MongoDB查询优化器通常工做的很不错,可是也可使用 hint 来强制 MongoDB 使用一个指定的索引。
这种方法某些情形下会提高性能。 一个有索引的 collection 而且执行一个多字段的查询(一些字段已经索引了)。
以下查询实例指定了使用 gender 和 user_name 索引字段来查询:
>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})
可使用 explain() 函数来分析以上查询:
>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()
mongodb不支持事务,因此,在你的项目中应用时,要注意这点。不管什么设计,都不要要求mongodb保证数据的完整性。
可是mongodb提供了许多原子操做,好比文档的保存,修改,删除等,都是原子操做。
所谓原子操做就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的状况。
考虑下面的例子,图书馆的书籍及结帐信息。
实例说明了在一个相同的文档中如何确保嵌入字段关联原子操做(update:更新)的字段是同步的。
book = { _id: 123456789, title: "MongoDB: The Definitive Guide", author: [ "Kristina Chodorow", "Mike Dirolf" ], published_date: ISODate("2010-09-24"), pages: 216, language: "English", publisher_id: "oreilly", available: 3, checkout: [ { by: "joe", date: ISODate("2012-10-15") } ] }
你可使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息。
在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:
db.books.findAndModify ( { query: { _id: 123456789, available: { $gt: 0 } }, update: { $inc: { available: -1 }, $push: { checkout: { by: "abc", date: new Date() } } } } )
用来指定一个键并更新键值,若键不存在并建立。
{ $set : { field : value } }
用来删除一个键。
{ $unset : { field : 1} }
$inc能够对文档的某个值为数字型(只能为知足要求的数字)的键进行增减的操做。
{ $inc : { field : value } }
用法:
{ $push : { field : value } }
把value追加到field里面去,field必定要是数组类型才行,若是field不存在,会新增一个数组类型加进去。
同$push,只是一次能够追加多个值到一个数组字段内。
{ $pushAll : { field : value_array } }
从数组field内删除一个等于value值。
{ $pull : { field : _value } }
增长一个值到数组内,并且只有当这个值不在数组内才增长。
删除数组的第一个或最后一个元素
{ $pop : { field : 1 } }
修改字段名称
{ $rename : { old_field_name : new_field_name } }
位操做,integer类型
{$bit : { field : {and : 5}}}
> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] } > t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true ) > t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
考虑如下文档集合(users ):
{ "address": { "city": "Los Angeles", "state": "California", "pincode": "123" }, "tags": [ "music", "cricket", "blogs" ], "name": "Tom Benzamin" }
以上文档包含了 address 子文档和 tags 数组。
假设咱们基于标签来检索用户,为此咱们须要对集合中的数组 tags 创建索引。
在数组中建立索引,须要对数组中的每一个字段依次创建索引。因此在咱们为数组 tags 建立索引时,会为 music、cricket、blogs三个值创建单独的索引。
使用如下命令建立数组索引:
>db.users.ensureIndex({"tags":1})
建立索引后,咱们能够这样检索集合的 tags 字段:
>db.users.find({tags:"cricket"})
为了验证咱们使用使用了索引,可使用 explain 命令:
>db.users.find({tags:"cricket"}).explain()
以上命令执行结果中会显示 "cursor" : "BtreeCursor tags_1" ,则表示已经使用了索引。
假设咱们须要经过city、state、pincode字段来检索文档,因为这些字段是子文档的字段,因此咱们须要对子文档创建索引。
为子文档的三个字段建立索引,命令以下:
>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})
一旦建立索引,咱们可使用子文档的字段来检索数据:
>db.users.find({"address.city":"Los Angeles"})
记住查询表达式必须遵循指定的索引的顺序。因此上面建立的索引将支持如下查询:
>db.users.find({"address.city":"Los Angeles","address.state":"California"})
一样支持如下查询:
>db.users.find({"address.city":"LosAngeles","address.state":"California","address.pincode":"123"})
每一个索引占据必定的存储空间,在进行插入,更新和删除操做时也须要对索引进行操做。因此,若是你不多对集合进行读取操做,建议不使用索引。
因为索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
若是索引的大小大于内存的限制,MongoDB会删除一些索引,这将致使性能降低。
索引不能被如下的查询使用:
因此,检测你的语句是否使用索引是一个好的习惯,能够用explain来查看。
从2.6版本开始,若是现有的索引字段的值超过索引键的限制,MongoDB中不会建立索引。
若是文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具相似。
在前面几个章节中咱们已经使用了MongoDB 的对象 Id(ObjectId)。
在本章节中,咱们将了解的ObjectId的结构。
ObjectId 是一个12字节 BSON 类型数据,有如下格式:
MongoDB中存储的文档必须有一个"_id"键。这个键的值能够是任何类型的,默认是个ObjectId对象。
在一个集合里面,每一个文档都有惟一的"_id"值,来确保集合里面每一个文档都能被惟一标识。
MongoDB采用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转换为字符串格式。你可使用下面的代码:
>new ObjectId().str
以上代码将返回Guid格式的字符串::
5349b4ddd2781d08c09890f3
Map-Reduce是一种计算模型,简单的说就是将大批量的工做(数据)分解(MAP)执行,而后再将结果合并成最终结果(REDUCE)。
MongoDB提供的Map-Reduce很是灵活,对于大规模数据分析也至关实用。
如下是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) 返回键值对。
参数说明:
如下实例在集合 orders 中查找 status:"A" 的数据,并根据 cust_id 来分组,并计算 amount 的总和。
考虑如下文档结构存储用户的文章,文档存储了用户的 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" } )
以上 mapReduce 输出结果为:
{ "result" : "post_total", "timeMillis" : 23, "counts" : { "input" : 5, "emit" : 5, "reduce" : 1, "output" : 2 }, "ok" : 1 }
结果代表,共有 5 个符合查询条件(status:"active")的文档, 在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。
具体参数说明:
使用 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()
以上查询显示以下结果,两个用户 tom 和 mark 有两个发布的文章:
{ "_id" : "mark", "value" : 4 } { "_id" : "runoob", "value" : 1 }
用相似的方式,MapReduce能够被用来构建大型复杂的聚合查询。
Map函数和Reduce函数可使用 JavaScript 来实现,使得MapReduce的使用很是灵活和强大。
全文检索对每个词创建一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先创建的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程相似于经过字典中的检索字表查字的过程。
MongoDB 从 2.4 版本开始支持全文检索,目前支持15种语言(暂时不支持中文)的全文索引。
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 使用 $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"}})
这里面使用正则表达式有两点须要注意:
正则表达式中使用变量。必定要使用eval将组合的字符串进行转换,不能直接将字符串拼接后传入给表达式。不然没有报错信息,只是结果为空!实例以下:
var name=eval("/" + 变量值key +"/i");
如下是模糊查询包含title关键词, 且不区分大小写:
title:eval("/"+title+"/i") // 等同于 title:{$regex:title,$Option:"$i"}
RockMongo是PHP5写的一个MongoDB管理工具。
经过 Rockmongo 你能够管理 MongoDB服务,数据库,集合,文档,索引等等。
它提供了很是人性化的操做。相似 phpMyAdmin(PHP开发的MySql管理工具)。
Rockmongo 下载地址:http://rockmongo.com/downloads
主要特征:
参考文章:http://rockmongo.com/wiki/introduction?lang=zh_cn
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,可是它是存储在MonoDB的集合中。
GridFS 能够更好的存储大于16M的文件。
GridFS 会将大文件对象分割成多个小的chunk(文件片断),通常为256k/个,每一个chunk将做为MongoDB的一个文档(document)被存储在chunks集合中。
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每一个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
如下是简单的 fs.files 集合文档:
{ "filename": "test.txt", "chunkSize": NumberInt(261120), "uploadDate": ISODate("2014-04-13T11:32:33.557Z"), "md5": "7b762939321e146569b07f72c62cca4f", "length": NumberInt(646) }
如下是简单的 fs.chunks 集合文档:
{ "files_id": ObjectId("534a75d19f54bfec8a2fe44b"), "n": NumberInt(0), "data": "Mongo Binary Data" }
如今咱们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles.exe工具。
打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:
>mongofiles.exe -d gridfs put song.mp3
GridFS 是存储文件的数据名称。若是不存在该数据库,MongoDB会自动建立。Song.mp3 是音频文件名。
使用如下命令来查看数据库中文件的文档:
>db.fs.files.find()
以上命令执行后返回如下文档数据:
{ _id: ObjectId('534a811bf8b4aa4d33fdf94d'), filename: "song.mp3", chunkSize: 261120, uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41", length: 10401959 }
咱们能够看到 fs.chunks 集合中全部的区块,如下咱们获得了文件的 _id 值,咱们能够根据这个 _id 获取区块(chunk)的数据:
>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})
以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。
MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,咱们能够想象其就像一个环形队列,当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素!
咱们经过createCollection来建立一个固定集合,且capped选项设置为true:
>db.createCollection("cappedLogCollection",{capped:true,size:10000})
还能够指定文档个数,加上max:1000属性:
>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})
判断集合是否为固定集合:
>db.cappedLogCollection.isCapped()
若是须要将已存在的集合转换为固定集合可使用如下命令:
>db.runCommand({"convertToCapped":"posts",size:10000})
以上代码将咱们已存在的 posts 集合转换为固定集合。
固定集合文档按照插入顺序储存的,默认状况下查询就是按照插入顺序返回的,也可使用$natural调整返回顺序。
>db.cappedLogCollection.find().sort({$natural:-1})
能够插入及更新,但更新不能超出collection的大小,不然更新失败,不容许删除,可是能够调用drop()删除集合中的全部行,可是drop后须要显式地重建集合。
在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。
MongoDB 没有像 SQL 同样有自动增加的功能, MongoDB 的 _id 是系统自动生成的12字节惟一标识。
但在某些状况下,咱们可能须要实现 ObjectId 自动增加功能。
因为 MongoDB 没有实现这个功能,咱们能够经过编程的方式来实现,如下咱们将在 counters 集合中实现_id字段自动增加。
考虑如下 products 文档。咱们但愿 _id 字段实现 从 1,2,3,4 到 n 的自动增加功能。
{ "_id":1, "product_name": "Apple iPhone", "category": "mobiles" }
为此,建立 counters 集合,序列字段值能够实现自动长:
>db.createCollection("counters")
如今咱们向 counters 集合中插入如下文档,使用 productid 做为 key:
{ "_id":"productid", "sequence_value": 0 }
sequence_value 字段是序列经过自动增加后的一个值。
使用如下命令插入 counters 集合的序列文档中:
>db.counters.insert({_id:"productid",sequence_value:0})
如今,咱们建立函数 getNextSequenceValue 来做为序列名的输入, 指定的序列会自动增加 1 并返回最新序列值。在本文的实例中序列名为 productid 。
>function getNextSequenceValue(sequenceName){ var sequenceDocument = db.counters.findAndModify( { query:{_id: sequenceName }, update: {$inc:{sequence_value:1}}, new:true }); return sequenceDocument.sequence_value; }
接下来咱们将使用 getNextSequenceValue 函数建立一个新的文档, 并设置文档 _id 自动为返回的序列值:
>db.products.insert({ "_id":getNextSequenceValue("productid"), "product_name":"Apple iPhone", "category":"mobiles"}) >db.products.insert({ "_id":getNextSequenceValue("productid"), "product_name":"Samsung S3", "category":"mobiles"})
就如你所看到的,咱们使用 getNextSequenceValue 函数来设置 _id 字段。
为了验证函数是否有效,咱们可使用如下命令读取文档:
>db.products.find()
以上命令将返回如下结果,咱们发现 _id 字段是自增加的:
{ "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"} { "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }