使用 Spring Data MongodB 更新嵌入式 mongodb 文档中的数组字段?

Posted

技术标签:

【中文标题】使用 Spring Data MongodB 更新嵌入式 mongodb 文档中的数组字段?【英文标题】:Update array field in embedded mongodb document using Spring Data MongodB? 【发布时间】:2016-12-18 03:13:40 【问题描述】:

我正在尝试使用 Spring Data MongoDB 更新 mongodb 中嵌入文档中的数组字段。

我想在 mongodb 集合中更新/插入的文档结构如下。

根据每个部门的类型,可以有更多这样的文档,比如“销售”、“营销”等等。


    "timestamp": "2014-09-26T04:00:00.000Z",
    "department": "accounts",
    "employee": [
        
            "type": "regular",
            "names": [
                "Raj",
                "Kumar",
                "Shankar"
            ]
        ,
        
            "type": "contract",
            "names": [
                "Penny",
                "Sheldon",
                "bob"
            ]
        ,
        
            "type": "temp",
            "names": [
                "jerry",
                "kramer",
                "bubbleboy"
            ]
        
    ]

基本上,我的更新查询如下,

db.getCollection('mytest').update(
    
        "timestamp" : "2014-09-26T04:00:00.000Z",
        "department" : "accounts",
        "employee.type" : "regular"
    ,
     $addToSet:  "employee.$.names" : "Jo"  ,
    
        upsert: true
    
)

我添加了 upsert: true ,因为如果没有与查询匹配的文档,我想将该文档插入到 mytest 集合中。

当我从 mongo shell 执行相同的操作时,我收到以下错误。

   The positional operator did not find the match needed from the query. Unexpanded update: employee.$.names

即使这样可行,我不确定我们是否有类似的支持来在 Spring Data mongodb 中实现相同的功能。

另外,我的另一个查询是,如果我想为多个部门添加/更新员工,比如“帐户”和“销售”,看来我必须使用不同的值执行相同的查询作为数量我想相应地更新部门(如果不存在则插入)。

是否有更好更高效的选项,例如批量/批量,我可以在单个 mongo 更新查询中同时更新/插入多个部门的员工。另外,spring data mongodb/mongotemplate 中是否有相同的支持。

【问题讨论】:

您是否有机会查看以下解决方案,看看它是否解决了您的问题? 【参考方案1】:

首先,我应该说,由于文档结构具有嵌入式数组,因此需要处理多种场景。如果可能,请尝试重新设计文档结构。

对错误的简短回答:- 如果查询未找到匹配的文档,则会出现位置运算符错误。

这就是为什么在我下面的解决方案中,我在 catch 块中处理了这个场景。如果你能通过单元测试,你应该能够理解。

解决方案:-

public Boolean updateDepartmentCollectionWithoutFind(String name, String timeStamp)
            throws JsonParseException, JsonMappingException, IOException 

        MongoOperations mongoOperations = getMongoConnection();

        Query query = new Query();
        query.addCriteria(Criteria.where("timestamp").is(timeStamp).and("department").is("accounts")
                .and("employee.type").is("regular"));
        Update update = new Update();
        update.addToSet("employee.$.names", name);

        try 

            System.out.println(query.toString());
            System.out.println(update.toString());

            WriteResult writeResult = mongoOperations.upsert(query, update, DepartmentCollection.class);

            if (writeResult != null) 

                System.out.println("111111111111111111 Update with position parameter has been successful :"
                        + writeResult.toString());

            
         catch (DataIntegrityViolationException die) 
            System.out.println("Update failed ====>" + die.getMessage());
            System.out.println("Trying to update without position parameters ...");

            Update updateWithoutPositionOperator = new Update();
            updateWithoutPositionOperator.addToSet("employee.names", name);
            WriteResult writeResultUpsert = mongoOperations.upsert(query, updateWithoutPositionOperator,
                    DepartmentCollection.class);

            if (writeResultUpsert != null) 

                System.out.println("2222222222222222222 Update without position parameter has been successful :"
                        + writeResultUpsert.toString());

            

        

        return true;

    

我的get连接方法供参考:- 如果您有 spring 上下文并且可以根据您的上下文更改上述代码,则不需要这个。

@SuppressWarnings("resource")
public MongoOperations getMongoConnection() 

    return (MongoOperations) new AnnotationConfigApplicationContext(SpringMongoConfig.class)
            .getBean("mongoTemplate");

单元测试:- 我发布单元测试是因为您需要了解场景和解决方案。请阅读每个测试和方法名称中的注释,以了解在每个场景中执行哪个 upsert。

Test 3 是一种特殊行为。请仔细看。

@Test
    public void updateDepartmentCollectionWhenNoDocumentsPresent() throws JsonParseException, JsonMappingException, IOException 

        //Second upsert executed (i.e. without positional parameter)
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Raj", "2014-09-26T04:00:00.000Z"));

    

    @Test
    public void updateDCWhenMatchingDocumentsPresentButDocumentHasOnlyRegularEmployeeType() throws JsonParseException, JsonMappingException, IOException 

        //Second upsert executed  (i.e. without positional parameter)       
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-26T04:00:00.000Z"));

       

    @Test
    public void updateDCWhenMultipleTypesPresentButNoRegular() throws JsonParseException, JsonMappingException, IOException 

        //First upsert executed  (i.e. with positional parameter). However, it creates a new document.      
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-27T04:00:00.000Z"));

       

    @Test
    public void updateDCWhenMultipleTypesPresentIncludingRegular() throws JsonParseException, JsonMappingException, IOException 

        //First upsert executed (i.e. with positional parameter) and existing document has been updated.        
        Assert.isTrue(queryOperations.updateDepartmentCollectionWithoutFind("Jo", "2014-09-29T04:00:00.000Z"));

    

【讨论】:

以上是关于使用 Spring Data MongodB 更新嵌入式 mongodb 文档中的数组字段?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Boot WebFlux、Spring Data MongoDB Reactive 和 ReactiveMongoRepository 更新 1 个或多个特定字段 MongoDB

使用 MongoOperation 在 Spring Data MongoDB 中构建 ArrayFilters 更新

如何配置两个实例mongodb使用spring boot和spring data

Spring data mongodb querydsl 环境配置

使用 Spring Data MongoRepository 进行更新查询的自定义方法

使用Spring访问Mongodb的方法大全——Spring Data MongoDB