猫鼬更新相互关系
Mongoose Updating mutual relationships
我正在使用 2 个模型:
产品和活动。
产品 模型引用了事件 (_id)
事件 模型引用了产品 (_id)。
到目前为止,我发现如果我想更新事件并添加或退出产品,我还必须使用另一个操作来更新产品模型。删除也是如此(如果我删除了一个事件,我必须更新与该事件关联的产品并将其删除)。
我的问题是,例如:
我有一个具有 event_id:[1,2,3,4}] 的产品 (id 1)
我的事件 (id 1) 有 product_id: [1,2].
如果我更新事件 (id 1) 并取出产品 1,事件现在将只有产品 id 2,但产品仍将具有事件 id 1。
为此,我正在使用钩子:为了更新,我正在这样做:
EventSchema.post("findOneAndUpdate", function(doc) {
Product.updateMany({
_id: {
$in: doc.product
}
}, {
event: doc._id.toString()
}, {
multi: true
}, function(error, product) {
if (error) console.log(error)
})
})
如何更新产品模型,取出 event_id 列数组中的 ID?我希望我的解释清楚。
事件 SHCEMA
const EventSchema = new Schema({
client: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Client'
}]
},
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
},
date: {
type: Date,
maxlength: 64,
lowercase: true,
trim: true
},
place: {
type: String,
maxlength: 1200,
minlength: 1,
},
price: {
type: Number
},
comment: {
type: String,
maxlength: 12000,
minlength: 1,
},
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
},
);
客户端架构
const ClientSchema = new Schema(
{
first_name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Client first name is required"]
},
last_name: {
type: String,
maxlength: 64,
minlength: 1
},
address: {
type: String,
maxlength: 1200,
minlength: 1
},
phone: {
type: String,
maxlength: 64,
minlength: 1
},
email: {
type: String,
maxlength: 64,
lowercase: true,
trim: true
},
web: {
type: String,
maxlength: 1200,
minlength: 1
},
comment: {
type: String,
maxlength: 12000,
minlength: 1
},
event: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
产品架构:
const ProductSchema = new Schema(
{
name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Product name is required"]
},
description: {
type: String,
maxlength: 12000,
minlength: 1
},
comment: {
type: String,
maxlength: 12000,
minlength: 1
},
state: {
type: String,
maxlength: 64,
minlength: 0
},
available: {
type: Boolean,
default: true
},
price: {
type: Number
},
category: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Category"
}
]
},
event: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
image: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Image"
}
]
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
输入数据:产品/:id 路线
{
"available": false,
"category": [
"5e04cd609b1c6812c8f9a9d1"
],
"event": [
"5e07f73d72503f6659f40b26"
],
"image": [
"5e05f9dd66432544b7bf0221"
],
"_id": "5e05f9d166432544b7bf0220",
"name": "Mesa ratona",
"price": 1200,
"comment": "-",
"description": "-",
"__v": 0,
"modelName": "Product",
"id": "5e05f9d166432544b7bf0220"
}
输入数据事件/:id
{
"client": [
"5e05f743cd57804386a92a89"
],
"product": [
"5e05f9d166432544b7bf0220",
"5e06464b811a954e831eafcf",
"5e064c5c811a954e831eafd3"
],
"_id": "5e07f73d72503f6659f40b26",
"date": "2020-01-12T00:45:33.069Z",
"place": "EVENT 1",
"price": 123,
"comment": "EVENT 1",
"__v": 0,
"id": "5e07f73d72503f6659f40b26"
}
输入数据客户端/:id
{
"event": [
"5e07f73d72503f6659f40b26"
],
"_id": "5e05f743cd57804386a92a89",
"first_name": "Ernesto",
"last_name": "De Lucía",
"address": "Calle Santa Fé 3129 Piso 5 Depto \"B\" Capital Federal",
"email": "ernestodl@gmail.com",
"phone": "+54 011 1567432984",
"web": "-",
"comment": "-",
"__v": 0,
"full_name": "Ernesto De Lucía",
"id": "5e05f743cd57804386a92a89"
}
谢谢!
在您的架构中有很多引用,因此当需要删除一个项目时,需要从所有引用的文档中删除它。
我建议您使用 virtual populate.
删除那么多对多的引用
我简化了架构以仅包含必填字段。
事件:(我删除了对产品和客户的引用,并使用虚拟填充)
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const EventSchema = new Schema(
{
name: {
type: String,
required: true
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
// Virtual populate
EventSchema.virtual("clients", {
ref: "Client",
foreignField: "events",
localField: "_id"
});
EventSchema.virtual("products", {
ref: "Product",
foreignField: "events",
localField: "_id"
});
module.exports = mongoose.model("Event", EventSchema);
客户:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const ClientSchema = new Schema(
{
first_name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Client first name is required"]
},
events: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
module.exports = mongoose.model("Client", ClientSchema);
产品:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const ProductSchema = new Schema(
{
name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Product name is required"]
},
events: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
module.exports = mongoose.model("Product", ProductSchema);
我有这些示例文档:
事件:
{
"_id": "5e08ceb5a62f094ba4ca2542",
"name": "Event 1",
"__v": 0,
"id": "5e08ceb5a62f094ba4ca2542"
}
{
"_id": "5e08cec5a62f094ba4ca2543",
"name": "Event 2",
"__v": 0,
"id": "5e08cec5a62f094ba4ca2543"
}
产品:
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
}
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfa9e9dfcc0e1431c90c",
"name": "Product 2",
"__v": 0,
"id": "5e08cfa9e9dfcc0e1431c90c"
}
客户:
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cfdce9dfcc0e1431c90e",
"first_name": "Client 1",
"__v": 0,
"id": "5e08cfdce9dfcc0e1431c90e"
}
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfece9dfcc0e1431c90f",
"first_name": "Client 2",
"__v": 0,
"id": "5e08cfece9dfcc0e1431c90f"
}
让我们首先解决从事件访问客户和产品的问题,因为我们删除了该引用。
我们可以使用以下路径来获取虚拟填充的客户和产品的事件:
router.get("/events/:id", async (req, res) => {
const result = await Event.findById(req.params.id)
.populate("clients")
.populate("products");
res.send(result);
});
响应将是这样的:(如您所见,即使我们删除了引用,我们也可以使用虚拟填充访问客户端和产品)
{
"_id": "5e08ceb5a62f094ba4ca2542",
"name": "Event 1",
"__v": 0,
"clients": [
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cfdce9dfcc0e1431c90e",
"first_name": "Client 1",
"__v": 0,
"id": "5e08cfdce9dfcc0e1431c90e"
},
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfece9dfcc0e1431c90f",
"first_name": "Client 2",
"__v": 0,
"id": "5e08cfece9dfcc0e1431c90f"
}
],
"products": [
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
},
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfa9e9dfcc0e1431c90c",
"name": "Product 2",
"__v": 0,
"id": "5e08cfa9e9dfcc0e1431c90c"
}
],
"id": "5e08ceb5a62f094ba4ca2542"
}
现在要从产品或客户中删除事件,我们只需从那里删除即可。
例如要从产品中删除一个事件,我们只需要从产品中删除它:
router.delete("/products/:id/:eventId", async (req, res) => {
const result = await Product.findByIdAndUpdate(
req.params.id,
{
$pull: {
events: req.params.eventId
}
},
{ new: true }
);
res.send(result);
});
在此删除之前,此产品有 2 个 ID 为 5e08ceb5a62f094ba4ca2542
和 5e08cec5a62f094ba4ca2543
的事件。
我们删除后它只会有一个id为5e08ceb5a62f094ba4ca2542
:
的事件
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
}
我正在使用 2 个模型:
产品和活动。
产品 模型引用了事件 (_id) 事件 模型引用了产品 (_id)。
到目前为止,我发现如果我想更新事件并添加或退出产品,我还必须使用另一个操作来更新产品模型。删除也是如此(如果我删除了一个事件,我必须更新与该事件关联的产品并将其删除)。
我的问题是,例如: 我有一个具有 event_id:[1,2,3,4}] 的产品 (id 1) 我的事件 (id 1) 有 product_id: [1,2].
如果我更新事件 (id 1) 并取出产品 1,事件现在将只有产品 id 2,但产品仍将具有事件 id 1。
为此,我正在使用钩子:为了更新,我正在这样做:
EventSchema.post("findOneAndUpdate", function(doc) {
Product.updateMany({
_id: {
$in: doc.product
}
}, {
event: doc._id.toString()
}, {
multi: true
}, function(error, product) {
if (error) console.log(error)
})
})
如何更新产品模型,取出 event_id 列数组中的 ID?我希望我的解释清楚。
事件 SHCEMA
const EventSchema = new Schema({
client: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Client'
}]
},
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
},
date: {
type: Date,
maxlength: 64,
lowercase: true,
trim: true
},
place: {
type: String,
maxlength: 1200,
minlength: 1,
},
price: {
type: Number
},
comment: {
type: String,
maxlength: 12000,
minlength: 1,
},
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
},
);
客户端架构
const ClientSchema = new Schema(
{
first_name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Client first name is required"]
},
last_name: {
type: String,
maxlength: 64,
minlength: 1
},
address: {
type: String,
maxlength: 1200,
minlength: 1
},
phone: {
type: String,
maxlength: 64,
minlength: 1
},
email: {
type: String,
maxlength: 64,
lowercase: true,
trim: true
},
web: {
type: String,
maxlength: 1200,
minlength: 1
},
comment: {
type: String,
maxlength: 12000,
minlength: 1
},
event: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
产品架构:
const ProductSchema = new Schema(
{
name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Product name is required"]
},
description: {
type: String,
maxlength: 12000,
minlength: 1
},
comment: {
type: String,
maxlength: 12000,
minlength: 1
},
state: {
type: String,
maxlength: 64,
minlength: 0
},
available: {
type: Boolean,
default: true
},
price: {
type: Number
},
category: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Category"
}
]
},
event: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
image: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Image"
}
]
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
输入数据:产品/:id 路线
{
"available": false,
"category": [
"5e04cd609b1c6812c8f9a9d1"
],
"event": [
"5e07f73d72503f6659f40b26"
],
"image": [
"5e05f9dd66432544b7bf0221"
],
"_id": "5e05f9d166432544b7bf0220",
"name": "Mesa ratona",
"price": 1200,
"comment": "-",
"description": "-",
"__v": 0,
"modelName": "Product",
"id": "5e05f9d166432544b7bf0220"
}
输入数据事件/:id
{
"client": [
"5e05f743cd57804386a92a89"
],
"product": [
"5e05f9d166432544b7bf0220",
"5e06464b811a954e831eafcf",
"5e064c5c811a954e831eafd3"
],
"_id": "5e07f73d72503f6659f40b26",
"date": "2020-01-12T00:45:33.069Z",
"place": "EVENT 1",
"price": 123,
"comment": "EVENT 1",
"__v": 0,
"id": "5e07f73d72503f6659f40b26"
}
输入数据客户端/:id
{
"event": [
"5e07f73d72503f6659f40b26"
],
"_id": "5e05f743cd57804386a92a89",
"first_name": "Ernesto",
"last_name": "De Lucía",
"address": "Calle Santa Fé 3129 Piso 5 Depto \"B\" Capital Federal",
"email": "ernestodl@gmail.com",
"phone": "+54 011 1567432984",
"web": "-",
"comment": "-",
"__v": 0,
"full_name": "Ernesto De Lucía",
"id": "5e05f743cd57804386a92a89"
}
谢谢!
在您的架构中有很多引用,因此当需要删除一个项目时,需要从所有引用的文档中删除它。
我建议您使用 virtual populate.
删除那么多对多的引用我简化了架构以仅包含必填字段。
事件:(我删除了对产品和客户的引用,并使用虚拟填充)
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const EventSchema = new Schema(
{
name: {
type: String,
required: true
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
// Virtual populate
EventSchema.virtual("clients", {
ref: "Client",
foreignField: "events",
localField: "_id"
});
EventSchema.virtual("products", {
ref: "Product",
foreignField: "events",
localField: "_id"
});
module.exports = mongoose.model("Event", EventSchema);
客户:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const ClientSchema = new Schema(
{
first_name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Client first name is required"]
},
events: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
module.exports = mongoose.model("Client", ClientSchema);
产品:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const ProductSchema = new Schema(
{
name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, "Product name is required"]
},
events: [
{
type: Schema.Types.ObjectId,
ref: "Event"
}
]
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
}
);
module.exports = mongoose.model("Product", ProductSchema);
我有这些示例文档:
事件:
{
"_id": "5e08ceb5a62f094ba4ca2542",
"name": "Event 1",
"__v": 0,
"id": "5e08ceb5a62f094ba4ca2542"
}
{
"_id": "5e08cec5a62f094ba4ca2543",
"name": "Event 2",
"__v": 0,
"id": "5e08cec5a62f094ba4ca2543"
}
产品:
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
}
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfa9e9dfcc0e1431c90c",
"name": "Product 2",
"__v": 0,
"id": "5e08cfa9e9dfcc0e1431c90c"
}
客户:
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cfdce9dfcc0e1431c90e",
"first_name": "Client 1",
"__v": 0,
"id": "5e08cfdce9dfcc0e1431c90e"
}
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfece9dfcc0e1431c90f",
"first_name": "Client 2",
"__v": 0,
"id": "5e08cfece9dfcc0e1431c90f"
}
让我们首先解决从事件访问客户和产品的问题,因为我们删除了该引用。
我们可以使用以下路径来获取虚拟填充的客户和产品的事件:
router.get("/events/:id", async (req, res) => {
const result = await Event.findById(req.params.id)
.populate("clients")
.populate("products");
res.send(result);
});
响应将是这样的:(如您所见,即使我们删除了引用,我们也可以使用虚拟填充访问客户端和产品)
{
"_id": "5e08ceb5a62f094ba4ca2542",
"name": "Event 1",
"__v": 0,
"clients": [
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cfdce9dfcc0e1431c90e",
"first_name": "Client 1",
"__v": 0,
"id": "5e08cfdce9dfcc0e1431c90e"
},
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfece9dfcc0e1431c90f",
"first_name": "Client 2",
"__v": 0,
"id": "5e08cfece9dfcc0e1431c90f"
}
],
"products": [
{
"events": [
"5e08ceb5a62f094ba4ca2542",
"5e08cec5a62f094ba4ca2543"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
},
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cfa9e9dfcc0e1431c90c",
"name": "Product 2",
"__v": 0,
"id": "5e08cfa9e9dfcc0e1431c90c"
}
],
"id": "5e08ceb5a62f094ba4ca2542"
}
现在要从产品或客户中删除事件,我们只需从那里删除即可。
例如要从产品中删除一个事件,我们只需要从产品中删除它:
router.delete("/products/:id/:eventId", async (req, res) => {
const result = await Product.findByIdAndUpdate(
req.params.id,
{
$pull: {
events: req.params.eventId
}
},
{ new: true }
);
res.send(result);
});
在此删除之前,此产品有 2 个 ID 为 5e08ceb5a62f094ba4ca2542
和 5e08cec5a62f094ba4ca2543
的事件。
我们删除后它只会有一个id为5e08ceb5a62f094ba4ca2542
:
{
"events": [
"5e08ceb5a62f094ba4ca2542"
],
"_id": "5e08cf9de9dfcc0e1431c90b",
"name": "Product 1",
"__v": 0,
"id": "5e08cf9de9dfcc0e1431c90b"
}