MongoDB Upsert操作概述

Upsert是insert(插入)和update(更新)的组合(inSERT + UPdate = upsert)。 我们可以使用不同的更新方法,如updatefindAndModifyreplaceOne来实现upsert。

MongoDB中,upsert选项是一个布尔值。如果设置为true且文档符合指定的查询过滤条件,将执行更新操作。如果匹配不到任何文档,该选项会插入一个新的文档到集合中。新文档将包含基于过滤器和应用操作的字段。

本教程将首先介绍MongoDB Shell查询中的upsert,然后使用Java驱动程序代码。

2. 数据库初始化

在进行upsert操作之前,我们首先需要创建一个名为baeldung的新数据库,并在其中添加一个示例集合vehicle

db.vehicle.insertMany([
{
    "companyName":"Nissan", 
    "modelName":"GTR",
    "launchYear":2016,
    "type":"Sports",
    "registeredNo":"EPS 5561"
},
{ 
    "companyName":"BMW",
    "modelName":"X5",
    "launchYear":2020,
    "type":"SUV",
    "registeredNo":"LLS 6899"
},
{
    "companyName":"Honda",
    "modelName":"Gold Wing",
    "launchYear":2018,
    "type":"Bike",
    "registeredNo":"LKS 2477"
}]);

如果插入成功,上述命令将打印类似下面的JSON输出:

{
    "acknowledged" : true, 
    "insertedIds" : [
        ObjectId("623c1db39d55d4e137e4781b"),
    ObjectId("623c1db39d55d4e137e4781c"),
    ObjectId("623c1db39d55d4e137e4781d")
    ]
}

我们已成功将示例数据添加到vehicle集合中。

3. 使用update方法

本节将学习如何使用upsert选项与update方法。upsert选项的主要目的是根据应用的过滤器更新现有文档,或者如果过滤条件不匹配,则插入新文档。

举例来说,我们将使用$setOnInsert操作符与upsert选项,以便在插入新字段时获得额外优势。

让我们检查一个查询,其中的过滤条件匹配集合中的现有文档:

db.vehicle.update(
{
    "modelName":"X5"
},
{
    "$set":{
        "companyName":"Hero Honda"
    }
},
{
    "upsert":true
});

上述查询将返回以下文档:

{ 
    "nMatched" : 1, 
    "nUpserted" : 0,
    "nModified" : 1 
}

接下来,我们将查看与上述Mongo Shell查询对应的Java驱动程序代码:

UpdateOptions options = new UpdateOptions().upsert(true);
UpdateResult updateResult = collection.updateOne(Filters.eq("modelName", "X5"), 
  Updates.combine(Updates.set("companyName", "Hero Honda")), options);
System.out.println("updateResult:- " + updateResult);

在这个例子中,modelName字段的值为“X5”,集合中已有此文档,因此该文档的companyName字段将被更新为“Hero Honda”。

现在,让我们看一个使用$setOnInsert操作符的upsert选项的例子。它只适用于添加新文档的情况:

db.vehicle.update(
{
    "modelName":"GTPR"
},
{
    "$set":{
        "companyName":"Hero Honda"
    },
    "$setOnInsert":{
        "launchYear" : 2022,
    "type" : "Bike",
    "registeredNo" : "EPS 5562"
    },  
},
{
    "upsert":true
});

上述查询将返回以下文档:

{
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("623b378ed648af670fe50e7f")
}

带有$setOnInsert选项的上述更新查询的Java驱动程序代码将是:

UpdateResult updateSetOnInsertResult = collection.updateOne(Filters.eq("modelName", "GTPR"),
  Updates.combine(Updates.set("companyName", "Hero Honda"),
  Updates.setOnInsert("launchYear", 2022),
  Updates.setOnInsert("type", "Bike"),
  Updates.setOnInsert("registeredNo", "EPS 5562")), options);
System.out.println("updateSetOnInsertResult:- " + updateSetOnInsertResult);

这里,过滤条件中的modelName字段值为“GTPR”并未匹配集合中的任何文档,所以我们将在集合中添加一个新文档。值得注意的是,$setOnInsert会在新文档中添加所有字段。

4. 使用findAndModify方法

我们还可以使用findAndModify方法与upsert选项。对于这种方法,upsert选项的默认值为false。如果设置为true,其行为将与update方法相同。

让我们看看带有upsert选项为truefindAndModify方法的一个用例:

db.vehicle.findAndModify(
{
    query:{
        "modelName":"X7"
    },
    update: {
        "$set":{
            "companyName":"Hero Honda"
        }
    },
    "upsert":true,
    "new":true
});

在这种情况下,上述查询将返回新创建的文档。以下是上述查询的Java驱动程序代码:

FindOneAndUpdateOptions upsertOptions = new FindOneAndUpdateOptions();
  upsertOptions.returnDocument(ReturnDocument.AFTER);
  upsertOptions.upsert(true);
Document resultDocument = collection.findOneAndUpdate(Filters.eq("modelName", "X7"),
  Updates.set("companyName", "Hero Honda"), upsertOptions);
System.out.println("resultDocument:- " + resultDocument);

在这里,我们首先创建了过滤条件,根据该条件,我们将更新现有文档或向vehicle集合中添加新文档。

5. 使用replaceOne方法

接下来,让我们使用replaceOne方法执行upsert操作。replaceOne方法在集合中仅替换匹配条件的单个文档。

首先,让我们看看Mongo Shell查询中的替换方法:

db.vehicle.replaceOne(
{
    "modelName":"GTPR"
},
{
    "modelName" : "GTPR",
    "companyName" : "Hero Honda",
    "launchYear" : 2022,
    "type" : "Bike",
    "registeredNo" : "EPS 5562"
},
{
    "upsert":true
});

上述查询将返回以下响应:

{ 
    "acknowledged" : true, 
    "matchedCount" : 1,
    "modifiedCount" : 1 
}

现在,让我们使用Java驱动程序代码编写上述查询:

Document replaceDocument = new Document();
replaceDocument.append("modelName", "GTPP")
  .append("companyName", "Hero Honda")
  .append("launchYear", 2022)
  .append("type", "Bike")
  .append("registeredNo", "EPS 5562");
UpdateResult updateReplaceResult = collection.replaceOne(Filters.eq("modelName", "GTPP"), replaceDocument, options);
System.out.println("updateReplaceResult:- " + updateReplaceResult);

在这个例子中,我们需要首先创建一个想要替换现有文档的新文档,并通过设置upsert选项为true,只有当条件匹配时才会替换文档。

6. 总结

在这篇文章中,我们了解了如何使用MongoDB的不同更新方法执行upsert操作。首先,我们学习了如何使用updatefindAndModify方法执行upsert,然后使用了replaceOne方法。简而言之,我们通过Mongo Shell查询和Java驱动程序代码实现了upsert操作。

所有案例的实现可以在GitHub上找到。