use
MongoDBTest
qw/skip_unless_mongod build_client get_test_db server_version/
;
skip_unless_mongod();
my
$conn
= build_client();
my
$testdb
= get_test_db(
$conn
);
my
$coll
=
$testdb
->get_collection(
"test_collection"
);
my
$server_version
= server_version(
$conn
);
my
$ismaster
=
$testdb
->run_command( {
ismaster
=> 1 } );
my
$server_status
=
$testdb
->run_command( {
serverStatus
=> 1 } );
my
$is_standalone
=
$conn
->topology_type eq
'Single'
&& !
exists
$server_status
->{repl};
my
$server_does_bulk
=
$server_version
>= v2.5.5;
my
$supports_collation
=
$server_version
>= v3.3.9;
my
$case_insensitive_collation
= {
locale
=>
"en_US"
,
strength
=> 2 };
sub
_truncate {
return
(
length
(
$_
[0]) > 1600 ? (
substr
(
$_
[0],0,1600).
"..."
) :
$_
[0] );
}
sub
_bulk_write_result {
return
MongoDB::BulkWriteResult->new(
acknowledged
=> 1,
write_errors
=> [],
write_concern_errors
=> [],
modified_count
=> 0,
inserted_count
=> 0,
upserted_count
=> 0,
matched_count
=> 0,
deleted_count
=> 0,
upserted
=> [],
inserted
=> [],
batch_count
=> 0,
op_count
=> 0,
@_
,
);
}
sub
_number_of_servers {
return
0
unless
$ismaster
->{hosts};
return
@{
$ismaster
->{hosts} } + @{
$ismaster
->{passives} // [] };
}
subtest
"constructors"
=>
sub
{
my
@constructors
=
qw(
initialize_ordered_bulk_op initialize_unordered_bulk_op
ordered_bulk unordered_bulk
)
;
for
my
$method
(
@constructors
) {
my
$bulk
=
$coll
->
$method
;
isa_ok(
$bulk
,
'MongoDB::BulkWrite'
,
$method
);
if
(
$method
=~ /unordered/ ) {
ok( !
$bulk
->ordered,
"ordered attr is false"
);
}
else
{
ok(
$bulk
->ordered,
"ordered attr is true"
);
}
is(
refaddr
$bulk
->collection,
refaddr
$coll
,
"MongoDB::BulkWrite holds ref to originating Collection"
);
}
};
note(
"QA-477 INSERT"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: insert errors"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
my
%bad_args
= (
LIST
=> [ {}, {} ],
EMPTY
=> [],
);
for
my
$k
(
sort
keys
%bad_args
) {
like(
exception {
$bulk
->insert_one( @{
$bad_args
{
$k
} } ) },
qr/reference/
,
"insert( $k ) throws an error"
);
}
like(
exception {
$bulk
->insert_one(
'foo'
) },
qr/reference/
,
"insert( 'foo' ) throws an error"
);
like(
exception {
$bulk
->insert_one( [
'foo'
] ) },
qr{must have key/value pairs}
,
"insert( ['foo'] ) throws an error"
,
);
like(
exception {
$bulk
->find( {} )->insert_one( {} ) },
qr/^Can't locate object method "insert_one"/
,
"find({})->insert_one({}) throws an error"
,
);
is( exception {
$bulk
->insert_one( {
'$key'
=> 1 } ) },
undef
,
"queuing insertion of document with \$key is allowed"
);
my
$err
= exception {
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::WriteError'
,
"executing insertion with \$key"
);
};
subtest
"$method: successful insert"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
is(
$coll
->count_documents({}), 0,
"no docs in collection"
);
$bulk
->insert_one( {
_id
=> 1 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on insert"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents({}), 1,
"one doc in collection"
);
isa_ok(
$result
,
'MongoDB::WriteResult'
,
"result object"
);
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
inserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 1,
batch_count
=> 1,
inserted
=> [ {
index
=> 0,
_id
=> 1 } ],
),
"result object correct"
) or diag _truncate explain
$result
;
};
subtest
"$method insert without _id"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
is(
$coll
->count_documents({}), 0,
"no docs in collection"
);
my
$doc
= {};
$bulk
->insert_one(
$doc
);
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on insert"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents({}), 1,
"one doc in collection"
);
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
inserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 1,
batch_count
=> 1,
inserted
=> [ {
index
=> 0,
_id
=> obj_isa(
"BSON::OID"
) } ],
),
"result object correct"
);
my
$id
=
$coll
->find_one()->{_id};
is(
$id
->_get_pid, $$ & 0xffff,
"generated ID has our PID"
);
};
}
note(
"QA-477 FIND"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: find"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
like(
exception {
$bulk
->find },
qr/find requires a criteria document/
,
"find without doc selector throws exception"
);
};
}
note(
"QA-477 UPDATE and UPDATE_ONE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"update and update_one errors with $method"
=>
sub
{
my
$bulk
;
my
%bad_args
= (
SCALAR
=> [
'foo'
],
EMPTY
=> [],
);
for
my
$update
(
qw/update_many update_one/
) {
$bulk
=
$coll
->
$method
;
for
my
$k
(
sort
keys
%bad_args
) {
like(
exception {
$bulk
->find( {} )->
$update
( @{
$bad_args
{
$k
} } ) },
qr/argument to $update must be a single hashref, arrayref, Tie::IxHash or BSON::Array/
,
"$update( $k ) throws an error"
);
}
$bulk
=
$coll
->
$method
;
like(
exception {
$bulk
->
$update
( {
'$set'
=> {
x
=> 1 } } ) },
qr/^Can't locate object method "$update"/
,
"$update on bulk object (without find) throws an error"
,
);
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->
$update
( {
key
=> 1 } );
like(
exception {
$bulk
->execute },
qr/update document must only contain update operators/
,
"single non-op key in $update doc throws exception"
);
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->
$update
( [
key
=> 1,
'$key'
=> 1 ]);
like(
exception {
$bulk
->execute },
qr/update document must only contain update operators/
,
"first non-op key in $update doc throws exception"
);
}
};
subtest
"update all docs with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$coll
->insert_one(
$_
)
for
map
{ {
key
=>
$_
} } 1, 2;
my
@docs
=
$coll
->find( {} )->all;
$bulk
->find( {} )->update_many( {
'$set'
=> {
x
=> 3 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
matched_count
=> 2,
modified_count
=> (
$server_does_bulk
? 2 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
if
(
$server_does_bulk
) {
ok(
$result
->has_modified_count,
"newer server has_modified_count"
);
}
else
{
ok( !
$result
->has_modified_count,
"older server has_modified_count"
);
}
$_
->{x} = num(3)
for
@docs
;
cmp_deeply( [
$coll
->find( {} )->all ], \
@docs
,
"all documents updated"
);
};
subtest
"update only matching docs with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$coll
->insert_one(
$_
)
for
map
{ {
key
=>
$_
} } 1, 2;
my
@docs
=
$coll
->find( {} )->all;
$bulk
->find( {
key
=> 1 } )->update_many( {
'$set'
=> {
x
=> 1 } } );
$bulk
->find( {
key
=> 2 } )->update_many( {
'$set'
=> {
x
=> 2 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
is_deeply(
$result
,
_bulk_write_result(
matched_count
=> 2,
modified_count
=> (
$server_does_bulk
? 2 :
undef
),
op_count
=> 2,
batch_count
=>
$server_does_bulk
? 1 : 2,
),
"result object correct"
);
$_
->{x} =
$_
->{key}
for
@docs
;
cmp_deeply( [
$coll
->find( {} )->all ], \
@docs
,
"all documents updated"
);
};
subtest
"update_one with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$coll
->insert_one(
$_
)
for
map
{ {
key
=>
$_
} } 1, 2;
$bulk
->find( {} )->update_one( {
'$set'
=> {
key
=> 3 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
is_deeply(
$result
,
_bulk_write_result(
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
);
is(
$coll
->count_documents( {
key
=> 3 } ), 1,
"one document updated"
);
};
subtest
"update and update_one with collation, using $method"
=>
sub
{
for
my
$update
(
qw/update_many update_one/
) {
$coll
->drop;
$coll
->insert_one( {
key
=>
"a"
} );
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
key
=>
"b"
} );
$bulk
->find( {
key
=>
"A"
} )->collation(
$case_insensitive_collation
)
->
$update
( {
'$set'
=> {
key
=>
"b"
} } );
my
$err
= exception {
$bulk
->execute };
if
(
$supports_collation
) {
is(
$err
,
undef
,
"bulk update_one w/ collation"
);
is(
$coll
->count_documents( {
key
=>
"b"
} ), 2,
"collection updated"
);
}
else
{
like(
$err
,
qr/MongoDB host '.*:\d+' doesn't support collation/
,
"bulk update_one w/ collation returns error if unsupported"
);
is(
$coll
->count_documents( {
key
=>
"b"
} ), 0,
"collection not updated"
);
}
}
};
}
note(
"QA-477 REPLACE_ONE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"replace_one errors with $method"
=>
sub
{
my
$bulk
;
my
%bad_args
= (
SCALAR
=> [
'foo'
],
EMPTY
=> [],
);
$bulk
=
$coll
->
$method
;
for
my
$k
(
sort
keys
%bad_args
) {
like(
exception {
$bulk
->find( {} )->replace_one( @{
$bad_args
{
$k
} } ) },
qr/argument to replace_one must be a single hashref, arrayref, Tie::IxHash or BSON::Array/
,
"replace_one( $k ) throws an error"
);
}
like(
exception {
$bulk
->replace_one( {
'$set'
=> {
x
=> 1 } } ) },
qr/^Can't locate object method "replace_one"/
,
"replace_one on bulk object (without find) throws an error"
,
);
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->replace_one( {
'$key'
=> 1 } );
like(
exception {
$bulk
->execute },
qr/replacement document must not contain update operators/
,
"single op key in replace_one doc throws exception"
);
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->replace_one( [
'$key'
=> 1,
key
=> 1 ] );
like(
exception {
$bulk
->execute },
qr/replacement document must not contain update operators/
,
"mixed op and non-op key in replace_one doc throws exception"
);
};
subtest
"replace_one with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
$bulk
->find( {} )->replace_one( {
key
=> 3 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
is_deeply(
$result
,
_bulk_write_result(
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
);
my
$distinct
= [
$coll
->distinct(
"key"
)->all ];
cmp_deeply(
$distinct
, bag( num(1), num(3) ),
"only one document replaced"
);
};
subtest
"replace_one with collation, using $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
"a"
} );
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
key
=>
"b"
} );
$bulk
->find( {
key
=>
"A"
} )->collation(
$case_insensitive_collation
)
->replace_one( {
key
=>
"b"
} );
my
$err
= exception {
$bulk
->execute };
if
(
$supports_collation
) {
is(
$err
,
undef
,
"bulk replace_one w/ collation"
);
is(
$coll
->count_documents( {
key
=>
"b"
} ), 2,
"collection updated"
);
}
else
{
like(
$err
,
qr/MongoDB host '.*:\d+' doesn't support collation/
,
"bulk replace_one w/ collation returns error if unsupported"
);
is(
$coll
->count_documents( {
key
=>
"b"
} ), 0,
"collection not updated"
);
}
};
}
note(
"QA-477 UPSERT-UPDATE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"upsert errors with $method"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
like(
exception {
$bulk
->upsert() },
qr/^Can't locate object method "upsert"/
,
"upsert on bulk object (without find) throws an error"
,
);
like(
exception {
$bulk
->find( {} )->upsert( {} ) },
qr/the upsert method takes no arguments/
,
"upsert( NONEMPTY ) throws an error"
);
};
subtest
"upsert-update insertion with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->update_many( {
'$set'
=> {
x
=> 1 } } );
$bulk
->find( {
key
=> 2 } )->upsert->update_many( {
'$set'
=> {
x
=> 2 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
upserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
upserted
=> [ {
index
=> 1,
_id
=> ignore() } ],
op_count
=> 2,
batch_count
=>
$server_does_bulk
? 1 : 2,
),
"result object correct"
) or diag _truncate explain
$result
;
cmp_deeply(
[
$coll
->find( {} )->all ],
[ {
_id
=> ignore(),
key
=> num(2),
x
=> num(2) } ],
"upserted document correct"
);
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->update_many( {
'$set'
=> {
x
=> 1 } } );
$bulk
->find( {
key
=> 2 } )->upsert->update_many( {
'$set'
=> {
x
=> 2 } } );
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on second upsert-update"
) or diag _truncate explain
$err
;
cmp_deeply(
$result
,
_bulk_write_result(
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 2,
batch_count
=>
$server_does_bulk
? 1 : 2,
),
"result object correct"
) or diag _truncate explain
$result
;
};
subtest
"upsert-update updates with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
my
@docs
=
$coll
->find( {} )->all;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->upsert->update_many( {
'$set'
=> {
x
=> 1 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
matched_count
=> 2,
modified_count
=> (
$server_does_bulk
? 2 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
$_
->{x} = num(1)
for
@docs
;
cmp_deeply( [
$coll
->find( {} )->all ], \
@docs
,
"all documents updated"
);
};
subtest
"upsert-update large doc with $method"
=>
sub
{
$coll
->drop;
my
$big_string
=
"a"
x ( 16 * 1024 * 1024 -
$server_does_bulk
? 41 : 97 );
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=>
"1"
} )->upsert->update_many( {
'$set'
=> {
x
=>
$big_string
} } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-update"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
upserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
upserted
=> [ {
index
=> 0,
_id
=> ignore() } ],
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
};
}
note(
"QA-477 UPSERT-UPDATE_ONE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"upsert-update_one insertion with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->update_one( {
'$set'
=> {
x
=> 1 } } );
$bulk
->find( {
key
=> 2 } )->upsert->update_one( {
'$set'
=> {
x
=> 2 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-update_one"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
upserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
upserted
=> [ {
index
=> 1,
_id
=> ignore() } ],
op_count
=> 2,
batch_count
=>
$server_does_bulk
? 1 : 2,
),
"result object correct"
) or diag _truncate explain
$result
;
cmp_deeply(
[
$coll
->find( {} )->all ],
[ {
_id
=> ignore(),
key
=> num(2),
x
=> num(2) } ],
"upserted document correct"
);
};
subtest
"upsert-update_one (no insert) with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
my
@docs
=
$coll
->find( {} )->all;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->upsert->update_one( {
'$set'
=> {
x
=> 2 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-update_one"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
$docs
[0]{x} = num(2);
my
@got
=
$coll
->find( {} )->all;
cmp_deeply( \
@got
, bag(
@docs
),
"updated document correct"
)
or diag _truncate explain \
@got
;
};
}
note(
"QA-477 UPSERT-REPLACE_ONE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"upsert-replace_one insertion with $method"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->replace_one( {
x
=> 1 } );
$bulk
->find( {
key
=> 2 } )->upsert->replace_one( {
x
=> 2 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-replace_one"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
upserted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
upserted
=> [ {
index
=> 1,
_id
=> ignore() } ],
op_count
=> 2,
batch_count
=>
$server_does_bulk
? 1 : 2,
),
"result object correct"
) or diag _truncate explain
$result
;
cmp_deeply(
[
$coll
->find( {} )->all ],
[ {
_id
=> ignore(),
x
=> num(2) } ],
"upserted document correct"
);
};
subtest
"upsert-replace_one (no insert) with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
my
@docs
=
$coll
->find( {} )->all;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->upsert->replace_one( {
x
=> 2 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on upsert-replace_one"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
$docs
[0]{x} = num(2);
delete
$docs
[0]{key};
my
@got
=
$coll
->find( {} )->all;
cmp_deeply( \
@got
, bag(
@docs
),
"updated document correct"
)
or diag _truncate explain \
@got
;
};
}
note(
"QA-477 delete_many"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"delete_many errors with $method"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
like(
exception {
$bulk
->delete_many() },
qr/^Can't locate object method "delete_many"/
,
"delete_many on bulk object (without find) throws an error"
,
);
};
subtest
"delete_many all with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->delete_many;
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on delete_many"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
deleted_count
=> 2,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
is(
$coll
->count_documents({}), 0,
"all documents removed"
);
};
subtest
"delete_many matching with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
key
=> 1 } )->delete_many;
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on delete_many"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
deleted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
cmp_deeply(
[
$coll
->find( {} )->all ],
[ {
_id
=> ignore(),
key
=> num(2) } ],
"correct object remains"
);
};
subtest
"delete_many with collation, using $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
"a"
} );
$coll
->insert_one( {
key
=>
"a"
} );
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
key
=>
"b"
} );
$bulk
->find( {
key
=>
"A"
} )->collation(
$case_insensitive_collation
)
->delete_many;
my
$err
= exception {
$bulk
->execute };
if
(
$supports_collation
) {
is(
$err
,
undef
,
"bulk delete_many w/ collation"
);
cmp_deeply(
[
$coll
->find( {} )->all ],
bag( {
_id
=> ignore(),
key
=> str(
"b"
) } ),
"collection updated"
);
}
else
{
like(
$err
,
qr/MongoDB host '.*:\d+' doesn't support collation/
,
"bulk delete_many w/ collation returns error if unsupported"
);
cmp_deeply(
[
$coll
->find( {} )->all ],
bag( {
_id
=> ignore(),
key
=> str(
"a"
) }, {
_id
=> ignore(),
key
=> str(
"a"
) } ),
"collection not updated"
);
}
};
}
note(
"QA-477 delete_one"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"delete_one errors with $method"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
like(
exception {
$bulk
->delete_one() },
qr/^Can't locate object method "delete_one"/
,
"delete_one on bulk object (without find) throws an error"
,
);
};
subtest
"delete_one with $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=> 1 } )
for
1 .. 2;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {} )->delete_one;
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on delete_one"
) or diag _truncate explain
$err
;
isa_ok(
$result
,
'MongoDB::BulkWriteResult'
,
"result object"
);
cmp_deeply(
$result
,
_bulk_write_result(
deleted_count
=> 1,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
op_count
=> 1,
batch_count
=> 1,
),
"result object correct"
) or diag _truncate explain
$result
;
is(
$coll
->count_documents({}), 1,
"only one doc removed"
);
};
subtest
"delete_one with collation, using $method"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
"a"
} );
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
key
=>
"b"
} );
$bulk
->find( {
key
=>
"A"
} )->collation(
$case_insensitive_collation
)
->delete_one;
my
$err
= exception {
$bulk
->execute };
if
(
$supports_collation
) {
is(
$err
,
undef
,
"bulk delete_one w/ collation"
);
cmp_deeply(
[
$coll
->find( {} )->all ],
bag( {
_id
=> ignore(),
key
=> str(
"b"
) } ),
"collection updated"
);
}
else
{
like(
$err
,
qr/MongoDB host '.*:\d+' doesn't support collation/
,
"bulk delete_one w/ collation returns error if unsupported"
);
cmp_deeply(
[
$coll
->find( {} )->all ],
bag( {
_id
=> ignore(),
key
=> str(
"a"
) } ),
"collection not updated"
);
}
};
}
note(
"QA-477 MIXED OPERATIONS, UNORDERED"
);
subtest
"mixed operations, unordered"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
a
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->initialize_unordered_bulk_op;
$bulk
->find( {
a
=> 1 } )->update_many( {
'$set'
=> {
b
=> 1 } } );
$bulk
->find( {
a
=> 2 } )->delete_many;
$bulk
->insert_one( {
a
=> 3 } );
$bulk
->find( {
a
=> 4 } )->upsert->update_one( {
'$set'
=> {
b
=> 4 } } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on mixed operations"
) or diag _truncate explain
$err
;
cmp_deeply(
$result
,
_bulk_write_result(
inserted_count
=> 1,
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
upserted_count
=> 1,
deleted_count
=> 1,
op_count
=> 4,
batch_count
=>
$server_does_bulk
? 3 : 4,
upserted
=> [ {
index
=> ignore(),
_id
=> obj_isa(
"BSON::OID"
) } ],
inserted
=> [ {
index
=> ignore(),
_id
=> obj_isa(
"BSON::OID"
) } ],
),
"result object correct"
) or diag _truncate explain
$result
;
};
note(
"QA-477 MIXED OPERATIONS, ORDERED"
);
subtest
"mixed operations, ordered"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->insert_one( {
a
=> 1 } );
$bulk
->find( {
a
=> 1 } )->update_one( {
'$set'
=> {
b
=> 1 } } );
$bulk
->find( {
a
=> 2 } )->upsert->update_one( {
'$set'
=> {
b
=> 2 } } );
$bulk
->insert_one( {
a
=> 3 } );
$bulk
->find( {
a
=> 3 } )->delete_many;
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on mixed operations"
) or diag _truncate explain
$err
;
cmp_deeply(
$result
,
_bulk_write_result(
inserted_count
=> 2,
upserted_count
=> 1,
matched_count
=> 1,
modified_count
=> (
$server_does_bulk
? 1 :
undef
),
deleted_count
=> 1,
op_count
=> 5,
batch_count
=>
$server_does_bulk
? 4 : 5,
upserted
=> [ {
index
=> 2,
_id
=> obj_isa(
"BSON::OID"
) } ],
inserted
=> [
{
index
=> 0,
_id
=> obj_isa(
"BSON::OID"
) },
{
index
=> 3,
_id
=> obj_isa(
"BSON::OID"
) },
],
),
"result object correct"
) or diag _truncate explain
$result
;
};
note(
"QA-477 UNORDERED BATCH WITH ERRORS"
);
subtest
"unordered batch with errors"
=>
sub
{
$coll
->drop;
$coll
->indexes->create_one( [
a
=> 1 ], {
unique
=> 1 } );
my
$bulk
=
$coll
->initialize_unordered_bulk_op;
$bulk
->insert_one( {
b
=> 1,
a
=> 1 } );
$bulk
->find( {
b
=> 2 } )->upsert->update_one( {
'$set'
=> {
a
=> 1 } } );
$bulk
->find( {
b
=> 3 } )->upsert->update_one( {
'$set'
=> {
a
=> 2 } } );
$bulk
->find( {
b
=> 2 } )->upsert->update_one( {
'$set'
=> {
a
=> 1 } } );
$bulk
->insert_one( {
b
=> 4,
a
=> 3 } );
$bulk
->insert_one( {
b
=> 5,
a
=> 1 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
)
or diag _truncate explain
$err
;
my
$details
=
$err
->result;
is(
$details
->op_count, 6,
"op_count"
);
is(
$details
->batch_count,
$server_does_bulk
? 2 : 6,
"batch_count"
);
if
(
$details
->inserted_count == 2 ) {
note(
"inserts went first"
);
is(
$details
->inserted_count, 2,
"inserted_count"
);
is(
$details
->upserted_count, 1,
"upserted_count"
);
is(
$details
->deleted_count, 0,
"deleted_count"
);
is(
$details
->matched_count, 0,
"matched_count"
);
is(
$details
->modified_count, (
$server_does_bulk
? 0 :
undef
),
"modified_count"
);
is(
$details
->count_write_errors, 3,
"writeError count"
)
or diag _truncate explain
$details
;
cmp_deeply(
$details
->upserted, [ {
index
=> 2,
_id
=> obj_isa(
"BSON::OID"
) }, ],
"upsert list"
);
}
else
{
note(
"updates went first"
);
is(
$details
->inserted_count, 1,
"inserted_count"
);
is(
$details
->upserted_count, 2,
"upserted_count"
);
is(
$details
->deleted_count, 0,
"deleted_count"
);
is(
$details
->matched_count, 1,
"matched_count"
);
is(
$details
->modified_count, (
$server_does_bulk
? 0 :
undef
),
"modified_count"
);
is(
$details
->count_write_errors, 2,
"writeError count"
)
or diag _truncate explain
$details
;
cmp_deeply(
$details
->upserted,
[
{
index
=> 1,
_id
=> obj_isa(
"BSON::OID"
) },
{
index
=> 2,
_id
=> obj_isa(
"BSON::OID"
) },
],
"upsert list"
);
}
my
$distinct
= [
$coll
->distinct(
"a"
)->all ];
cmp_deeply(
$distinct
, bag(
map
{ num(
$_
) } 1 .. 3 ),
"distinct keys"
);
};
note(
"QA-477 ORDERED BATCH WITH ERRORS"
);
subtest
"ordered batch with errors"
=>
sub
{
$coll
->drop;
$coll
->indexes->create_one( [
a
=> 1 ], {
unique
=> 1 } );
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->insert_one( {
b
=> 1,
a
=> 1 } );
$bulk
->find( {
b
=> 2 } )->upsert->update_one( {
'$set'
=> {
a
=> 1 } } );
$bulk
->find( {
b
=> 3 } )->upsert->update_one( {
'$set'
=> {
a
=> 2 } } );
$bulk
->find( {
b
=> 2 } )->upsert->update_one( {
'$set'
=> {
a
=> 1 } } );
$bulk
->insert_one( {
b
=> 4,
a
=> 3 } );
$bulk
->insert_one( {
b
=> 5,
a
=> 1 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
);
my
$details
=
$err
->result;
is(
$details
->upserted_count, 0,
"upserted_count"
);
is(
$details
->matched_count, 0,
"matched_count"
);
is(
$details
->deleted_count, 0,
"deleted_count"
);
is(
$details
->modified_count, (
$server_does_bulk
? 0 :
undef
),
"modified_count"
);
is(
$details
->inserted_count, 1,
"inserted_count"
);
is(
$details
->op_count,
$server_does_bulk
? 4 : 2,
"op_count"
);
is(
$details
->batch_count, 2,
"op_count"
);
is(
$details
->count_write_errors, 1,
"writeError count"
);
is(
$details
->write_errors->[0]{code}, 11000,
"error code"
);
is(
$details
->write_errors->[0]{
index
}, 1,
"error index"
);
ok(
length
$details
->write_errors->[0]{errmsg},
"error string"
);
cmp_deeply(
$details
->write_errors->[0]{op},
{
q =>
methods([
'FETCH'
,
'b'
] => 3 ),
u
=> obj_isa(
$server_does_bulk
?
'BSON::Raw'
:
'Tie::IxHash'
),
multi
=> false,
upsert
=> true,
},
"error op"
) or diag _truncate explain
$details
->write_errors->[0]{op};
is(
$coll
->count_documents({}), 1,
"subsequent inserts did not run"
);
};
note(
"QA-477 BATCH SPLITTING: maxBsonObjectSize"
);
subtest
"ordered batch split on size"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
my
$big_string
=
"a"
x ( 4 * 1024 * 1024 );
$bulk
->insert_one( {
_id
=>
$_
,
a
=>
$big_string
} )
for
0 .. 5;
$bulk
->insert_one( {
_id
=> 0 } );
$bulk
->insert_one( {
_id
=> 100 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
)
or diag
"CAUGHT ERROR: $err"
;
my
$details
=
$err
->result;
my
$errdoc
=
$details
->write_errors->[0];
is(
$details
->inserted_count, 6,
"inserted_count"
);
cmp_deeply(
$details
->inserted_ids,
{
map
{
$_
=>
$_
} 0 .. 5 },
"inserted_ids correct"
);
is(
$details
->count_write_errors, 1,
"count_write_errors"
);
is(
$errdoc
->{code}, 11000,
"error code"
) or diag _truncate explain
$errdoc
;
is(
$errdoc
->{
index
}, 6,
"error index"
);
ok(
length
(
$errdoc
->{errmsg} ),
"error message"
);
is(
$coll
->count_documents({}), 6,
"collection count"
);
};
subtest
"unordered batch split on size"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_unordered_bulk_op;
my
$big_string
=
"a"
x ( 4 * 1024 * 1024 );
$bulk
->insert_one( {
_id
=>
$_
,
a
=>
$big_string
} )
for
0 .. 5;
$bulk
->insert_one( {
_id
=> 0 } );
$bulk
->insert_one( {
_id
=> 100 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
)
or diag
$err
;
my
$details
=
$err
->result;
my
$errdoc
=
$details
->write_errors->[0];
is(
$details
->inserted_count, 7,
"inserted_count"
);
is(
$details
->count_write_errors, 1,
"count_write_errors"
);
is(
$errdoc
->{code}, 11000,
"error code"
) or diag _truncate explain
$errdoc
;
is(
$errdoc
->{
index
}, 6,
"error index"
);
ok(
length
(
$errdoc
->{errmsg} ),
"error message"
);
is(
$coll
->count_documents({}), 7,
"collection count"
);
};
note(
"QA-477 BATCH SPLITTING: maxWriteBatchSize"
);
subtest
"ordered batch split on number of ops"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->insert_one( {
_id
=>
$_
} )
for
0 .. 1999;
$bulk
->insert_one( {
_id
=> 0 } );
$bulk
->insert_one( {
_id
=> 10000 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
)
or diag
$err
;
my
$details
=
$err
->result;
my
$errdoc
=
$details
->write_errors->[0];
is(
$details
->inserted_count, 2000,
"inserted_count"
);
cmp_deeply(
$details
->inserted_ids,
{
map
{
$_
=>
$_
} 0 .. 1999 },
"inserted_ids correct"
);
is(
$details
->count_write_errors, 1,
"count_write_errors"
);
is(
$errdoc
->{code}, 11000,
"error code"
);
is(
$errdoc
->{
index
}, 2000,
"error index"
);
ok(
length
(
$errdoc
->{errmsg} ),
"error message"
);
is(
$coll
->count_documents({}), 2000,
"collection count"
);
};
subtest
"unordered batch split on number of ops"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_unordered_bulk_op;
$bulk
->insert_one( {
_id
=>
$_
} )
for
0 .. 1999;
$bulk
->insert_one( {
_id
=> 0 } );
$bulk
->insert_one( {
_id
=> 10000 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
'caught error'
)
or diag
$err
;
my
$details
=
$err
->result;
my
$errdoc
=
$details
->write_errors->[0];
is(
$details
->inserted_count, 2001,
"inserted_count"
);
is(
$details
->count_write_errors, 1,
"count_write_errors"
);
is(
$errdoc
->{code}, 11000,
"error code"
);
is(
$errdoc
->{
index
}, 2000,
"error index"
);
ok(
length
(
$errdoc
->{errmsg} ),
"error message"
);
is(
$coll
->count_documents({}), 2001,
"collection count"
);
};
note(
"QA-477 RE-RUNNING A BATCH"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: rerun a bulk operation"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {} );
my
$err
= exception {
$bulk
->execute };
is(
$err
,
undef
,
"first execute succeeds"
);
$err
= exception {
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::Error'
,
"re-running a bulk op throws exception"
);
like(
$err
->message,
qr/bulk op execute called more than once/
,
"error message"
)
or diag _truncate explain
$err
;
};
}
note(
"QA-477 EMPTY BATCH"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: empty bulk operation"
=>
sub
{
my
$bulk
=
$coll
->
$method
;
my
$err
= exception {
$bulk
->execute };
isa_ok(
$err
,
'MongoDB::Error'
,
"empty bulk op throws exception"
);
like(
$err
->message,
qr/no bulk ops to execute/
,
"error message"
)
or diag _truncate explain
$err
;
};
}
note(
"QA-477 W>1 AGAINST STANDALONE"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: w > 1 against standalone (explicit)"
=>
sub
{
plan
skip_all
=>
'needs a standalone server'
unless
$is_standalone
;
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {} );
my
$err
= exception {
$bulk
->execute( {
w
=> 2 } ) };
isa_ok(
$err
,
'MongoDB::DatabaseError'
,
"executing write concern w > 1 throws error"
);
like(
$err
->message,
qr/replica/
,
"error message mentions replication"
);
};
subtest
"$method: w > 1 against standalone (implicit)"
=>
sub
{
plan
skip_all
=>
'needs a standalone server'
unless
$is_standalone
;
$coll
->drop;
my
$coll2
=
$coll
->clone(
write_concern
=> {
w
=> 2 } );
my
$bulk
=
$coll2
->
$method
;
$bulk
->insert_one( {} );
my
$err
= exception {
$bulk
->execute() };
isa_ok(
$err
,
'MongoDB::DatabaseError'
,
"executing write concern w > 1 throws error"
);
like(
$err
->message,
qr/replica/
,
"error message mentions replication"
);
};
}
note(
"QA-477 WTIMEOUT PLUS DUPLICATE KEY ERROR"
);
subtest
"initialize_unordered_bulk_op: wtimeout plus duplicate keys"
=>
sub
{
plan
skip_all
=>
'needs a replica set'
unless
_number_of_servers > 1;
my
$W
= _number_of_servers() + 1;
$coll
->drop;
my
$bulk
=
$coll
->initialize_unordered_bulk_op;
$bulk
->insert_one( {
_id
=> 1 } );
$bulk
->insert_one( {
_id
=> 1 } );
my
$err
= exception {
$bulk
->execute( {
w
=>
$W
,
wtimeout
=> 100 } ) };
isa_ok(
$err
,
'MongoDB::DuplicateKeyError'
,
"executing throws error"
);
my
$details
=
$err
->result;
is(
$details
->inserted_count, 1,
"inserted_count == 1"
);
is(
$details
->count_write_errors, 1,
"one write error"
);
is(
$details
->count_write_concern_errors, 1,
"one write concern error"
);
};
note(
"QA-477 W = 0"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: w = 0"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
_id
=> 1 } );
$bulk
->insert_one( {
_id
=> 1 } );
$bulk
->insert_one( {
_id
=> 2 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute( {
w
=> 0 } ) };
is(
$err
,
undef
,
"execute with w = 0 doesn't throw error"
)
or diag _truncate explain
$err
;
my
$expect
=
$method
eq
'initialize_ordered_bulk_op'
? 1 : 2;
is(
$coll
->count_documents({}),
$expect
,
"document count ($expect)"
);
};
}
note(
"WRITE CONCERN ERRORS"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: write concern errors"
=>
sub
{
plan
skip_all
=>
'needs a replica set'
unless
_number_of_servers > 1;
my
$W
= _number_of_servers() + 1;
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
_id
=> 1 } );
$bulk
->insert_one( {
_id
=> 2 } );
$bulk
->find( {
id
=> 3 } )->upsert->update_many( {
'$set'
=> {
x
=> 2 } } );
$bulk
->insert_one( {
_id
=> 4 } );
my
$err
= exception {
$bulk
->execute( {
w
=>
$W
,
wtimeout
=> 100 } ) };
isa_ok(
$err
,
'MongoDB::WriteConcernError'
,
"executing throws error"
);
my
$details
=
$err
->result;
is(
$details
->inserted_count, 3,
"inserted_count"
);
is(
$details
->upserted_count, 1,
"upserted_count"
);
is(
$details
->count_write_errors, 0,
"no write errors"
);
ok(
$details
->count_write_concern_errors,
"got write concern errors"
);
};
}
note(
"ARRAY REFS"
);
subtest
"insert (ARRAY)"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
is(
$coll
->count_documents({}), 0,
"no docs in collection"
);
$bulk
->insert_one( [
_id
=> 1 ] );
$bulk
->insert_one( [] );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on insert"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents({}), 2,
"doc count"
);
};
subtest
"update (ARRAY)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
_id
=> 1 } );
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( [] )->update_many( [
'$set'
=> {
x
=> 2 } ] );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
is(
$coll
->find_one( {} )->{x}, 2,
"document updated"
);
};
subtest
"update_one (ARRAY)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
_id
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( [] )->update_one( [
'$set'
=> {
x
=> 2 } ] );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update_one"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents( {
x
=> 2 } ), 1,
"only one doc updated"
);
};
subtest
"replace_one (ARRAY)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( [] )->replace_one( [
key
=> 3 ] );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on replace"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents( {
key
=> 3 } ), 1,
"only one doc replaced"
);
};
note(
"Tie::IxHash"
);
subtest
"insert (Tie::IxHash)"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
is(
$coll
->count_documents({}), 0,
"no docs in collection"
);
$bulk
->insert_one( Tie::IxHash->new(
_id
=> 1 ) );
my
$doc
= Tie::IxHash->new();
$bulk
->insert_one(
$doc
);
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on insert"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents({}), 2,
"doc count"
);
};
subtest
"update (Tie::IxHash)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
_id
=> 1 } );
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( Tie::IxHash->new() )
->update_many( Tie::IxHash->new(
'$set'
=> {
x
=> 2 } ) );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
is(
$coll
->find_one( {} )->{x}, 2,
"document updated"
);
};
subtest
"update_one (Tie::IxHash)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
_id
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( Tie::IxHash->new() )
->update_one( Tie::IxHash->new(
'$set'
=> {
x
=> 2 } ) );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on update"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents( {
x
=> 2 } ), 1,
"only one doc updated"
);
};
subtest
"replace_one (Tie::IxHash)"
=>
sub
{
$coll
->drop;
$coll
->insert_one( {
key
=>
$_
} )
for
1 .. 2;
my
$bulk
=
$coll
->initialize_ordered_bulk_op;
$bulk
->find( Tie::IxHash->new() )->replace_one( Tie::IxHash->new(
key
=> 3 ) );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"no error on replace"
) or diag _truncate explain
$err
;
is(
$coll
->count_documents( {
key
=> 3 } ), 1,
"only one doc replaced"
);
};
note(
"W = 0 IGNORES ERRORS"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: w = 0"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->insert_one( {
_id
=> 1 } );
$bulk
->insert_one( {
_id
=> 3,
'$bad'
=> 1 } );
$bulk
->insert_one( {
_id
=> 4 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute( {
w
=> 0 } ) };
is(
$err
,
undef
,
"execute with w = 0 doesn't throw error"
)
or diag _truncate explain
$err
;
my
$expect
=
$method
eq
'initialize_ordered_bulk_op'
? 1 : 2;
is(
$coll
->count_documents({}),
$expect
,
"document count ($expect)"
);
};
}
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"collation w/ w:0 bulk writes return error, using $method"
=>
sub
{
plan
skip_all
=>
"Collation tests for MongoDB 3.4+"
unless
$supports_collation
;
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
x
=>
"foo"
} )->collation(
$case_insensitive_collation
)->delete_one;
like(
exception {
$bulk
->execute( {
w
=> 0 } ) },
qr/Unacknowledged bulk writes that specify a collation are not allowed/
,
"bulk write w/ collation returns error if write is unacknowledged"
);
};
}
note(
"UPSERT _ID NOT RETURNED"
);
for
my
$method
(
qw/initialize_ordered_bulk_op initialize_unordered_bulk_op/
) {
subtest
"$method: upsert with non OID _ids"
=>
sub
{
$coll
->drop;
my
$bulk
=
$coll
->
$method
;
$bulk
->find( {
_id
=> 0 } )->upsert->update_one( {
'$set'
=> {
a
=> 0 } } );
$bulk
->find( {
a
=> 1 } )->upsert->replace_one( {
_id
=> 1 } );
$bulk
->find( {
_id
=>
$server_does_bulk
? 2 : 3 } )->upsert->replace_one( {
_id
=> 2 } );
my
(
$result
,
$err
);
$err
= exception {
$result
=
$bulk
->execute };
is(
$err
,
undef
,
"execute doesn't throw error"
)
or diag _truncate explain
$err
;
cmp_deeply(
$result
,
_bulk_write_result(
upserted_count
=> 3,
modified_count
=> (
$server_does_bulk
? 0 :
undef
),
upserted
=>
[ {
index
=> num(0),
_id
=> num(0) }, {
index
=> num(1),
_id
=> num(1) }, {
index
=> num(2),
_id
=> num(2) }, ],
op_count
=> 3,
batch_count
=>
$server_does_bulk
? 1 : 3,
),
"result object correct"
) or diag _truncate explain
$result
;
};
}
subtest
"replace with custom op_char"
=>
sub
{
$coll
->drop;
my
$coll2
=
$coll
->with_codec(
op_char
=>
'-'
);
my
$bulk
=
$coll2
->ordered_bulk;
$bulk
->insert_one( {
_id
=> 0 } );
$bulk
->find( {
_id
=> 0 } )->replace_one( {
'-set'
=> {
key
=> 1} } );
like(
exception {
$bulk
->execute },
qr/replacement document must not contain update operators/
,
"single non-op key in update doc throws exception"
);
};
done_testing;