mongodb独立集合存储分类和标签 关系是放到article表中还是新建一个关系表

推荐方案:关系存储在 Article 表中

javascript
// articles 集合
{
  _id: ObjectId("..."),
  title: "文章标题",
  content: "文章内容",
  categoryId: ObjectId("category_id"), // 引用分类ID
  tagIds: [
    ObjectId("tag_id_1"),
    ObjectId("tag_id_2"),
    ObjectId("tag_id_3")
  ], // 引用标签ID数组
  createdAt: ISODate("..."),
  updatedAt: ISODate("...")
}

// categories 集合
{
  _id: ObjectId("..."),
  name: "技术文章",
  slug: "tech",
  description: "技术相关文章",
  articleCount: 15, // 该分类下的文章数量
  createdAt: ISODate("...")
}

// tags 集合
{
  _id: ObjectId("..."),
  name: "JavaScript",
  slug: "javascript",
  color: "#f7df1e",
  articleCount: 8, // 使用该标签的文章数量
  createdAt: ISODate("...")
}

为什么选择这种方案?

优点:

  1. 查询性能高:获取文章时直接拿到所有关系ID

  2. 结构简单:不需要维护额外的关系表

  3. 符合 MongoDB 设计哲学:文档数据库适合嵌入/引用关系

  4. 易于维护:添加/删除标签只需更新文章的 tagIds 数组

查询示例:

获取文章及其分类和标签信息:

javascript
// 使用聚合查询
db.articles.aggregate([
  {
    $match: { _id: ObjectId("文章ID") }
  },
  {
    $lookup: {
      from: "categories",
      localField: "categoryId",
      foreignField: "_id",
      as: "category"
    }
  },
  {
    $lookup: {
      from: "tags",
      localField: "tagIds",
      foreignField: "_id",
      as: "tags"
    }
  },
  {
    $project: {
      title: 1,
      content: 1,
      category: { $arrayElemAt: ["$category", 0] },
      tags: 1,
      createdAt: 1
    }
  }
])

获取某个分类下的所有文章:

javascript
db.articles.find({ categoryId: ObjectId("分类ID") })

获取包含某个标签的所有文章:

javascript
db.articles.find({ tagIds: ObjectId("标签ID") })

为什么不推荐单独的关系表?

在 MongoDB 中创建单独的关系表(如 article_tags)会增加复杂性:

  1. 额外的查询开销:需要多次查询才能获取完整信息

  2. 维护复杂:需要同时更新多个集合

  3. 事务问题:在分布式环境中维护数据一致性更困难

前端处理示例

javascript
// 创建文章时
const createArticle = async (articleData) => {
  const article = {
    title: articleData.title,
    content: articleData.content,
    categoryId: articleData.categoryId, // 分类ID
    tagIds: articleData.tagIds, // 标签ID数组
    createdAt: new Date()
  }
  
  return await api.createArticle(article)
}

// 获取文章详情时
const getArticleDetail = async (articleId) => {
  // 可以一次查询获取完整信息,或者分开查询
  
  // 方式1:后端聚合查询返回完整数据
  const article = await api.getArticleWithRelations(articleId)
  
  // 方式2:分别查询然后组合
  const [article, categories, tags] = await Promise.all([
    api.getArticle(articleId),
    api.getCategories(),
    api.getTags()
  ])
  
  return {
    ...article,
    category: categories.find(c => c._id === article.categoryId),
    tags: tags.filter(t => article.tagIds.includes(t._id))
  }
}

索引优化

为了提升查询性能,记得创建索引:

javascript
// 在 articles 集合上创建索引
db.articles.createIndex({ categoryId: 1 })
db.articles.createIndex({ tagIds: 1 })
db.articles.createIndex({ createdAt: -1 })

// 在 categories 和 tags 集合上创建索引
db.categories.createIndex({ slug: 1 }, { unique: true })
db.tags.createIndex({ slug: 1 }, { unique: true })

总结:在 MongoDB 中,将关系直接存储在文章表中是最简单、最高效的方案,符合文档数据库的设计理念。