%PDF- %PDF-
Direktori : /home/opti/optisoftcom-new/perl5/lib/perl5/MongoDB/ |
Current File : /home/opti/optisoftcom-new/perl5/lib/perl5/MongoDB/BulkWriteResult.pm |
# Copyright 2014 - present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; package MongoDB::BulkWriteResult; # ABSTRACT: MongoDB bulk write result document use version; our $VERSION = 'v2.0.2'; # empty superclass for backcompatibility; add a variable to the # package namespace so Perl thinks it's a real package $MongoDB::WriteResult::VERSION = $VERSION; use Moo; use MongoDB::Error; use MongoDB::_Constants; use MongoDB::_Types qw( ArrayOfHashRef Numish ); use Types::Standard qw( HashRef Undef ); use namespace::clean; # fake empty superclass for backcompat our @ISA; push @ISA, 'MongoDB::WriteResult'; with $_ for qw( MongoDB::Role::_PrivateConstructor MongoDB::Role::_WriteResult ); has [qw/upserted inserted/] => ( is => 'ro', required => 1, isa => ArrayOfHashRef, ); has inserted_ids => ( is => 'lazy', builder => '_build_inserted_ids', init_arg => undef, isa => HashRef, ); sub _build_inserted_ids { my ($self) = @_; return { map { $_->{index}, $_->{_id} } @{ $self->inserted } }; } has upserted_ids => ( is => 'lazy', builder => '_build_upserted_ids', init_arg => undef, isa => HashRef, ); sub _build_upserted_ids { my ($self) = @_; return { map { $_->{index}, $_->{_id} } @{ $self->upserted } }; } for my $attr (qw/inserted_count upserted_count matched_count deleted_count/) { has $attr => ( is => 'ro', writer => "_set_$attr", required => 1, isa => Numish, ); } # This should always be initialized either as a number or as undef so that # merges accumulate correctly. It should be undef if talking to a server < 2.6 # or if talking to a mongos and not getting the field back from an update. The # default is undef, which will be sticky and ensure this field stays undef. has modified_count => ( is => 'ro', writer => '_set_modified_count', required => 1, isa => (Numish|Undef), ); sub has_modified_count { my ($self) = @_; return defined( $self->modified_count ); } has op_count => ( is => 'ro', writer => '_set_op_count', required => 1, isa => Numish, ); has batch_count => ( is => 'ro', writer => '_set_batch_count', required => 1, isa => Numish, ); #--------------------------------------------------------------------------# # emulate old API #--------------------------------------------------------------------------# my %OLD_API_ALIASING = ( nInserted => 'inserted_count', nUpserted => 'upserted_count', nMatched => 'matched_count', nModified => 'modified_count', nRemoved => 'deleted_count', writeErrors => 'write_errors', writeConcernErrors => 'write_concern_errors', count_writeErrors => 'count_write_errors', count_writeConcernErrors => 'count_write_concern_errors', ); while ( my ( $old, $new ) = each %OLD_API_ALIASING ) { no strict 'refs'; *{$old} = \&{$new}; } #--------------------------------------------------------------------------# # private functions #--------------------------------------------------------------------------# # defines how an logical operation type gets mapped to a result # field from the actual command result my %op_map = ( insert => [ inserted_count => sub { $_[0]->{n} } ], delete => [ deleted_count => sub { $_[0]->{n} } ], update => [ matched_count => sub { $_[0]->{n} } ], upsert => [ matched_count => sub { $_[0]->{n} - @{ $_[0]->{upserted} || [] } } ], ); my @op_map_keys = sort keys %op_map; sub _parse_cmd_result { my $class = shift; my $args = ref $_[0] eq 'HASH' ? shift : {@_}; unless ( 2 == grep { exists $args->{$_} } qw/op result/ ) { MongoDB::UsageError->throw("parse requires 'op' and 'result' arguments"); } my ( $op, $op_count, $batch_count, $result, $cmd_doc ) = @{$args}{qw/op op_count batch_count result cmd_doc/}; $result = $result->output if eval { $result->isa("MongoDB::CommandResult") }; MongoDB::UsageError->throw("op argument to parse must be one of: @op_map_keys") unless grep { $op eq $_ } @op_map_keys; MongoDB::UsageError->throw("results argument to parse must be a hash reference") unless ref $result eq 'HASH'; my %attrs = ( batch_count => $batch_count || 1, $op_count ? ( op_count => $op_count ) : (), inserted_count => 0, upserted_count => 0, matched_count => 0, deleted_count => 0, upserted => [], inserted => [], ); $attrs{write_errors} = $result->{writeErrors} ? $result->{writeErrors} : []; # rename writeConcernError -> write_concern_errors; coerce it to arrayref $attrs{write_concern_errors} = $result->{writeConcernError} ? [ $result->{writeConcernError} ] : []; # if we have upserts, change type to calculate differently if ( $result->{upserted} ) { $op = 'upsert'; $attrs{upserted} = $result->{upserted}; $attrs{upserted_count} = @{ $result->{upserted} }; } # recover _ids from documents if ( exists($result->{n}) && $op eq 'insert' ) { my @pairs; my $docs = {@$cmd_doc}->{documents}; for my $i ( 0 .. $result->{n}-1 ) { push @pairs, { index => $i, _id => $docs->[$i]{metadata}{_id} }; } $attrs{inserted} = \@pairs; } # change 'n' into an op-specific count if ( exists $result->{n} ) { my ( $key, $builder ) = @{ $op_map{$op} }; $attrs{$key} = $builder->($result); } # for an update/upsert we want the exact response whether numeric or undef # so that new undef responses become sticky; for all other updates, we # consider it 0 and let it get sorted out in the merging $attrs{modified_count} = ( $op eq 'update' || $op eq 'upsert' ) ? $result->{nModified} : 0; return $class->_new(%attrs); } # these are for single results only sub _parse_write_op { my $class = shift; my $op = shift; my %attrs = ( batch_count => 1, op_count => 1, write_errors => $op->write_errors, write_concern_errors => $op->write_concern_errors, inserted_count => 0, upserted_count => 0, matched_count => 0, modified_count => undef, deleted_count => 0, upserted => [], inserted => [], ); my $has_write_error = @{ $attrs{write_errors} }; # parse by type my $type = ref($op); if ( $type eq 'MongoDB::InsertOneResult' ) { if ( $has_write_error ) { $attrs{inserted_count} = 0; $attrs{inserted} = []; } else { $attrs{inserted_count} = 1; $attrs{inserted} = [ { index => 0, _id => $op->inserted_id } ]; } } elsif ( $type eq 'MongoDB::DeleteResult' ) { $attrs{deleted_count} = $op->deleted_count; } elsif ( $type eq 'MongoDB::UpdateResult' ) { if ( defined $op->upserted_id ) { my $upsert = { index => 0, _id => $op->upserted_id }; $attrs{upserted} = [$upsert]; $attrs{upserted_count} = 1; # modified_count *must* always be defined for 2.6+ servers # matched_count is here for clarity and consistency $attrs{matched_count} = 0; $attrs{modified_count} = 0; } else { $attrs{matched_count} = $op->matched_count; $attrs{modified_count} = $op->modified_count; } } else { MongoDB::InternalError->throw("can't parse unknown result class $op"); } return $class->_new(%attrs); } sub _merge_result { my ( $self, $result ) = @_; # Add simple counters for my $attr (qw/inserted_count upserted_count matched_count deleted_count/) { my $setter = "_set_$attr"; $self->$setter( $self->$attr + $result->$attr ); } # If modified_count is defined in both results we're merging, then we're # talking to a 2.6+ mongod or we're talking to a 2.6+ mongos and have only # seen responses with modified_count. In any other case, we set # modified_count to undef, which then becomes "sticky" if ( defined $self->modified_count && defined $result->modified_count ) { $self->_set_modified_count( $self->modified_count + $result->modified_count ); } else { $self->_set_modified_count(undef); } # Append error and upsert docs, but modify index based on op count my $op_count = $self->op_count; for my $attr (qw/write_errors upserted inserted/) { for my $doc ( @{ $result->$attr } ) { $doc->{index} += $op_count; } push @{ $self->$attr }, @{ $result->$attr }; } # Append write concern errors without modification (they have no index) push @{ $self->write_concern_errors }, @{ $result->write_concern_errors }; $self->_set_op_count( $op_count + $result->op_count ); $self->_set_batch_count( $self->batch_count + $result->batch_count ); return 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME MongoDB::BulkWriteResult - MongoDB bulk write result document =head1 VERSION version v2.0.2 =head1 SYNOPSIS # returned directly my $result = $bulk->execute; # from a WriteError or WriteConcernError my $result = $error->result; if ( $result->acknowledged ) { ... } =head1 DESCRIPTION This class encapsulates the results from a bulk write operation. It may be returned directly from C<execute> or it may be in the C<result> attribute of a C<MongoDB::DatabaseError> subclass like C<MongoDB::WriteError> or C<MongoDB::WriteConcernError>. =head1 ATTRIBUTES =head2 inserted_count Number of documents inserted =head2 upserted_count Number of documents upserted =head2 matched_count Number of documents matched for an update or replace operation. =head2 deleted_count Number of documents removed =head2 modified_count Number of documents actually modified by an update operation. This is not necessarily the same as L</matched_count> if the document was not actually modified as a result of the update. This field is not available from legacy servers before version 2.6. If results are seen from a legacy server (or from a mongos proxying for a legacy server) this attribute will be C<undef>. You can call C<has_modified_count> to find out if this attribute is defined or not. =head2 upserted An array reference containing information about upserted documents (if any). Each document will have the following fields: =over 4 =item * index — 0-based index indicating which operation failed =item * _id — the object ID of the upserted document =back =head2 upserted_ids A hash reference built lazily from C<upserted> mapping indexes to object IDs. =head2 inserted An array reference containing information about inserted documents (if any). Documents are just as in C<upserted>. =head2 inserted_ids A hash reference built lazily from C<inserted> mapping indexes to object IDs. =head2 write_errors An array reference containing write errors (if any). Each error document will have the following fields: =over 4 =item * index — 0-based index indicating which operation failed =item * code — numeric error code =item * errmsg — textual error string =item * op — a representation of the actual operation sent to the server =back =head2 write_concern_errors An array reference containing write concern errors (if any). Each error document will have the following fields: =over 4 =item * index — 0-based index indicating which operation failed =item * code — numeric error code =back =head2 op_count The number of operations sent to the database. =head2 batch_count The number of database commands issued to the server. This will be less than the C<op_count> if multiple operations were grouped together. =head1 METHODS =head2 assert Throws an error if write errors or write concern errors occurred. =head2 assert_no_write_error Throws a MongoDB::WriteError if C<count_write_errors> is non-zero; otherwise returns 1. =head2 assert_no_write_concern_error Throws a MongoDB::WriteConcernError if C<count_write_concern_errors> is non-zero; otherwise returns 1. =head2 count_write_errors Returns the number of write errors =head2 count_write_concern_errors Returns the number of write errors =head2 last_code Returns the last C<code> field from either the list of C<write_errors> or C<write_concern_errors> or 0 if there are no errors. =head2 last_errmsg Returns the last C<errmsg> field from either the list of C<write_errors> or C<write_concern_errors> or the empty string if there are no errors. =head2 last_wtimeout True if a write concern timed out or false otherwise. =head1 AUTHORS =over 4 =item * David Golden <david@mongodb.com> =item * Rassi <rassi@mongodb.com> =item * Mike Friedman <friedo@friedo.com> =item * Kristina Chodorow <k.chodorow@gmail.com> =item * Florian Ragwitz <rafl@debian.org> =back =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2018 by MongoDB, Inc. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut