Mongoose 两张表相互关联

情景说明

假设现在要做一个博客系统, 用到两张表,一张是 分类表,一张是文章表

  • 分类表的定义 , 一个分类能有多个文章

    const mongoose = require("mongoose")
    const Schema = mongoose.Schema
    const Category = mongoose.model("Category", new Schema({ name: String }))
    
  • 文章表的定义

    const mongoose = require("mongoose")
    const Schema = mongoose.Schema
    const Post = mongoose.model(
      "Post",
      new Schema({
        title: String,
        content: String,
      })
    )
    

    现在这两张表没有任何 联系, 也就是说分类表里没有文章的外键,那么我们如何将文章和分类关联起来呢?

一、通过文章查询分类

我们可以给文章表增加一个分类的外键

const mongoose = require("mongoose")
const Schema = mongoose.Schema
const Post = mongoose.model(
  "Post",
  new Schema({
    title: String,
    content: String,
    category: { type: Schema.Types.ObjectId, ref: "Category" },
  })
)

其中 category 字段是一个对象, 里面有一个 type 属性, 值为 Schema.Types.ObjectId, 这个属性表示这个字段是一个 ObjectId 类型, 还有一个 ref 属性, 值为 Category, 这个属性表示这个 ObjectId 类型对应的是 Category 表的 _id 字段。

这样, 我们就将文章和分类关联起来了。

现在表是关联起来了,我们尝试一下查询

// 插入分类数据
await category.create({ name: "前端" })
await category.create({ name: "后端" })
console.log(await category.find())

查询分类数据, 得到如下结果

;[
  { _id: new ObjectId("676953bdeee144f62bc43841"), name: "前端", __v: 0 },
  { _id: new ObjectId("676953bdeee144f62bc43844"), name: "后端", __v: 0 },
]

说明我们插入数据成功了,接下来插入文章数据

// 插入文章数据
await Post.create({
  title: "Vue",
  content: "Vue是一套用于构建用户界面的渐进式框架",
})
await Post.create({
  title: "React",
  content: "React 是一个用于构建用户界面的 JavaScript 。",
})
console.log(await Post.find())

查询文章数据, 得到如下结果

;[
  {
    _id: new ObjectId("67695485fdcb80ec7fedc6ac"),
    title: "Vue",
    content: "Vue是一套用于构建用户界面的渐进式框架",
    __v: 0,
  },
  {
    _id: new ObjectId("67695485fdcb80ec7fedc6af"),
    title: "React",
    content:
      "React 是一个用于构建用户界面的 JavaScript 库,它被设计为可以适应不同的开发环境和设计理念。",
    __v: 0,
  },
]

说明我们文章也插入成功了,但是有个问题,我们明明在上面定义了一个 category 字段,为什么文章表里没有 category 字段? 那是因为我们只是定义了一个外键, 并没有在文章表里创建这个字段。

现在我们将文章表里的 category 字段创建出来,并将外键关联到分类表

const cat = await Category.findOne({ name: "前端" })
const post = await Post.findOne({ title: "Vue" })
post.category = cat._id
await post.save()
console.log(await Post.find())

得到如下结果

;[
  {
    _id: new ObjectId("67695485fdcb80ec7fedc6ac"),
    title: "Vue",
    content: "Vue是一套用于构建用户界面的渐进式框架,",
    __v: 0,
    category: new ObjectId("676953bdeee144f62bc43841"),
  },
]

到这里,我们就完成了文章和分类的关联。 但是还有个问题,我不想它显示 ID 我希望它能完整的显示出来 这个是什么分类?那应该怎么做呢?
很简单 只需要在查询的时候加上 populate 方法就可以了

const cat = await Category.findOne({ name: "前端" })
const post = await Post.findOne({ title: "Vue" })
post.category = cat._id
await post.save()
console.log(await Post.find().populate("category"))

执行后 就得到如下结果

;[
  {
    _id: new ObjectId("67695485fdcb80ec7fedc6ac"),
    title: "Vue",
    content: "Vue是一套用于构建用户界面的渐进式框架",
    __v: 0,
    category: {
      _id: new ObjectId("676953bdeee144f62bc43841"),
      name: "前端",
      __v: 0,
    },
  },
]

这样就能完整的显示出分类名称了。但是 还有种情况,假设说我一篇文字有多个分类,那我们应该怎么做呢?其实也很简单,只需要将 category 字段定义成数组类型就可以了。

const mongoose = require("mongoose")
const Schema = mongoose.Schema
const Post = mongoose.model(
  "Post",
  new Schema({
    title: String,
    content: String,
    category: [{ type: Schema.Types.ObjectId, ref: "Category" }],
  })
)

这样就可以将文章和多个分类关联起来了。

const cat1 = await Category.findOne({ name: "前端" })
const cat2 = await Category.findOne({ name: "后端" })
const post = await Post.findOne({ title: "Vue" })
post.category = [cat1._id, cat2._id]
await post.save()
console.log(await Post.find().populate("category"))

二、通过分类查询文章

现在我们已经将文章和分类关联起来了, 但是我们还需要一个方法, 能根据分类查询文章。

我们先看一下分类表的定义

const mongoose = require("mongoose")
const Schema = mongoose.Schema
const Category = mongoose.model("Category", new Schema({ name: String }))

现在的分类列表里面是没有 Post 外键的,但是我们又需要一个 那怎么办呢?
解决办法就是通过Schema 的 virtual 属性来实现。

现在我们需要将上面的代码改动一下

const mongoose = require("mongoose")
const CategorySchema = new mongoose.Schema(
  { name: String },
  { toJSON: { virtuals: true } }
)
CategorySchema.virtual("posts", {
  // 引用的是那个模型
  ref: "Post",
  // 本地(CategorySchema)那个字段关联去关联外部的那个字段
  localField: "_id",
  // 表示的是Post表的那个字段
  foreignField: "category",
  justOne: false,
})
const Category = mongoose.model("Category", CategorySchema)

这里我们定义了一个 virtual 属性, 名字叫 posts, 他有一个 ref 属性,表示引用的是哪张表, 一个 localField 属性,表示当前表的哪个字段, 一个 foreignField 属性,表示那张表的哪个字段, 还有一个 justOne 属性,表示是否只返回一个结果。

接下来我们来查询一下

const cat = await Category.find().populate("posts")
console.log(JSON.stringify(cat))

得到如下结果

;[
  {
    _id: "676953bdeee144f62bc43841",
    name: "前端",
    __v: 0,
    posts: [
      {
        _id: "67695485fdcb80ec7fedc6ac",
        title: "Vue",
        content: "Vue是一套用于构建用户界面的渐进式框架",
        __v: 0,
        category: "676953bdeee144f62bc43841",
      },
    ],
    id: "676953bdeee144f62bc43841",
  },
  {
    _id: "676953bdeee144f62bc43844",
    name: "后端",
    __v: 0,
    posts: [],
    id: "676953bdeee144f62bc43844",
  },
]

至此,大功告成,关于通过分类查询文章 还有很多细节没说,比如 为什么 最后要使用JSON.stringify(cat)啊? 还有 我在查询的时候 为什么加了 populate 方法啊 等等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值