Nosql / mongoose——设计[重复]

Posted

技术标签:

【中文标题】Nosql / mongoose——设计[重复]【英文标题】:Nosql / mongoose -- design [duplicate] 【发布时间】:2018-11-11 15:31:01 【问题描述】:

我有一个关于撰写文章的个人项目。 我用猫鼬在nosql中做。对于我的 API,我使用 nodejs + express。 但是我精通sql,但我是Nosql的初学者...

我的问题是关于我的分类文章。

我想设置 2 级这样的类别:

第 1 类

子类别 1 文章 子类别 2 文章 子类别 3 文章

第 2 类

子类别 4 文章 子类别 5 文章 子类别 6 文章

然后我的文章被填充到子类别中。

我开始设置3个文件:

类别 子类别 文章

像这样,我可以在子类别中填充我的文章,并在类别中填充我的子类别。

但我不确定这是否是使用 mongoose 和 nosql 的最佳方式。我需要另一种观点。

我显然希望尊重最佳做法。

简单来说,我需要:

显示子类别中的所有文章 显示分类中的所有文章 在一篇文章的页面上显示类别和子类别。

我也允许只使用现有的类别。

有什么建议吗?

非常感谢;)

【问题讨论】:

我认为你建议的三个系列是一个好的开始。目前,它涵盖了您的要求并提供了足够的灵活性。只需实现它,当新需求不适合您的模型时,很容易更改 mongodb 模型。 【参考方案1】:

作为面向文档的数据库的一般规则,您应该根据访问来构建文档。

如果您想拥有一个呈现包含类别和子类别名称的完整文章的页面,您可以像这样构建文章:


    "_id": ObjectId("5b0f9961076337823360d072"),
    "category": "Gadgets",
    "subcategory": "TVs",
    "title": "New TV from ACME",
    "text": "Lorem Ipsum ....",
    "published": ISODate("2018-05-31T06:42:41.270Z")

通过这种方式,您可以通过查询获取文章页面的所有数据。

如果您想显示一个类别和子类别的所有文章,您可以过滤这些字段并且只需要一个查询。在这种情况下,您应该在这些字段上创建索引以加快查询速度。

您可以(但这取决于您希望如何访问您的类别)将您的类别和子类别保存在一个集合中。这些文档可以这样构造:


    "_id": ObjectId("5b0f9961076337823360d072"),
    "name": "Gadgets",
    "subcategories": ["TVs", "Computers", "Phones"]

但也许这种结构不符合您的需求。在这种情况下,您可以有两个集合。它始终取决于您的应用程序的实现。

您会注意到,数据结构中存在冗余。类别名称保存多篇文章。但这没关系。您必须在保存之前通过验证输入来确保在您的应用程序中保存的数据是正确的。

冗余证明了另一个问题:如果要重命名类别,则必须更新具有已更改名称的类别的每个文档。但这些更新通常不会经常发生。文档设计是一种权衡。快速获取,因为您只访问一个集合甚至一个文档。繁琐而缓慢的更新。但是通过良好的文档结构,您可以最大限度地减少慢速操作并最大化快速操作。

【讨论】:

简单来说,我需要: - 显示子类别中的所有文章 - 显示类别中的所有文章 - 在一篇文章的页面上显示类别和子类别。您的第一个解决方案可能很好,但这意味着我们可以从类别中找到所有文章,但是对于具有不同拼写的同一类别(如 TV、TV、TVs)也存在一些问题……我只允许使用现有类别。在我的 api 中,我想检查类别和子类别是否存在,然后使用它。顺便说一句,我可以在文章模型中找到类别...我不知道最好的方法.. :( 是的,拼写错误确实会造成问题。您必须确保您的数据已正确保存。对于类别名称,您可以向作者显示一个选择框。此外,您必须在保存之前验证所有值。我会扩展我的答案。 @KiloumapL'artélon 缺乏一致性是使用面向文档的数据库的主要权衡。这意味着您的应用程序应该能够处理这些错误:在您的查询中,使用正则表达式;要查找的单词词典;不区分大小写的表达式...这可能违反直觉,但 noSQL 基本上抛弃了 ACID 属性,以支持(理论上)更有效的查询和水平扩展。【参考方案2】:

正如我个人所学到的,与关系相反,NoSQL 的主要目的最初是尽可能避免将表相互连接。这通常是 数据复制(ACID 属性)和易于更新 VS 之间的权衡。查询效率

实际上,这包括将一个文档嵌套在另一个文档中。例如,每个Article 将包含它所属的Category 数据(但这可以根据您的需要以相反的方式实现)。

Article name: string, content: string, category: Category  name: ...

以下只是我个人的看法;但在这件事上,我不是猫鼬的忠实粉丝。 它确实增加了一个抽象层,这确实让来自关系世界的开发人员放心,但如果不小心使用,您可能很容易回到使用不是为此而设计的工具来做关系。

编辑:上面的 mbuechmann 更好的例子。

【讨论】:

【参考方案3】:

我认为您可以采用这种方法。将有2个集合

    文章 类别

类别集合将具有例如以下类型的文档。


    "_id" : ObjectId("5a7030519697334c17afe3e6"),
    "parent_id" : ObjectId("5a7030469697334c17afe3e5"),
    "name" : "cat 1",
    "is_subtype" : true,
    "sub_types" : [
        ObjectId("5a7030969697334c17afe3e9"),
        ObjectId("5a70309c9697334c17afe3ea")
    ],

您现在可以在此处使用其 ID 填充父类别及其子类别。同样在文章中,您可以按类别 id 填充类别信息。

并且文章会有以下类型的文档


  "_id": ObjectId("5a7030519697334c17afe3e6"),
  "category_id": ObjectId("5a7030469697334c17afe3e5"),
  "text": "article 1",
  "slug": "article-1"

我希望这就是你要找的。​​p>

【讨论】:

这完全像一个关系数据库设计。这不会让您利用基于文档的设计的优势。要显示一篇文章加上类别名称和子类别名称,需要三个查询。并且这些查询不能并行完成,因为您必须获取文章、读出类别 ID、获取类别等等...... 你错了。您可以在单个查询中获取所有三个信息。由于我们使用的是猫鼬,我们可以从类别表中填充类别,也可以从类别集合的类别键中填充子类别。您不需要编写三个不同的查询。这是正确的声明,因为如果有人想显示类别列表,那么您将需要单独的唯一 ID。如果不同的子类别,您可以使用相同的名称,因此它需要唯一的 Id 来执行操作。 这可能只是一个猫鼬调用,但它不是数据库级别的一个查询。一次调用无法查询多个集合。 如果您在 Mongo DB 中执行查询,那么您是对的。但这就是 mongoose 的用武之地。它允许您在单个查询中填充多个集合数据。如果您在谈论应用程序级编程,那么这种方式是正确的。 在我看来,您混淆了一些概念。 to query 表示向数据库发送请求。如果您正在调用 mongoose 函数,则它是 a function call。尽管您只执行一个 mongoose 函数调用,但 mongoose 在内部会执行多个数据库查询。那是低效的,意味着次优的文档设计。

以上是关于Nosql / mongoose——设计[重复]的主要内容,如果未能解决你的问题,请参考以下文章

mongoose基本操作

Mongoose:如何避免重复文件?

MongoDB 中的关联查询MongoDB : aggregate/lookup 对比 Mongoose : ref / populate

Mongoose 用户、角色和权限

Mongoose:find() 忽略重复值

Mongoose 添加到 MongoDB 的基本功能?