MongoDB: Use save() to update an existing document in a collection
Asked Answered
P

1

8

everybody. I love manga. And now I am learning to love mongo too :-)

There is an explanation of how to use save() to update an existing document in a collection in MongoDB PHP? but I couldn't apply it to the PHP's "reality" :-)

> var mongo = db.things.findOne({name:"mongo"});
> print(tojson(mongo));
{"_id" : "497dab624ee47b3a675d2d9c" , "name" : "mongo"}
> mongo.type = "database";
database
> db.things.save(mongo);
> db.things.findOne({name:"mongo"});
{"_id" : "497dab624ee47b3a675d2d9c" , "name" : "mongo" , "type" : "database"}

Here's my test code:

<?php
$a=array('_id'=>'test_a','field1'=>'anything');
$b=array('_id'=>'test_a','field2'=>'anything else');

$m=new Mongo();
$c=$m->db->test;
$c->save($a);
$c->save($b);//overwrites the previous record

/*
//With update() it works fine
$filter=array('_id'=>'test_a');
$update=array('$set'=>array('field2'=>'anything else'));
$c->update($filter,$update);
//$c->save($filter,$update);//Also tried...
*/

$f=$c->find();
echo $f->count()." found \n";
$i=iterator_to_array($f);//mongo cursos iterator
$m->close();//disconnect mongo

print_r($i);
?>

The fact is that in the PHP example save() overwrites the object while in the JS example it updates it. I wish I can reproduce in PHP the same as in JS.

Thanks in advance.

Polarize answered 1/1, 2011 at 12:35 Comment(5)
In mongodb.org/display/DOCS/Updating#Updating-ModifierOperations it's said that "save() does an upsert if x has an _id field (x is some JSON style object and upsert means update if present; insert if missing)".Polarize
I don't see what the problem is, doesn't the JS and PHP examples do the same thing? Tell us what it is you're expecting. Don't expect us to set up the environment and run your code just to see the output.Torsi
Thanks for the reply. If you run the php example, you will see that save() overwrites the object while in the JS example it updates it. I wish I can reproduce in PHP the same as in JS.Polarize
Hi @Polarize - no need to append [SOLVED] to the title, there are graphical way in which questions with accepted answers are identified in the list.Mensural
Well, I am sorry if you think that way. As far as I know the site is with a serious problem with SEO fighting against scarp. To have the word SOLVED somewhere in the titles can help in solving that problem.Polarize
S
12

These examples aren't doing the same thing ...

In the JS examples you declared the object mongo

> var mongo = db.things.findOne({name:"mongo"});

Then, you modified the same object to add type ...

> mongo.type = "database";

In the PHP example you declared TWO objects ...

$a=array('_id'=>'test_a','field1'=>'anything');
$b=array('_id'=>'test_a','field2'=>'anything else');

This would be like doing the following in the JS shell ...

> var apples = db.things.findOne({name:"mongo"});
> var orange = db.things.findOne({type:"database"});

So apples is a different object/document from oranges ... hence when you run save() and pass the NEW object it totally overwrites the old object.

Your update() worked fine because you searched for the object ...

$filter=array('_id'=>'test_a');

Then ran the update on that object; passing the new field instead of passing a new object to replace it ...

$update=array('$set'=>array('field2'=>'anything else'));
$c->update($filter,$update);

In the JS examples you only had one object so it basically appended it on the update instead of replacing it.

Correct PHP Code

The following PHP would replicate what you were doing in the JS shell ...

<?php

$connection = new Mongo();
$db = $connection->foo;
$collection = $db->testing;

// create new object & save
$a = array('color'=>'red');
$collection->save($a);

// add type = fruit to existing object & save
$a['type'] = 'fruit';
$collection->save($a);

// print out results.
echo "<pre>";
var_dump($collection->findOne(array("color" => "red")));
echo "</pre>";

?>

Note: I'd strongly recommend you don't set your own object _id's and instead let the driver do it for you as I did in the example above.

Scent answered 2/1, 2011 at 3:37 Comment(5)
Justin, thank you for you very brilliant answer. In my example I thought save() was being fed only with the arrays contents, not considering that $a and $b were actually different objects. In fact, in the JS example, there was a findOne() before the "upsert". This findOne() appears in the PHP update(), as you well said, in $filter=array('_id'=>'test_a'); so that whatever comes in the next argument will be inserted into this object. Thank you for you final example, it was perfect.Polarize
And I am feeding Mongo with my only _id in order not to repeat myself, for I need a unique an easy way to identify a value in order to make future updates. It was just natural for me to use the _id field. However, if there is a good reason for not doing this, I would happily put it in other field and let Mongo to decide its own way. Would you please tell me why you recommend not to use the _id field?Polarize
Great! If I answered your question please mark this as the "answer" so it has the big green check mark next to it!Scent
Generally you should use the built in _id (you can always also add your own, separately.) More about that here: mongodb.org/display/DOCS/Object+IDsScent
Actually, setting your own object _id is perfectly acceptable. It specifically states this in the documentation: mongodb.org/display/DOCS/Object+IDs. I'm assuming that you recommend against it because it will ensure a unique _id automatically. In certain situations - that I can't elaborate on in a commenting environment - it is preferable to set your _id manually even if just using the default MongoId. Good answer nonetheless.Accentual

© 2022 - 2024 — McMap. All rights reserved.