在大多数教程中,标签将像[tag,tag,tag]一样存储。我还想到了这样保存标签:" tag.tag.tag" ,例如" web.javascript.angularJS",然后像这样查询文档
db.articles.find({'tags': /javascript/})
我认为查找子字符串比数组中的元素更快。有没有人有类似的经历。
答案 0 :(得分:2)
MongoDB中的数据建模是通过首先确定需要回答的问题并从这些问题中推导出优化数据模型来完成的。在您的情况下,您的问题似乎是
对于给定的标签,文章是什么?
为了尽快回答您的问题,您需要在其上添加索引。索引基本上是键的键值存储,因为用户在数据文件中定义它们和文档位置。
如果您在单个字符串中编写标记,我们将看看索引的外观。假设我们有三个文档,每个文档有三个标记,其中两个标记为" javascript"的标准化标记。简化了很多(实际上,索引存储在B-trees中),我们的索引将如下所示:
"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 ];
仍然多余,对吗?好吧,除了一些因素:我们已经大大减少了关键方面的尺寸和" LocationOfDocumentX"与相当昂贵的冗长字符串作为B树中的键相比,值的存储相当便宜。 ( Excursus :我认为文档位置存储为4字节整数。) 所以我们的索引可能有更多的条目,但它更紧凑。
另外,我们还有一个额外的优势:我们可以放弃相当昂贵的正则表达式。换句话说:您可以通过使用简单的相等表达式来消除使用正则表达式对索引键进行查找的搜索字符串的成本。在shell术语中,这看起来像:
db.articles.insert({"foo":bar,tags:[tag1.toLowerCase(), tag2.toLowerCase()]})
…
db.articles.find({"tags":inputStr.toLowerCase()})
索引存储在B-Tree中,您的搜索时间会大大减少。还有另一个优点。由于B树被排序,当我们找到正匹配时,例如,在" javascript"上,我们将使用这些标记所有文档,并且索引处理可以停止。通过在键上进行正则表达式搜索,索引的所有键都必须每次都被处理 - 并且操作也相当昂贵。
使用存储在数组中的标记,您将加快给定标记的平均查找时间,并且肯定不会比标记上的索引减少到相当长的字符串更糟糕。此外,您需要较少的RAM来存储索引,这在缩放时非常重要。
预期相应的评论:是的,这也是数据和经验所显示的内容。
注意强> 我在提出以下建议时犹豫不决(因为这可能弊大于利),但有些用例text search index可能有意义。例如,当您想对" JavaScript"进行不区分大小写的搜索时两者都在文章的标签,标题和文字上。但是,使用文本索引会带来一些复杂性,这些复杂性超出了本答案的范围。而且,你会把你的标签放在一个数组中。