如何在进行批量更新时有条件地包含 CreationDate ($setOnInsert with $set creates error)

How to conditionally include CreationDate when making a bulk upsert ($setOnInsert with $set creates error)

我是 mongoDB 的初学者,我正在尝试更新或创建一个非常大的产品列表。数据库中已存在的产品将有一个我不希望覆盖的“creationDate”。使用以下代码时:

public AddProductResponse add(final String trackingId, final String catalogName, final List<Product> products) {
            
        final AddProductResponse res = new AddProductResponse();
        res.products(products);

        final List<WriteModel<Product>> writes = new ArrayList<>();
        for (final Product product : products) {
            final OffsetDateTime creationDate = OffsetDateTime.now();
            product.setLastUpdate(OffsetDateTime.now());
            final Bson filter = Filters.eq(Constants.PRODUCT_ID_PROPERTY, product.getId());
            final Bson update = new Document("$set", product).append("$setOnInsert", new Document(Constants.PRODUCT_CREATION_DATE_PROPERTY, creationDate));
            final UpdateOptions options = new UpdateOptions().upsert(true);


            writes.add(new UpdateOneModel<>(filter, update, options));
        }

        try {
            final BulkWriteResult bulkWriteResult = getCollection(trackingId, catalogName, COLLECTION_TYPE, Product.class).bulkWrite(writes, new BulkWriteOptions().ordered(false));
            LOG.info("Bulk write result: {}", bulkWriteResult);

            if (!bulkWriteResult.wasAcknowledged()) {
                res.setSuccess(false);
                res.reason(Constants.UNKNOWN_ERROR_MESSAGE);

            } else {
                res.setSuccess(true);

            }
        } catch (final Exception e) {
            res.setSuccess(false);
            LOG.info("Error: {}", e.getMessage());
            res.setReason(e.getMessage());
        }

        return res;
    }

我收到错误消息:“更新路径 'creationDate' 会在 'creationDate' 处产生冲突”,我理解另一个 中列出的原因,但我'我想知道是否有其他方法可以实现这一目标。此外,下面是我为确保实现功能而编写的测试。感谢您的帮助。

 @Test
    public void testAddProductListEndpointWithExistingProduct() throws JsonProcessingException {
        final Product p = getProduct();
        final String token = getToken(trackingId);

        addCatalog(trackingId, catalog, null, token);
        final Response addProductResponse = addProduct(trackingId, catalog, p, token);
        final Document doc = getDatabase().getCollection(productsCollection).find(new BasicDBObject("id", p.getId()))
                .first();

        final List<Product> products = new ArrayList<>();
        products.add(new Product().id("p1").name("Product 1").creationDate(OffsetDateTime.now().plusHours(1)).categoryHierarchies(List.of(new Category().id("id1").label("label1").type("type1"))));
        products.add(new Product().id("p2").name("Product 2").categoryHierarchies(List.of(new Category().id("id1").label("label1").type("type1"))));
        products.add(new Product().id("p3").name("Product 3").categoryHierarchies(List.of(new Category().id("id1").label("label1").type("type1"))));
        final Response addProductResponse2 = addProducts(trackingId, catalog, products, token);
        final Document doc2 = getDatabase().getCollection(productsCollection).find(new BasicDBObject("id", "p1")).first();

        //creation date of existing product should not be overwritten
        Assert.assertTrue(doc.get("creationDate").equals(doc2.get("creationDate")));

    }

如果您希望使用批量和产品列表来更新产品,那么如果您的列表中的每个产品尚不存在,则将被插入,并且如果存在则将覆盖现有文档,但不creationDate属性就是直接在product下,可以这样:
如果创建日期在您的产品下,您不能同时 $set 它在产品和 $setOnInsert 下,因此如果您的产品结构是:

product: {
    creationDate: ISODate,
    propX: 'a',
    propY: {propZ: 2, propT: 4, propA: ['A', 'B']}
}

你的代码应该是这样的:

 final Bson update = new Document("$set", 'product.propX', 'product.propY')
.append("$setOnInsert", new Document(Constants.PRODUCT_CREATION_DATE_PROPERTY, creationDate));

请注意,批量操作是在作为查询执行之前使用您的代码编写的。这意味着您可以使用循环来更新批量的 $set 部分,以便将所有属性直接添加到 product 下,从您的产品列表到您的 $set 子句。

您可以在 playground, in the 上看到类似问题。