在mongodb存储标记中,我应该使用联合字符串还是数组

In mongodb storing tags, should I use joint string or array?

本文关键字:字符串 数组 我应该 存储 mongodb      更新时间:2023-09-26

在大多数教程中,标记将像[tag,tag,tag]一样存储。我有另一个想法,保存这样的标签:"tag.tag.tag",例如"web.javascript.angularJS",然后像这个一样查询文档

db.articles.find({'tags': /javascript/})

我想在数组中查找子字符串要比查找元素快。有人有类似的经历吗。

简介

MongoDB中的数据建模是通过首先确定需要回答的问题来完成的,并从这些问题中导出优化的数据模型。在你的情况下,你的问题似乎是

对于给定的标签,文章是什么?

为了尽可能快地回答您的查询,您需要对它们建立索引。索引基本上是键的键值存储,因为用户定义了它们以及数据文件中的文档位置。

查看索引

我们将看看如果你把标签写在一个字符串中,索引会是什么样子。假设我们有三个文档,每个文档都有三个标记,其中两个带有标准化标记"javascript"。简化了很多(实际上,索引存储在B树中),我们的索引将如下所示:

"foo.bar.baz": LocationOfDocument1;
"foo.javascript.bar": LocationOfDocument2;
"bar.javascript.baz": LocationOfDocument3;

正如你所看到的,我们在关键方面有很多冗余。这有两个问题。第一个问题是,即使找到了标记,索引仍然可能提供额外的命中率,因此我们的查询所花费的时间超过了最佳时间。第二个问题是冗余占用了宝贵的RAM。想象一下你有几十万甚至数百万的文章。

那么,如果我们使用数组来存储标签,我们的索引会是什么样子呢?

"foo":[ LocationOfDocument1, LocationOfDocument2 ];
"bar":[ LocationOfDocument2, LocationOfDocument2, LocationOfDocument3 ];
"baz":[ LocationOfDocument1, LocationOfDocument3 ];
"javascript":[ LocationOfDocument2, LocationOfDocument3 ];

还是多余的,对吧?好吧,除了一些因素:我们已经大大减少了密钥端的大小,并且与B树中相当昂贵的长字符串作为密钥相比,"LocationOfDocumentX"值的存储成本相当低。(Excursus:我认为文档位置存储为4字节整数。)因此,我们的索引可能有更多的条目,但它要紧凑得多。

此外,我们还有一个额外的优势:我们可以抛弃相当昂贵的regex。与众不同:通过使用简单的等式表达式,可以消除对索引键使用正则表达式来查找搜索字符串的成本。在shell术语中,这看起来像:

db.articles.insert({"foo":bar,tags:[tag1.toLowerCase(), tag2.toLowerCase()]})
…
db.articles.find({"tags":inputStr.toLowerCase()})

有了存储在B-树中的索引,您的搜索时间大大缩短。还有另一个优势。由于B-Trees是排序的,当我们发现正匹配时,例如在"javascript"上,我们将拥有所有带有这些标记的文档,并且索引处理可以停止。使用正则表达式搜索键,每次都必须处理索引的所有键,而且操作成本也相当高。

结论

有了存储在数组中的标签,您将加快给定标签的平均查找时间,而且肯定不会比减少为相当长的字符串的标签索引差。此外,您需要更少的RAM来存储索引,这在缩放时非常重要。

根据评论预测:是的,数据和经验也表明了这一点。

注意我不太愿意提出以下建议(因为这可能弊大于利),但在某些用例中,文本搜索索引可能是有意义的。例如,当您想在文章的标签、标题和文本上进行不区分大小写的"JavaScript"搜索时。然而,使用文本索引会带来一些超出这个答案范围的复杂性。尽管如此,你还是会把你的标签放在一个数组中。