Nodejs mongoDB新手问多对多关系的文档设计
Nodejs mongoDB新手问多对多关系的文档设计
我看了cnodejs的源码,里面话题是topic,源码里面还有tag,对于一个话题,发布者可以设置多个tag。而一个tag里面也会有许多topic。这个是典型的多对多关系。 cndoejs的设计是这样的:有三个表,一个topic,一个tag,一个topic_tag。这个思路和关系型数据库是一样的。 我另外有一种办法。就是topic里面有一个tags数组,里面存放了tag的id,然后tag里面有一个topic数组,里面存放了topic的id。请问这样的设计好不好?会不会有什么问题?
当然可以!让我们来详细探讨一下在MongoDB中如何处理多对多关系的设计,并且比较这两种不同的方法。
方法一:使用中间集合
这是CNodeJS采用的方法,通过引入一个新的集合来表示两个实体之间的关联。这种方法类似于关系型数据库中的中间表(junction table)。
示例代码:
Topic 集合
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4c"),
"title": "Node.js 教程",
"description": "学习 Node.js 的基础",
"author": "John Doe",
"tags": [
ObjectId("5f3e4d2a7b8c9d0e1f2a3b4d"), // Tag ID
ObjectId("5f3e4d2a7b8c9d0e1f2a3b4e") // Another Tag ID
]
}
Tag 集合
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4d"),
"name": "Node.js",
"description": "关于 Node.js 的一切"
}
Topic_Tag 中间集合
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4f"),
"topicId": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4c"),
"tagId": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4d")
}
方法二:嵌入引用
这种方法是在Topic
和Tag
集合中分别存储对方的引用。
示例代码:
Topic 集合
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4c"),
"title": "Node.js 教程",
"description": "学习 Node.js 的基础",
"author": "John Doe",
"tags": [
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4d"),
"name": "Node.js",
"description": "关于 Node.js 的一切"
},
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4e"),
"name": "JavaScript",
"description": "关于 JavaScript 的一切"
}
]
}
Tag 集合
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4d"),
"name": "Node.js",
"description": "关于 Node.js 的一切",
"topics": [
{
"_id": ObjectId("5f3e4d2a7b8c9d0e1f2a3b4c"),
"title": "Node.js 教程",
"description": "学习 Node.js 的基础",
"author": "John Doe"
}
]
}
比较与结论
优点:
- 中间集合:查询更高效,因为可以通过索引优化性能。
- 嵌入引用:数据一致性更好,读取时不需要额外的查询。
缺点:
- 中间集合:需要维护更多的集合,增加了复杂性。
- 嵌入引用:可能导致数据冗余,更新时需要小心处理。
根据你的具体需求,选择合适的方法。如果数据量较大,推荐使用中间集合;如果数据量较小且需要保持简单,可以选择嵌入引用的方式。
范式 和 反范式得 区别, 对于经常改动得东西,用范式, 不怎么改动,而且经常需要搜索得结果,一般用反范式,对于 一个tag 里面 存放多个tagid 是一个极度 不理智得行为,要想一下,你生成一个主题,需要遍历10篇文章里面得tagid,然后根据tagid找名字,菜都凉了,tag 一般都是一次生成,后面基本不怎么改动,如果需要搜索 也是 很简单得, tags: [tagName1, tagName2] db.collection.find({tags: tagName1}) mongodb 支持数组搜索。当然这样做也是有缺点得,tag作为数组,索引得空间会比较大,注意数组与数组之间做索引
感谢回复。我不是一个tag 里面 存放多个tagid。是这样的: topic: { tags: [tag_id1, tag_id2] }
tag: { topics: [topic_id1, topic_id2] }
等于是把topic_tag这个表合到两个表里面去了。而不是另外有一个表叫topic_tag
tag里面不应该存topic,如果想查找tag对应的topic列表可通过条件查找来topic实现
明白了,非常感谢!
在处理多对多关系时,常见的做法是在关系型数据库中使用第三个关联表来连接两个实体。而在MongoDB这种非关系型数据库中,处理多对多关系有多种方式。你提到的两种方法都有其优缺点,下面是针对这两种方法的分析:
方法一:使用关联表
这种方法类似于传统的关系型数据库设计。你提到的 topic
、tag
和 topic_tag
表分别表示话题、标签和它们之间的关联。以下是简单的文档设计示例:
// topic 文档
{
_id: ObjectId("6471e03a9577e83c524a9494"),
title: "Node.js 入门",
tags: [ObjectId("6471e03a9577e83c524a9495"), ObjectId("6471e03a9577e83c524a9496")]
}
// tag 文档
{
_id: ObjectId("6471e03a9577e83c524a9495"),
name: "Node.js"
}
// topic_tag 文档
{
topicId: ObjectId("6471e03a9577e83c524a9494"),
tagId: ObjectId("6471e03a9577e83c524a9495")
}
方法二:直接引用对方的ID
这种方法将所有标签ID直接存储在话题文档中,并且在每个标签文档中存储相关话题的ID。以下是示例:
// topic 文档
{
_id: ObjectId("6471e03a9577e83c524a9494"),
title: "Node.js 入门",
tags: [ObjectId("6471e03a9577e83c524a9495"), ObjectId("6471e03a9577e83c524a9496")]
}
// tag 文档
{
_id: ObjectId("6471e03a9577e83c524a9495"),
name: "Node.js",
topics: [ObjectId("6471e03a9577e83c524a9494")]
}
分析与建议
-
关联表(方法一)的优点在于易于管理和查询。例如,你可以轻松地查询某个标签下的所有话题,或者某个话题下有哪些标签。缺点是需要额外维护一个关联表,可能会增加复杂度。
-
直接引用(方法二)的优点在于简单,查询速度快。缺点是更新或删除标签/话题时需要确保所有相关的引用都正确更新,否则容易出现数据不一致。
总的来说,如果你的数据更新频率较低,推荐使用方法二;如果需要频繁操作或查询,建议使用方法一。实际应用中可以根据具体需求选择合适的方法。