MongoDB:如何插入子文档?

Posted

技术标签:

【中文标题】MongoDB:如何插入子文档?【英文标题】:MongoDB: how to insert a sub-document? 【发布时间】:2016-04-14 21:02:21 【问题描述】:

我在 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 我可以很多...)。 查询order.items时可能会出现同样的问题...

问题是:如何在itemPerOrderSchema 数组中插入项目,而不必遍历订单中已插入的所有项目?

【问题讨论】:

itemPerOrder 架构只有项目或项目加上项目 ID?我使用类似的格式,我直接将元素推入数组,如果你想要代码,我可以在这里粘贴。 它也有 itemid (var itemPerOrderSchema = new mongoose.Schema( itemId: String, ...)。如果您可以粘贴您的代码(当然是在答案中,而不是在评论中),它肯定会有所帮助... modelname.findOneAndUpdate(_id: idname, $addToSet: items: $each: iems , function(err, docs) - 这就是我所做的。 iems 你的意思是items?但是item要插入到哪里呢? iems 是项目,拼写错误 - 抱歉。在我的例子中,iems 是一个字符串数组。您必须插入一个对象。 【参考方案1】:

首先,您可能需要将 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");
);

希望这会有所帮助。

【讨论】:

谢谢!它可能会有所帮助......但是,我确实希望找到一些方法来调用 order.save,只需通过将元素(或修改它)推送到 javascript 数组来更新订单子文档......跨度> 可能在existingItem 更新中使用 $inc 而不是 $set (假设计数是整数) - 以防您有并发更新。【参考方案2】:

如果您可以对项目使用对象而不是数组,也许您可​​以稍微更改架构以进行单查询更新。

类似这样的:


  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
);

即使客户没有条目,也可以在此处传递 upsert 以创建订单。

这样做的缺点:

如果您使用聚合框架,则无法迭代您的项目,或者如果您有一组有限的已知 itemId,则非常冗长。你可以用 mapReduce 来解决这个问题,它可能会慢一点,这取决于你在那里有多少,所以 YMMB。

客户端上没有干净的 items 数组。您可以通过任一客户端提取此信息(简单的let items = Object.keys(order.items).map(key => ( key: order.items[key] )); 或使用猫鼬虚拟字段或 schema.path() 来解决此问题,但这可能是另一个问题,已经回答了。

【讨论】:

这正是我要找的……我不知道 Schema.Types.Mixed……谢谢!我只是有点担心聚合框架问题,因为我目前正在使用它进行查询......虽然我可能能够避免迭代服务器上的项目,但仅限于客户端(我将测试订单.items.map() 获取项目...)。 我是Object.keys(order.items).map();,但这很酷。查询和聚合也可以,根据您的需要,可以完成。但有时它可能有点冗长。

以上是关于MongoDB:如何插入子文档?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Mongoose和MongoDB 2.6插入子文档中数组中的特定位置

如何使用Mongoose和MongoDB 2.6插入子文档中数组中的特定位置

使用 Node.js 将数组插入 MongoDB 子文档

mongodb怎么插入多个文档

Yii,MongoDB查询子文档以插入Mysql表

更新/插入子文档时的 mongodb 性能