skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    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")
                )
            );
        }
    });
    

  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search