1

I need to update multiple embedded docs in mongo using PHP. My layout looks like this:

{
  _id: id,
  visits : {
        visitID: 12
        categories: [{
              catagory_id: 1,
              name: somename,
              count: 11,
              duration: 122
              },
              {
              catagory_id: 1,
              name: some other name,
              count: 11,
              duration: 122
              },
              {
              catagory_id: 2,
              name: yet another name,
              count: 11,
              duration: 122
              }]
   }
}

The document can have more than one visit too.

Now i want to update 2 categories, one with id=1 and name=somename and the other with id=1 and name=some_new_name. Both of them should inc "count" by 1 and "duration" by 45.

First document exists, but second does not.

Im thinking of having a function like this:

function updateCategory($id, $visitID,$category_name,$category_id) {

    $this->profiles->update(
            array(
                '_id' => $id, 
                'visits.visitID' => $visitID,
                'visits.categories.name' => $category_name,
                'visits.categories.id' => $category_id,
            ), 
            array(
                '$inc' =>   array(
                            'visits.categories.$.count' => 1,
                            'visits.categories.$.duration' =>45,
                        ),
            ), 
            array("upsert" => true)
    );  
}   

But with this i need to call the function for each category i will update. Is there any way to do this in one call?

EDIT:

Changed the layout a bit and made "categories" an object instead of array. Then used a combination of "category_id" and "category_name" as property name. Like:

categories: {
              1_somename : {
                  count: 11,
                  duration: 122
              },
              1_some other name : {
                  count: 11,
                  duration: 122
              },
              2_yet another name : {
                  count: 11,
                  duration: 122
              },
}

Then with upsert and something like

$inc: {
 "visits.$.categories.1_somename.d": 100,
 "visits.$.categories.2_yet another name.c": 1
}

i can update several "objects" at a time..

1
  • Seems it will only update one subdoc as multiple refers to root document....hmmm Commented Jun 6, 2011 at 9:48

1 Answer 1

1

Mongodb currently not supporting arrays multiple levels deep updating (jira)

So following code will not work:

'$inc' =>      array( 
   'visits.categories.$.count' => 1, 
   'visits.categories.$.duration' => 123, 
 ),

So there is some solutions around this:

1.Load document => update => save (possible concurrency issues)
2.Reorganize your documents structure like this(and update using one positional operator):

{
  _id: id,
  visits : [{
        visitID: 12
  }],
  categories: [{
              catagory_id: 1,
              name: somename,
              count: 11,
              duration: 122,
              visitID: 12
              }]
   }
}

3.Wait for multiple positional operators support (planning in 2.1 version of mongodb).
4.Reorganize your documents structure somehow else, in order to avoid multiple level arrays nesting.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the answer. I would prefer to avoid the extra read, so instead i changed the layout a bit so categories is now an object instead of array - see edit in original post.

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.