MongoDB: 如何插入子文档?
MongoDB: how to insert a sub-document?
我在我的 MEAN 项目中使用子文档来处理订单和每个订单的项目。
这些是我的(简化的)模式:
var itemPerOrderSchema = new mongoose.Schema({
itemId: String,
count: Number
});
var OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: [ itemPerOrderSchema ]
});
要在 itemPerOrderSchema 数组中插入项目,我目前这样做:
var orderId = '123';
var item = { itemId: 'xyz', itemsCount: 7 };
Order.findOne({ id: orderId }, function(err, order) {
order.items.push(item);
order.save();
});
问题是我显然每个 itemId
想要一个项目,这样我就每个项目获得许多子文档...
一种解决方案可能是遍历所有 order.items
,但这当然不是最优的(order.items
可以 many...)。
查询 order.items
...
时可能会出现同样的问题
问题是:如何在 itemPerOrderSchema
数组中插入项目,而不必遍历订单中已插入的所有项目?
首先,您可能需要将 orderId 添加到 itemPerOrderSchema,因为 orderId 和 itemId 的组合将使记录唯一。
假设将 orderId 添加到 itemPerOrderSchema,我会建议以下实现:
function addItemToOrder(orderId, newItem, callback) {
Order.findOne({ id: orderId }, function(err, order) {
if (err) {
return callback(err);
}
ItemPerOrder.findOne({ orderId: orderId, itemId: newItem.itemId }, function(err, existingItem) {
if (err) {
return callback(err);
}
if (!existingItem) {
// there is no such item for this order yet, adding a new one
order.items.push(newItem);
order.save(function(err) {
return callback(err);
});
}
// there is already item with itemId for this order, updating itemsCount
itemPerOrder.update(
{ id: existingItem.id },
{ $inc: { itemsCount: newItem.itemsCount }}, function(err) {
return callback(err);
}
);
});
});
}
addItemToOrder('123', { itemId: ‘1’, itemsCount: 7 }, function(err) {
if (err) {
console.log("Error", err);
}
console.log("Item successfully added to order");
});
希望这可能有所帮助。
如果您可以对项目使用对象而不是数组,也许您可以稍微更改架构以进行 single-query 更新。
像这样:
{
customerId: 123,
items: {
xyz: 14,
ds2: 7
}
}
因此,每个 itemId 都是对象中的键,而不是数组的元素。
let OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: mongoose.Schema.Types.Mixed
});
那么更新您的订单就非常简单了。假设您要将商品编号 'xyz' 的 3 添加到客户 123.
db.orders.update({
customerId: 123
},
{
$inc: {
'items.xyz': 3
}
},
{
upsert: true
});
即使客户没有条目,也可以在此处传递更新插入以创建订单。
这样做的缺点:
就是如果你使用聚合框架,要么无法迭代你的项目,要么如果你有一个有限的、已知的 itemIds 集,那么非常冗长。你可以用 mapReduce 来解决这个问题,这可能会慢一点,这取决于你有多少人,所以 YMMB。
您的客户端上没有干净的 items
数组。您可以通过客户端提取此信息(一个简单的 let items = Object.keys(order.items).map(key => ({ key: order.items[key] }));
或使用 mongoose 虚拟字段或 schema.path() 来解决这个问题,但这可能是另一个问题,已经回答了。
我在我的 MEAN 项目中使用子文档来处理订单和每个订单的项目。
这些是我的(简化的)模式:
var itemPerOrderSchema = new mongoose.Schema({
itemId: String,
count: Number
});
var OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: [ itemPerOrderSchema ]
});
要在 itemPerOrderSchema 数组中插入项目,我目前这样做:
var orderId = '123';
var item = { itemId: 'xyz', itemsCount: 7 };
Order.findOne({ id: orderId }, function(err, order) {
order.items.push(item);
order.save();
});
问题是我显然每个 itemId
想要一个项目,这样我就每个项目获得许多子文档...
一种解决方案可能是遍历所有 order.items
,但这当然不是最优的(order.items
可以 many...)。
查询 order.items
...
问题是:如何在 itemPerOrderSchema
数组中插入项目,而不必遍历订单中已插入的所有项目?
首先,您可能需要将 orderId 添加到 itemPerOrderSchema,因为 orderId 和 itemId 的组合将使记录唯一。
假设将 orderId 添加到 itemPerOrderSchema,我会建议以下实现:
function addItemToOrder(orderId, newItem, callback) {
Order.findOne({ id: orderId }, function(err, order) {
if (err) {
return callback(err);
}
ItemPerOrder.findOne({ orderId: orderId, itemId: newItem.itemId }, function(err, existingItem) {
if (err) {
return callback(err);
}
if (!existingItem) {
// there is no such item for this order yet, adding a new one
order.items.push(newItem);
order.save(function(err) {
return callback(err);
});
}
// there is already item with itemId for this order, updating itemsCount
itemPerOrder.update(
{ id: existingItem.id },
{ $inc: { itemsCount: newItem.itemsCount }}, function(err) {
return callback(err);
}
);
});
});
}
addItemToOrder('123', { itemId: ‘1’, itemsCount: 7 }, function(err) {
if (err) {
console.log("Error", err);
}
console.log("Item successfully added to order");
});
希望这可能有所帮助。
如果您可以对项目使用对象而不是数组,也许您可以稍微更改架构以进行 single-query 更新。
像这样:
{
customerId: 123,
items: {
xyz: 14,
ds2: 7
}
}
因此,每个 itemId 都是对象中的键,而不是数组的元素。
let OrderSchema = new mongoose.Schema({
customerId: String,
date: String,
items: mongoose.Schema.Types.Mixed
});
那么更新您的订单就非常简单了。假设您要将商品编号 'xyz' 的 3 添加到客户 123.
db.orders.update({
customerId: 123
},
{
$inc: {
'items.xyz': 3
}
},
{
upsert: true
});
即使客户没有条目,也可以在此处传递更新插入以创建订单。
这样做的缺点:
就是如果你使用聚合框架,要么无法迭代你的项目,要么如果你有一个有限的、已知的 itemIds 集,那么非常冗长。你可以用 mapReduce 来解决这个问题,这可能会慢一点,这取决于你有多少人,所以 YMMB。
您的客户端上没有干净的
items
数组。您可以通过客户端提取此信息(一个简单的let items = Object.keys(order.items).map(key => ({ key: order.items[key] }));
或使用 mongoose 虚拟字段或 schema.path() 来解决这个问题,但这可能是另一个问题,已经回答了。