情景说明
假设现在要做一个博客系统, 用到两张表,一张是 分类表,一张是文章表
-
分类表的定义 , 一个分类能有多个文章
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 方法啊 等等