Alsatian Alsatian - 3 months ago 19
PHP Question

MongoDB with PHP : Bulk.upsert() insertion with PHP function Execute

I want to use this MongoDB query from a PHP script :

Bulk.find(<query>).upsert().update(<update>);


Doc from the MongoDB operation :
http://docs.mongodb.org/manual/reference/method/Bulk.find.upsert/#Bulk.find.update

Doc from the PHP execute operation :
http://php.net/manual/en/mongodb.execute.php (see Example #3 Scope example)

I'm trying tu use the example no. 3 from the PHP doc to do a Bulk operation for my app.

But an equivalent code doesnt work.

The JS function I pass to MongoDB for the upsert operations thrue a PHP MongoCode Object: (http://php.net/manual/en/class.mongocode.php)

$function="function(idClient,name){
bulk.find({idc:idClient,pid:partnerId}).upsert().update(
{
\$setOnInsert: {idc:idClient,pid:partnerId,dateCreate:new Date()},
\$set: {name:name,n:0},
});}";

$function=new \MongoCode($function,array('partnerId'=>$partnerId));


The 3 variants I testet :

With var variant :

// 1
$db->execute(new \MongoCode('var bulk = db.MyCollection.initializeUnorderedBulkOp();'));

while ([...])
{
[...]
// 2
$db->execute($function,array($idClient,$name));
}

// 3
$db->execute(new \MongoCode('bulk.execute();'));


1 returns : "exception: SyntaxError: Unexpected token var"

2 returns : "exception: ReferenceError: bulk is not defined"

3 returns : "exception: ReferenceError: bulk is not defined"

Without var variant :

// 1
$db->execute(new \MongoCode('var bulk = db.MyCollection.initializeUnorderedBulkOp();'));

while ([...])
{
[...]
// 2
$db->execute($function,array($idClient,$name));
}

// 3
$db->execute(new \MongoCode('bulk.execute();'));


1 returns : array([...],"OK"=>1)

2 in first occurence returns : array([...],"OK"=>1)

2 in second occurence returns : "exception: ReferenceError: bulk is not defined"

3 returns : "exception: ReferenceError: bulk is not defined"

Without var variant without while:

// 1
$db->execute(new \MongoCode('var bulk = db.MyCollection.initializeUnorderedBulkOp();'));

//2
$db->execute($function,array($idClient,$name));


// 3
$db->execute(new \MongoCode('bulk.execute();'));


1 returns : array([...],"OK"=>1)

2 returns : array([...],"OK"=>1)

3 returns : array([...],"OK"=>1)

The 3rd variant works but, I want to upsert 240 000 documents, and I normaly don't have to create a new UnorderedBulkOp() for each upsert.

I'm Using MongoDB 2.6.7 with PHP 2.6.4.

Does somebody know how I can do ?

Thnak you

Answer

This appears to be a duplicate of your question in PHP-1393, which was opened on the PHP driver project. I'll repeat by original reply for the benefit of anyone else that comes across this question.


The eval command, which MongoDB::execute() invokes, does not share context between multiple invocations. If you're going to use server-side JavaScript evaluation, you'll need to pass in your entire script as a MongoCode object and invoke the command once.

That said, you are much better off just using MongoUpdateBatch and the add() method here, since all of your operations are updates. Using the driver to prepare and execute the write commands will be far more efficient that relying on server-side JavaScript.

As an example, we could use the driver's batch API to upsert 100 documents like so:

$batch = new MongoUpdateBatch($collection, ['ordered' => false]);

for ($i = 0; $i < 100; $i++) {
    $batch->add([
        'q' => [ 'idc' => $i ],
        'u' => [
            '$setOnInsert' => [ 'idc' => $i ],
            '$set' => [ 'name' => 'Test' ],
        ],
        'upsert' => true,
    ]);
}

$batch->execute();

Note: the 1.x PHP driver does not provide the API you find in the MongoDB shell, which allows you to mix insert, update, and delete operations in the same batch. MongoWriteBatch (and its sub-classes) allow you to construct operation-specific batches, and have the driver handle splitting them into an optimal number of write commands.

Going forward, the initializeUnorderedBulkOp() and initializeOrderedBulkOp() API you find in the shell (and some other drivers) will ultimately be superseded by our recently published CRUD API specification. The CRUD API spec defines a much more concise interface for issuing bulk operations without requiring a fluent API (see the bulkWrite() method). You can expect to see that in the next major version of the PHP driver.