2

Lets say I have this object:

{
"_class" : "com.foo.Person",
"_id" : "2894",
"name" : "Pixel Spacebag",
"photos" : [
    {
        "_id" : 10,
         "amount" : "100"
    },
    {
        "_id" : 11,
         "amount" : "200"
    }
    ]
}

Now, I want to add on an additional field into the "photos" array of photo objects.

I want to add into photo _id = 10 the field that "distributors": "Pixel Studios".

        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(new ObjectId("2984"));
        Update update = new Update();
        update.set("distributor", "Pixel Studios");
        mongoTemplate.updateFirst(query, update, Person.class);

I could get to the Person object correctly but I cannot update the specfic photo _id 10 correctly.

Does anyone know how to update this correctly?

6
  • Is it OK to assume the items in photos are sorted by their _id ? Commented Nov 1, 2015 at 10:44
  • Nope... The id are actually unique id generated by mongodb and are not necessarily sorted. Commented Nov 1, 2015 at 10:54
  • but how are they stored inside the array ? In the example you posted they are sorted Commented Nov 1, 2015 at 10:56
  • Yes, but that is just by chance. When a person object is created inside mongodb, a photo object is create alongside it. For efficiency purposes, some of the fields of the photo object are embedded into the person object as an array of photo objects. If I make a change to the photo object, I want to be able to make a change to the array of photo objects within the person object. Commented Nov 1, 2015 at 10:58
  • You still aren't answering how you are creating the array photos. You can randomly $push into the array or keep it sorted. If it is not ordered, than my answer bellow will have a step of search and replace inside the array. But conceptually it stays the same. Commented Nov 1, 2015 at 11:03

2 Answers 2

2

I dug around for a little while and actually found a solution for this. It comes in the form of a positional operator:

https://docs.mongodb.org/manual/reference/operator/projection/positional/

I also read this thread related to positional operators and how to update elements of an array here:

How to update value of specific embedded document, inside an array, of a specific document in MongoDB?

com.mongodb.DBCollection collection = mongoTemplate.getDb().getCollection("person");
            ObjectId _id = new ObjectId("2984");
            ObjectId photos_id = new ObjectId(10);

            BasicDBObject query = new BasicDBObject();
            query.put("_id", _id);
            query.put("photos._id", photos_id);

            BasicDBObject data = new BasicDBObject();
            data.put("photos.$.distributor", "Pixel Studios");

            BasicDBObject command = new BasicDBObject();
            command.put("$set", data);

            collection.update(query, command);
Sign up to request clarification or add additional context in comments.

1 Comment

Code is added to my solution.
0

It's much easier to modify the object you get from the database on the client side.
I am assuming here that the documents inside photos are ordered by the _id, if it's not the case you'll have to search inside the array, and thus have a few more steps. First, here is how you do this in the mongo shell (JavaScript), but this should not be much different in Java:

> v = db.ppl.find({_id: "2894"}).next();
{
    "_id" : "2894",
    "_class" : "com.foo.Person",
    "name" : "Pixel Spacebag",
    "photos" : [
        {
            "_id" : 10,
            "amount" : "100"
        },
        {
            "_id" : 11,
            "amount" : "200"
        }
    ]
}
> v.photos[0]
{ "_id" : 10, "amount" : "100" }
> v.photos[0].distributor = "pixel studios"
> v.photos[0]
{ "_id" : 10, "amount" : "100" }
> v.photos[0].distributor = "pixel studios"

> db.ppl.update({_id:"2894"}, {$set : {"photos": v.photos}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.ppl.findOne()
{
    "_id" : "2894",
    "_class" : "com.foo.Person",
    "name" : "Pixel Spacebag",
    "photos" : [
        {
            "_id" : 10,
            "amount" : "100",
            "distributor" : "pixel studios"
        },
        {
            "_id" : 11,
            "amount" : "200"
        }
    ]
}

So, where I am using . for assignment you'll have to use the appropriate get or set method with possible casting for the right type.

The following code is the Java equivalent for the JS I posted above:

Document example = new Document("_class", "com.foo.Person")
                .append("_id", "2894")
                .append("name", "Pixel Spacebag");

        List<Document> photos = new ArrayList<Document>();
        photos.add(new Document("_id", 10).append("amount", "100"));
        photos.add(new Document("_id", 11).append("amount", "200"));

        example.append("photos", photos);
        collection.insertOne(example);

        FindIterable<Document> res = collection.find(new Document("_id", "2894"));
        Document temp = res.iterator().next();
        List<Document> photosDoc = (List<Document>) temp.get("photos");
        Document docToUpdate = photosDoc.get(0);
        docToUpdate.append("distributor", "pixel studios");
        photosDoc.set(0, docToUpdate);
        collection.updateOne(new Document("_id", temp.get("_id")),
                new Document("$set", new Document("photos", photosDoc)));

        /* Check that everything worked */
        res = collection.find(new Document("_id", "2894"));
        Document updatedDocument = res.iterator().next();
        System.out.println(updatedDocument);
        /* outputs:
         Document{{_id=2894, _class=com.foo.Person, name=Pixel Spacebag, photos=[Document{{_id=10, amount=100, distributor=pixel studios}}, Document{{_id=11, amount=200}}]}}
         */

Comments

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.