![MongoDB进阶与实战:微服务整合、性能优化、架构管理](https://wfqqreader-1252317822.image.myqcloud.com/cover/697/38209697/b_38209697.jpg)
3.4 数组、内嵌
支持灵活的数据结构,是MongoDB这种文档数据库的一大优势。在面向对象的编程方式中,对象的成员可以是多种形式的,包括数组、子对象等。但是当我们希望将对象中的数据持久化到传统的关系型数据库中时,却发现没有很好的匹配模式。常见的一些做法如:
● 使用平铺式的多列式结构,如用tag1、tag2、tag3…表示数组中的若干个元素。
● 使用序列化的单列进行收敛,比如将数组或子对象转换为JSON字符串后存储到某个列,在读取时再进行解析。
无论哪一种方式,都是存在一些弊端的。平铺式的结构会导致列的数量膨胀,关系型数据库需要提前设计好Schema,但数组往往是动态的,无法满足快速变化的需求;单列序列化的方式带来了应用上的复杂性,数据库无法理解该列的内部结构,所能提供的操作只有“整存整取”。
MongoDB的文档模型充分理解了数组、内嵌式文档的数据结构,除了可以方便地对数组内的元素、内嵌文档的字段进行操作,还可以对这些“内嵌式”的字段进行索引以满足快速的查询。它们在使用方式上和普通的字段并没有什么大的不同,这是文档型数据库的一种强大的表现力。
值得注意的是,一些关系型数据库如MySQL、PostgreSQL在后来也支持数组和内嵌对象的类型,充分说明了该能力的重要性及普适性。
3.4.1 内嵌文档
让我们再回到前面的例子,一个book文档中可以包含作者的信息,包括作者名称、性别、家乡所在地等,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_60_1.jpg?sign=1739282925-ZnI4L0BlpoGL6z1iLmDES5TI11LyyE3e-0-bbf0bb708cb8776f02fa281a1f188ab3)
一个显著的优点是,当我们查询book文档的信息时,作者的信息也会一并返回。如果只希望返回作者的名称,则可以指定author.name,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_60_2.jpg?sign=1739282925-DFgdDm3BDm0sf9r4NJ7ZXbc4qpgdbGPB-0-249f1fc6d1861cb7f7e487c15b9621ef)
也可以将author.name作为查询条件,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_60_3.jpg?sign=1739282925-qNp8kEgOEGeH6buIcb9hZ09nEqgTYrgP-0-0cf5d3589c1cd0fecefdeee09ab29b53)
如果作者信息需要修改,则可以指定其中的某个字段,比如修改作者的家乡所在地,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_60_4.jpg?sign=1739282925-crfa8lO1CzwkaBdzgyFl0e6Mut4BLIOZ-0-eba4fb06c645e0c8c4bdb324946019a5)
3.4.2 数组
除了作者信息,book文档中还包含了若干个标签,这些标签可以用来表示book文档所包含的一些特征,如豆瓣读书中的标签(tag),如图3-3所示。
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_1.jpg?sign=1739282925-fR7mB8pAEGasIWrPs0pFC1Ex8bCAKcQr-0-522194eed5420f238d12f4bc42bfecab)
图3-3 豆瓣读书中的标签
我们用文档结构来表示,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_2.jpg?sign=1739282925-0264zHa6nOO1EI1NORt2y4mTsnJVZjvu-0-27086cb097f5ef4c76c15a2c17eeecc1)
1.查询元素
在查询文档时,数组中的标签会被一起返回,如果只想获得最后一个标签元素,则可以用如下命令查询:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_3.jpg?sign=1739282925-CkhGTPVefi0vhYjhON7c3XsbgxmR0U95-0-465eb11f963157a779215bdefc28315a)
这里的$silice是一个查询操作符,用于指定数组的切片方式,与JavaScript中的用法类似。
2.修改元素
如果希望在标签中的这个数组末尾添加一个元素,则可以使用$push操作符,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_4.jpg?sign=1739282925-1JwirxGowFe18NWCtyG6vjXHKeqV1TKO-0-8468f5d14f0b78fb618b7932bb811c53)
$push操作符可以配合其他操作符,一起实现不同的数组修改操作,比如和$each操作符配合可以用于添加多个元素,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_5.jpg?sign=1739282925-lzemGIacZp0WEXE24HuQIuieQpQ24zGL-0-fa86798043e2399a396b8746664cffc6)
如果加上$slice操作符,那么只会保留经过切片后的元素,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_61_6.jpg?sign=1739282925-lMrvDWKkmXwniYolgOQZ2EnuLpE2exgq-0-dbde7f2f8981c08718444328c12f10ac)
上述代码除了添加多个标签,最终只会保留最后的3个元素,即经过$slice操作后的结果。
3.根据元素查询
标签的一个重要作用就是用于查询,可以根据标签中的元素进行book文档的查找,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_62_1.jpg?sign=1739282925-QqrjiTTdb3jb6ndORtrZvLV5NF1Ms0Hs-0-fe327db276b67496628a1f3223adb7e2)
上述代码会将所有标签数组中包含“伤感”一词的book文档都查找出来。如果希望查询同时存在多个标签的文档,则可以使用$all操作符,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_62_2.jpg?sign=1739282925-RRDeN8kSrXaFCWhY0UIAyBaESao7P0hW-0-38c501a2a1bc5b9dc5e098d6caa15f01)
3.4.3 嵌套型的数组
数组元素可以是基本类型,也可以是内嵌的文档结构,我们尝试将标签的概念扩充一下,一个标签由tagKey和tagValue所组成,文档结构如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_62_3.jpg?sign=1739282925-ilw5LGiZG7o34Em8X8yFM3pHsGXG6pTH-0-56d1163924562285958e21ea076fc9d8)
这种结构非常灵活,一个很适合的场景就是商品的多属性表示,如图3-4所示。
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_62_4.jpg?sign=1739282925-iwIcoPJ7YJVg0L6wDDaMWbVJ4GdR0Vj3-0-f940317025f91d3ae134ae46c5c75b56)
图3-4 电商平台中的商品属性
一个商品可以同时包含多个维度的属性,比如尺码、颜色、风格等,使用文档可以表示为:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_63_1.jpg?sign=1739282925-mLEs7DqwYkzzY4RBWjTBkQHW2FjM9Cdz-0-b9d16134d2a90e5e57b94332cb835265)
以上的设计是一种常见的多值属性的做法,当我们需要根据属性进行检索时,需要用到$elementMatch操作符,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_63_2.jpg?sign=1739282925-S9rpKhlYujtxgjXrfcSAHI8ruF6zq4Uq-0-e4a0bc62cd979b376c1dfaf94c5f76e7)
当然,如果进行组合式的条件检索,则可以使用多个$elemMatch操作符,代码如下:
![](https://epubservercos.yuewen.com/6F449D/20118171608699906/epubprivate/OEBPS/Images/40827_63_3.jpg?sign=1739282925-3UMil6AwzwKKcMcmELSTNKqsNy9fj54X-0-aa9f7215b6002347379c0b60669863ca)
上述代码可以筛选出color=蓝色,并且size=大码的商品信息。