【问题标题】:Add multiple sub-documents in MongoDB在MongoDB中添加多个子文档
【发布时间】:2012-05-06 15:03:58
【问题描述】:

我正在开发一个客户数据加载器,客户可以在其中拥有多个地址。如果找不到客户,我创建它并添加地址。如果客户存在,我只需添加新地址,如下所示:

    DBObject findCustomer = new BasicDBObject();
    findCustomer.put("email", custEmail);

    //check for existing customer
    DBObject newCustomer = customerCollection.findOne(findCustomer);

    if (newCustomer == null) {
        //INSERT    
        newCustomer = new BasicDBObject();
        newCustomer.put("firstname", firstname);
        newCustomer.put("lastname", lastname);
        newCustomer.put("email", custEmail);
        newCustomer.put("password", custData.getPassword());
        newCustomer.put("softwaretime", new Date());
    }

    DBObject newAddress = new BasicDBObject();
    City tempCity = new City();
    tempCity = addressData.getCity();

    newAddress.put("type", addressData.getType());
    newAddress.put("line1", addressData.getLine1());
    newAddress.put("line2", addressData.getLine2());
    newAddress.put("city", tempCity.getCity());
    newAddress.put("state", tempCity.getState());
    newAddress.put("postal", tempCity.getZip());
    newAddress.put("country", tempCity.getCountry());

    newCustomer.put("address", newAddress);

    customerCollection.save(newCustomer);

这适用于新客户。问题是当客户已经存在时,新地址会覆盖现有地址。

如何将新地址添加给客户,使其保留多个地址?

根据我的发现,我应该能够通过外壳通过“推送”来完成此操作。但我不认为“推”是 BasicDBObject 上的方法。

【问题讨论】:

  • 这看起来很危险 - 当您有多个客户的名字和姓氏相同时会发生什么?这段代码也不是线程安全的——如果两个线程试图创建同一个客户,你最终可能会得到一个重复。您也可能将同一个地址推送了两次。
  • 好点@AsyaKamsky,我将使用电子邮件地址作为搜索键。此外,这是一个单线程应用程序,旨在将数据加载到 MongoDB 中。如果多个(竞争)线程是可能的,你会如何考虑线程安全?
  • 有多种方法可以做到这一点 - 取决于您的应用程序逻辑期望/需要什么。您可以只使电子邮件唯一,然后第二次插入尝试将失败,但您的应用程序需要期待并恢复。您可以使用 Java 并发,但如果它只是地址的多个副本,那么您可以使用 $addToSet 而不是 $push,这样您就不会添加一个已经在列表中的地址。

标签: java mongodb mongodb-java


【解决方案1】:

您希望地址是地址列表而不是单个地址文档。因此,对于您希望拥有的新客户:

newCustomer.put("地址", [newAddress]) customerCollection.save(新客户)

对于您想要的现有客户

customerCollection.update(newCustomer, {$push: {"addresses": newAddress}})

抱歉,我不知道 java API,所以你必须修改上面的代码来创建合适的对象

【讨论】:

  • 谢谢@ChrisAtLee,我会试一试的。
  • $push 对现有客户不正确,除非您检查此地址记录不存在(即使您这样做,在多线程设置中也不安全。请改用 $addToSet - 只有在它不存在时才会添加,$push 无条件添加)。
  • @AsyaKamsky 你会碰巧知道这样做的 Java 语法吗?
  • 是的。由于我认为此答案不正确,因此我将其发布在单独的答案中。
【解决方案2】:

事实证明,您的逻辑可以简单得多。您无需通过“电子邮件”获取客户(我假设这是您唯一的客户识别密钥),只需更新即可。

findCustomer.put("email", custEmail); // search query for the customer email

// construct your newAddress object the same way you already are

BasicDBObject custMod = new BasicDBObject();
custMod.put("$addToSet", newAddress);
customerCollection.update(findCustomer, custMod, true /* upsert */, false /* multi */ );

你现在的逻辑方式的大问题是它不能在多线程中工作。您可以检查客户,它不会在那里。当您构造对象以插入它时,另一个线程已经在执行此操作。由于地址对象是一个数组而不是单个字段,如果存在的话,使用 $addToSet 将添加到数组中,但如果它正在创建一个新客户,那么它会将地址创建为一个数组。

【讨论】:

    猜你喜欢
    • 2014-07-29
    • 2015-07-23
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 2021-05-16
    • 2021-06-17
    • 2013-08-22
    • 2015-06-12
    相关资源
    最近更新 更多