mongoDB-document

MongoDB 将数据记录存储为 BSON类型的 文档(document)。 BSON 是一种二进制数据类型,是json 的一种扩展, bson 支持了更多的数据类型。 下图就是一个document(文档) 示例:


document

1. 数据结构

document 的数据结构如下所示:由若干个字段-值对组成,其中字段的值可以是任何 BSON 数据类型,包括嵌入式数据结构如 其他文档、数组和文档数组等。

{
   field1: value1,
   field2: value2,
   field3: value3,
   ...
   fieldN: valueN 
}

如下申明了一个mydoc 的文档,

var mydoc = {
               _id: ObjectId("5099803df3f4948bd2f98391"),
               name: { first: "Alan", last: "Turing" },
               birth: new Date('Jun 23, 1912'),
               death: new Date('Jun 07, 1954'),
               contribs: [ "Turing machine", "Turing test", "Turingery" ],
               views : NumberLong(1250000)
            }

2. 字段命名

字段(field)的命名都是由字符串组成
字段名称有以下几条限制:

  • 字段名 _id 用作主键;它的值在集合中必须是唯一的,是不可变的,可以是数组以外的任何类型。如果 _id 包含子字段,则子字段名称不能以 $ 符号开头
  • 字段名称不能包含空字符
  • 服务器允许存储包含点 . 和美元符号 $ 开头的字段名称
  • MongodB 5.0 增加了对在字段名称中使用 $. 的改进支持。但有一些限制,具体如下:
    在大多数情况下,无法直接访问使用此类字段名称存储的数据。您需要在访问这些字段的查询中使用 $getField$setField$literal 等辅助方法

对于不同的字段类型的存储操作,字段名称验证规则并不相同。下面总结了不同的插入和更新操作如何处理以美元 ($) 为前缀的字段名称

  • MongoDB 不支持重复的字段名称
2.1 插入操作
  • 允许以$ 为前缀的字段作为插入的外层和嵌套字段名称
// 示例 $price为外层字段
db.sales.insertOne( {
   "$price": 50.00,
   "quantity": 30
} )
  • 使用其他保留关键字的插入允许使用$ 作为前缀。像 $inc 这样的运算符名称可以用作字段名称,也可以用作 id、db 和 ref 等保留关键字
db.books.insertOne( {
   "$id": "h1961-01",
   "location": {
      "$db": "novels",
      "$ref": "2007042768",
      "$inc": true
} } )
  • 在upset操作中 ,如果更新未找到执行插入操作时, upset 可以接受$ 为前缀的字段, 但是如果更新的匹配到了文档,此时,更新操作可能会导致错误。 如下例子所示,$hotel 字段以$开头, 如果未匹配到date 为"2021-07-07" 的文档,此时支持插入新的文档, 但是如果匹配到了,此时更新操作失败
db.expenses.updateOne(
   { "date": "2021-07-07" },
   { $set: {
      "phone": 25.17,
      "$hotel": 320.10
   } },
   { upsert: true }
)
2.2 更新操作
  • 文档替代更新
    文档的更新要么新字段替换现有字段,要么修改这些字段。在更新执行替换的情况下,不允许以美元$ 为前缀的字段作为最外层字段名称。
    例如: 有如下的文档,$rooms 字段是最外层字段,此时不允许对该字段的修改(不包括包括子文档,子文档中的字段可以,例如brbath),但是可以对其他非$前缀的字段修改,如address 字段,包括address 字段中的子字段$number$street 修改
{
   "_id": "E123",
   "address": {
      "$number": 123,
      "$street": "Elm Road"
   },
   "$rooms": {
      "br": 2,
      "bath": 1
   }
}
// 可以
db.expenses.updateOne(
   { "_id": "E123" },
   { $set: { "address.$street": "Elm Ave" } }
)

// 可以
db.expenses.updateOne(
   { "_id": "E123" },
   { $set: { "$rooms.br": "3222" } }
)
// 更新报错
db.expenses.updateOne( 
  { "_id": "E123" },
   { $set: { "$rooms":{"bath":22,"bath":1111}}}
)

  • 文档的修改更新
    当更新修改而不是替换现有文档字段时,以$ 为前缀的字段可以是最外层字段名称。可以直接访问子字段,但您需要一个辅助方法来访问最外层字段
// 示例文档
{
   _id: ObjectId("610023ad7d58ecda39b8d161"),
   "part": "AB305",
   "$bin": 200,
   "quantity": 100,
   "pricing": { sale: true, "$discount": 60 }
}

修改非$前缀的字段

db.inventory.findAndModify( {
    query: { "part": { $eq: "AB305" } },
    update: { $inc: { "pricing.$discount": 10 } }
} )

修改非$前缀的最外层字段,通过getField 和literal 实现

db.inventory.findAndModify( {
   query: { $expr: {
      $eq: [ { $getField: { $literal: "$bin" } }, 200 ]
   } },
   update: { $inc: { "quantity": 10 } }
} )
  • 文档的聚合修改
    $replaceWith$setField$getField$literal 联合使用来修改聚合管道中的$ 前缀字段
// 示例文档
{
   "_id": 100001,
   "$term": "fall",
   "registered": true,
   "grade": 4
}

使用管道创建一个名为spring2022的新集合,更新$ 为前缀的 $term字段

db.school.aggregate( [
   { $match: { "registered": true } },
   { $replaceWith: {
      $setField: {
         field: { $literal: "$term" },
         input: "$$ROOT",
         value: "spring"
   } } },
   { $out: "spring2022" }
] )

3. 点符号

MongoDB 使用点符号来访问数组的元素和访问嵌入文档的字段

3.1 数组

要通过从零开始的索引位置指定或访问数组的元素,请将数组名称与点 (.) 和从零开始的索引位置连接起来,并用引号引起来

"<array>.<index>"

示例:

{
   ...
   contribs: [ "Turing machine", "Turing test", "Turingery" ],
   ...
}
3.2 嵌入式文档

要使用点表示法指定或访问嵌入文档的字段,请将嵌入文档名称与点 (.) 和字段名称连接起来,并用引号引起来

"<embedded document>.<field>"

示例:

{
   ...
   name: { first: "Alan", last: "Turing" },
   contact: { phone: { type: "cell", number: "111-222-3333" } },
   ...
}
  • "name.last": 获取name 字段中嵌入的子字段
  • "contact.phone.number": 获取contact 嵌入文档中phone 中的子字段 number

4. 文档限制

4.1 文档大小

BSON 文档的最大大小为 16 兆字节
最大文档大小有助于确保单个文档不会使用过多的 RAM,或者在传输期间不会使用过多的带宽。为了存储大于最大大小的文档,MongoDB 提供了 GridFS API。

4.2 文档字段有序性

与 JavaScript 对象不同,BSON 文档中的字段是有序的

查询操作时字段顺序:

  • 比较文档时,字段排序很重要 例如 {a: 1, b: 1} 与 {b: 1, a: 1} 是不同的
  • 为了高效的查询执行,查询引擎可以在查询处理期间重新排序字段。在其他情况下,处理下列运算符时可能会发生重新排序字段:$project$addFields$set$unset
    1. 字段重新排序可能发生在中间结果以及查询返回的最终结果中
    2. 由于某些操作可能会重新排序字段,因此不应依赖使用前面列出的运算符的查询返回的结果中的特定字段排序。

写操作时字段顺序
对于写入操作,MongoDB 保留文档字段的顺序,但以下情况除外:

  • _id 字段始终是文档中的第一个字段
  • 对字段的重新命名的更新操作($rename)可能会导致文档中的字段重新排序
4.3 _id字段

在 MongoDB 中,存储在集合中的每个文档都需要一个唯一的 _id 字段作为主键。如果插入的文档省略了 _id 字段,MongoDB 驱动程序会自动为 _id 字段生成一个 ObjectId。 这也适用于通过带有 upsert: true 的更新操作插入的文档

_id 字段具有以下限制:

  • 默认情况下,MongoDB 在创建集合期间会在 _id 字段上创建唯一索引
  • _id 字段始终是文档中的第一个字段。如果服务器先接收到一个没有_id字段的文档,那么服务器会自动生成该字段并将该字段移到开头
  • 如果 _id 包含子字段,则子字段名称不能开头 带有 ($) 符号
  • _id 字段可以包含任何 BSON 数据类型的值,除了数组、正则表达式和未定义类型

_id 字段常用值:

  • 使用ObjectId 类型
  • 使用自然唯一标识符。这节省了空间并避免了额外的索引
  • 生成一个自动递增的数字
  • 在您应用程序代码中生成一个 UUID, 并将UUID以BinData 类型存储。 使得在集合和 _id 索引中更有效地存储 UUID 值
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容