使用 Reactive Mongo 对嵌套对象列表进行更新插入

问题描述 投票:0回答:1

我有一个相对嵌套的订单域对象,其中有一个 OrderItem 列表,而 OrderItem 列表又包含一个供应商列表。我想做一个更新插入,以便可以添加新订单,并且它们相应的订单不存在。问题是我不知道如何处理嵌套集合的更新。我正在使用 Spring Boot 3 和 Reactive Mongo。是否可以在实体级别进行更新,即将 OrderItem 作为对象进行更新,而不必更新各个字段?

这是对象模型

     // Order class
     import lombok.Data;
     import org.springframework.data.annotation.Id;
     import org.springframework.data.mongodb.core.mapping.Document;
    
     import java.util.List;
    
    
     @Document(collection = "order")
     @Data
     public class Order {
    
        @Id
        String orderId;
        List<OrderItem> orderItemList;
    }

     // OrderItem class
     import lombok.AllArgsConstructor;
     import lombok.Data;
     import lombok.NoArgsConstructor;
     import org.springframework.data.annotation.Id;
     import org.springframework.data.mongodb.core.mapping.Document;
        
     import java.math.BigDecimal;
     import java.util.List;
        
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
     @Document(collection = "order-item")
     public class OrderItem {
        
       @Id
       String orderItemId;
       BigDecimal price;
       String partNumber;
       String description;
       int quantityInStock;
       List<Supplier> suppliers;
     }


     import lombok.Data;
     import org.springframework.data.annotation.Id;
     import org.springframework.data.mongodb.core.mapping.Document;
    
     @Document(collection = "supplier")
     @Data
     public class Supplier {
    
       @Id
       String supplierId;
       String supplierName;
       String supplierPartNumber;
    }

以下是执行更新插入的代码的开头。缺少的部分是嵌套对象片段,我不知道该怎么做:我相信代码的其余部分很好,当然除了嵌套对象的缺失部分。理想情况下,我想在更新中使用整个实体,例如 update("$[orderItems].orderItem", orderItem) 但我不知道这是否可能以及如果可能的话使用什么语法。


     import lombok.RequiredArgsConstructor;
     import lombok.extern.slf4j.Slf4j;
     import org.springframework.data.mongodb.core.BulkOperations;
     import org.springframework.data.mongodb.core.MongoOperations;
     import org.springframework.data.mongodb.core.query.Criteria;
     import org.springframework.data.mongodb.core.query.Query;
     import org.springframework.data.mongodb.core.query.Update;
    
     import org.springframework.stereotype.Service;
    
     import java.util.List;
    
     @Service
     @RequiredArgsConstructor
     @Slf4j
     public class OrderService {
    
        private final MongoOperations mongoOperations;
        private final static String ORDER_COLLECTION = "orders";
    
        public void upsertOrders(List<Order> orders, List<OrderItem> orderItems) {
            try {
    
                if (!ordersItems.isEmpty()) {
                    BulkOperations bulkTemplateOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, ORDER_COLLECTION);
                    for (Order order : orders) bulkTemplateOperations.upsert(new Query(Criteria.where("_id").is(order.getOrderId())), createOrderUpdate(order));
                    bulkTemplateOperations.execute();
                }
    
    
            } catch (Exception e) {
                log.error("Failed to update orders collection  {}",e.getMessage());
                throw e;
            }
        }
    
    
        protected Update createOrderUpdate(Order order) {
            Update update =  new Update();
            update.set("orderId", order.getOrderId());
            // how to do for OrderItems
            return update;
        }
    }


我希望能得到一些帮助来填补我理解上的空白。预先感谢

spring-boot spring-data spring-data-mongodb
1个回答
0
投票

您的代码没问题,您可以使用整个对象更新单个文档,而不是更新每个字段。我在这里提交:https://github.com/sbernardo/spring-issues-examples/tree/main/sof-questions-77623978完整代码。

您有2个选择:

protected Update createOrderUpdate(Order order) {
    Update update = new Update();
    update.set("orderId", order.getOrderId());
    update.set("orderItemList", order.getOrderItemList());
    return update;
}
protected Update createOrderUpdateWithAddToSet(Order order) {
    Update update = new Update();
    update.set("orderId", order.getOrderId());
    order.getOrderItemList().forEach(oi -> update.addToSet("orderItemList", oi));
    return update;
}

第一个用输入列表替换整个列表(仅当发生更改时)。第二个添加一个新的订单项到列表中(如果它已经存在)。

注意:我建议使用第一个,这样您就可以管理整个列表,只有当您想添加新内容时才必须使用第二个(不适用于更新数组中的单个元素,仅适用于添加新元素).

我尝试了项目中的第一个(请参阅以下代码)来管理单个订单:

@PatchMapping("orders")
public String myEndpoint() {
    var supplierFirstOrderItem = new Supplier("suppId1", "Supplier1", "PART_NUM122");
    var suppliersFirstOrderItem = List.of(supplierFirstOrderItem);
    var firstOrderItem = new OrderItem("IDIT1", BigDecimal.ONE, "PART_NUM1", "Ordered from Amazon", 1, suppliersFirstOrderItem);
    var orderItems = new ArrayList<>(List.of(firstOrderItem));
    var order = new Order("ID1", orderItems);
    var orders = List.of(order);

    //First upsert -> new order -> INSERT DONE
    var firstUpsert = service.upsertOrders(orders, orderItems);
    log.info("First upsert result: {}", firstUpsert);

    //Second upsert -> add a new order item on an existing order -> UPDATE DONE
    var supplierSecondOrderItem = new Supplier("suppId2", "Supplier2", "PART_NUM211");
    var suppliersSecondOrderItem = List.of(supplierSecondOrderItem);
    var newOrderItem = new OrderItem("IDIT2", BigDecimal.TEN, "PART_NUM2", "Ordered from Ebay", 100, suppliersSecondOrderItem);
    orderItems.add(newOrderItem);
    var secondUpsert = service.upsertOrders(orders, orderItems);
    log.info("Second upsert result: {}", secondUpsert);

    //Third upsert -> modified price on existing order item -> UPDATE DONE
    order.getOrderItemList().getFirst().setPrice(BigDecimal.valueOf(10000));
    var thirdUpsert = service.upsertOrders(orders, orderItems);
    log.info("Third upsert result: {}", thirdUpsert);

    //Fourth upsert -> writing same order -> NO UPDATE DONE
    var fourthUpsert = service.upsertOrders(orders, orderItems);
    log.info("Fourth upsert result: {}", fourthUpsert);

    var newOrderListOrdered = List.of(newOrderItem, firstOrderItem);
    order.setOrderItemList(newOrderListOrdered);
    //Fifth upsert -> writing same order (different orderItems internal list) -> UPDATE DONE
    var fifthUpsert = service.upsertOrders(orders, orderItems);
    log.info("Fifth upsert result: {}", fifthUpsert);

    var newOrderListDeleteOne = List.of(newOrderItem);
    order.setOrderItemList(newOrderListDeleteOne);
    //Sixth upsert -> writing same order (orderItems deleted one) -> UPDATE DONE
    var sixthUpsert = service.upsertOrders(orders, orderItems);
    log.info("Sixth upsert result: {}", sixthUpsert);

    return sixthUpsert.toString();
}

输出日志向您建议 mongo 做了什么

First upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=0, removedCount=0, modifiedCount=0, upserts=[BulkWriteUpsert{index=0, id=BsonString{value='ID1'}}], inserts=[]}
Second upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Third upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Fourth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=0, upserts=[], inserts=[]}
Fifth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}
Sixth upsert result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=1, removedCount=0, modifiedCount=1, upserts=[], inserts=[]}

如您所见,只有发生更改才会更新(列表的顺序很重要!更新不同的顺序!)

希望这会有用! :)

© www.soinside.com 2019 - 2024. All rights reserved.