0

I have couple of documens in mongodb as follow:

{
"_id" : ObjectId("54901212f315dce7077204af"),
"Date" : ISODate("2014-10-20T04:00:00.000Z"),
"Type" : "Twitter",
"Entities" : [ 
    {
        "ID" : 4,
        "Name" : "test1",
        "Sentiment" : {
            "Value" : 20,
            "Neutral" : 1
        }
    },
    {
        "ID" : 5,
        "Name" : "test5",
        "Sentiment" : {
            "Value" : 10,
            "Neutral" : 1
        }
    }
]

}

Now I want to update the document that has Entities.ID=4 by adding (Sentiment.Value+4)/2 for example in the above example after update we have 12.

I wrote the following code but I am stuck in the if statement as you can see:

 DBCollection collectionG;
   collectionG = db.getCollection("GraphDataCollection");
    int entityID = 4;
    String entityName = "test";
    BasicDBObject queryingObject = new BasicDBObject();
    queryingObject.put("Entities.ID", entityID);
    DBCursor cursor = collectionG.find(queryingObject);

    if (cursor.hasNext())
    {
        BasicDBObject existingDocument = new BasicDBObject("Entities.ID", entityID);
        //not sure how to update the sentiment.value for entityid=4
    }

First I thought I should unwind the Entities array first to get the value of sentiment but if I do that then how can I wind them again and update the document with the same format as it has now but with the new sentiment value ?

also I found the this link as well : MongoDB - Update objects in a document's array (nested updating)

but I could not understand it since it is not written in java query, can anyone explain how I can do this in java?

Community
  • 1
  • 1
HMdeveloper
  • 2,772
  • 9
  • 45
  • 74
  • http://stackoverflow.com/a/12103403/1254903 Would this help? – Arun A K Jan 08 '15 at 02:28
  • No because it is not a simple update I need to get the current value of sentiment for that entityid and add it by for example 4 and divide it by 2 – HMdeveloper Jan 08 '15 at 02:31
  • I think the depicted document in your question has a different structure than what you actually propose to do. There is no field such as `sentiment.value`, with which performing the mentioned operations we get a value of 12. Can you check if the document structure is correct? – BatScream Jan 08 '15 at 03:07
  • @BatScream Sorry yes you are right I updated the document – HMdeveloper Jan 08 '15 at 03:22

1 Answers1

1

You need to do this in two steps:

  • Get all the _id of the records which contain a Entity with sentiment value 4.
  • During the find, project only the entity sub document that has matched the query, so that we can process it to consume only its Sentiment.Value. Use the positional operator($) for this purpose.
  • Instead of hitting the database every time to update each matched record, use the Bulk API, to queue up the updates and execute it finally.

Create the Bulk operation Writer:

    BulkWriteOperation bulk = col.initializeUnorderedBulkOperation();

Find all the records which contain the value 4 in its Entities.ID field. When you match documents against this query, you would get the whole document returned. But we do not want the whole document, we would like to have only the document's _id, so that we can update the same document using it, and the Entity element in the document that has its value as 4. There may be n other Entity documents, but they do not matter. So to get only the Entity element that matches the query we use the positional operator $.

    DBObject find = new BasicDBObject("Entities.ID",4);
    DBObject project = new BasicDBObject("Entities.$",1);
    DBCursor cursor = col.find(find, project);

What the above could would return is the below document for example(since our example assumes only a single input document). If you notice, it contains only one Entity element that has matched our query.

{
        "_id" : ObjectId("54901212f315dce7077204af"),
        "Entities" : [
                {
                        "ID" : 4,
                        "Name" : "test1",
                        "Sentiment" : {
                                "Value" : 12,
                                "Neutral" : 1
                        }
                }
        ]
}

Iterate each record to queue up for update:

    while(cursor.hasNext()){
        BasicDBObject doc = (BasicDBObject)cursor.next();
        int curVal = ((BasicDBObject)
                     ((BasicDBObject)((BasicDBList)doc.get("Entities")).
                     get(0)).get("Sentiment")).getInt("Value");
        int updatedValue = (curVal+4)/2;
        DBObject query = new BasicDBObject("_id",doc.get("_id"))
                         .append("Entities.ID",4);
        DBObject update = new BasicDBObject("$set",
                          new BasicDBObject("Entities.$.Sentiment.Value",
                                             updatedValue));
        bulk.find(query).update(update);
    }

Finally Update:

    bulk.execute();

You need to do a find() and update() and not simply an update, because currently mongodb does not allow to reference a document field to retrieve its value, modify it and update it with a computed value, in a single update query.

BatScream
  • 19,260
  • 4
  • 52
  • 68
  • 1
    Thanks a lot yes it worked, just a quick question : can you explain DBObject project = new BasicDBObject("Entities.$",1); more? what is for example 1 in that? – HMdeveloper Jan 08 '15 at 05:14
  • 2
    @HamedMinaee - Please see my updated answer. To know more about positional operators click on the `$` symbol in my answer.(There are tow, go through both) – BatScream Jan 08 '15 at 05:41
  • 1
    Now I got it but why 1 why not 2 ? still I do no know what that 1 point to? – HMdeveloper Jan 08 '15 at 05:47
  • 2
    The allowed values for projection are `0` and `1`, `0` will exclude the field, `1` will include the field. It is **not** an index. – BatScream Jan 08 '15 at 05:50