0

I have an existing collection, containing several documents:

[{
    "_id": "1",
    "someArray": [
        {
            "_id": "1.1"
            "color": "RED"
        },
        {
            "_id": "1.2"
            "color": "GREEN"
        },
        {
            "_id": "1.3"
            "color": "GREEN"
        }
    ]
}, {
    "_id": "2",
    "someArray": [
        {
            "_id": "2.1"
            "color": "WHITE"
        },
        {
            "_id": "2.2"
            "color": "BLUE"
        }
    ]
}, // many others here...
]

I need to replace the color field of the sub-elements by a colors field, which is an array containing the same value color did.

Here is the result I want to obtain:

[{
    "_id": "1",
    "someArray": [
        {
            "_id": "1.1"
            "colors": [ "RED" ]
        },
        {
            "_id": "1.2"
            "colors": [ "GREEN" ]
        },
        {
            "_id": "1.3"
            "colors": [ "GREEN" ]
        }
    ]
}, {
    "_id": "2",
    "someArray": [
        {
            "_id": "2.1"
            "colors": [ "WHITE" ]
        },
        {
            "_id": "2.2"
            "colors": [ "BLUE" ]
        }
    ]
}]

My closest attempt was this:

collection.updateMany(
    Filters.ne("someArray", Collections.emptyList()),
    Updates.combine(
        Updates.set("someArray.$[].colors", "[ $someArray.color ]"),
        Updates.unset("someArray.$[].color")
    )
);

But the value of colors is inserted as a String, as-is. It's not interpreted as "an array containing the value of color field".

This has to be done in Java. Please don't provide JS-based solution.

1

2 Answers 2

1

The solution I finally came up with...

MongoCollection<Document> collection = database.getCollection("myCollection");
collection.find(
    Filters.ne("someArray", Collections.emptyList()), MyDocumentRoot.class
).forEach(rootElement -> {
    for(int i = 0; i < rootElement.getSomeArray().size(); i++){
        Document document = collection.find(Filters.eq("_id", rootElement.getId())).first();
        String color = document.getList("someArray", Document.class)
            .get(i)
            .getString("color");
        collection.updateOne(
            Filters.and(
                Filters.eq("_id", rootElement.getId()),
                Filters.eq("someArray._id", rootElement.getSomeArray().get(i).getId())
            ),
            Updates.combine(
                Updates.set("someArray.$.colors", Collections.singleton(color)),
                Updates.unset("someArray.$.color")
            )
        );
    }
});
Sign up to request clarification or add additional context in comments.

Comments

0

As suggested in the comment section, I am afraid this can be done with a simple update query, however, if you are using MongoDB version 4.2 and above, you can use $merge to your advantage, to generate your updated document and merge it in the existing collection like this:

MongoClient mongoClient = new MongoClient(
                new MongoClientURI(
                        "mongodb://localhost:27017/"
                )
        );
        MongoDatabase database = mongoClient.getDatabase("StackOverflow");
        MongoCollection<Document> collection = database.getCollection("Sample");

        FindIterable<Document> result = (FindIterable<Document>) collection.aggregate(Arrays.asList(new Document("$match",
                        new Document("someArray",
                                new Document("$exists", true)
                                        .append("$ne", Arrays.asList()))),
                new Document("$unwind",
                        new Document("path", "$someArray")),
                new Document("$set",
                        new Document("someArray.colors", Arrays.asList("$someArray.color"))),
                new Document("$unset", "someArray.color"),
                new Document("$group",
                        new Document("_id", "$_id")
                                .append("someArray",
                                        new Document("$push", "$someArray"))),
                new Document("$merge", "collection")));

Here is the working, mongo shell equivalent pipeline.

1 Comment

Thanks for your answer. Unfortunately, I'm using MongoDB 3.6 (for now). In the meantime, I found a Java-based solution. I will publish it now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.