line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright 2014 - present MongoDB, Inc. |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
4
|
|
|
|
|
|
|
# you may not use this file except in compliance with the License. |
5
|
|
|
|
|
|
|
# You may obtain a copy of the License at |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software |
10
|
|
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
11
|
|
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12
|
|
|
|
|
|
|
# See the License for the specific language governing permissions and |
13
|
|
|
|
|
|
|
# limitations under the License. |
14
|
|
|
|
|
|
|
|
15
|
58
|
|
|
58
|
|
388
|
use strict; |
|
58
|
|
|
|
|
129
|
|
|
58
|
|
|
|
|
1647
|
|
16
|
58
|
|
|
58
|
|
297
|
use warnings; |
|
58
|
|
|
|
|
128
|
|
|
58
|
|
|
|
|
5157
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package MongoDB::Op::_Query; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Encapsulate a query operation; returns a MongoDB::QueryResult object |
21
|
|
|
|
|
|
|
|
22
|
58
|
|
|
58
|
|
398
|
use version; |
|
58
|
|
|
|
|
129
|
|
|
58
|
|
|
|
|
367
|
|
23
|
|
|
|
|
|
|
our $VERSION = 'v2.2.0'; |
24
|
|
|
|
|
|
|
|
25
|
58
|
|
|
58
|
|
4677
|
use boolean; |
|
58
|
|
|
|
|
166
|
|
|
58
|
|
|
|
|
488
|
|
26
|
58
|
|
|
58
|
|
4269
|
use Moo; |
|
58
|
|
|
|
|
205
|
|
|
58
|
|
|
|
|
396
|
|
27
|
|
|
|
|
|
|
|
28
|
58
|
|
|
58
|
|
19312
|
use Scalar::Util qw/blessed/; |
|
58
|
|
|
|
|
154
|
|
|
58
|
|
|
|
|
3617
|
|
29
|
58
|
|
|
58
|
|
390
|
use List::Util qw/min/; |
|
58
|
|
|
|
|
162
|
|
|
58
|
|
|
|
|
3939
|
|
30
|
58
|
|
|
58
|
|
412
|
use MongoDB::QueryResult; |
|
58
|
|
|
|
|
139
|
|
|
58
|
|
|
|
|
1687
|
|
31
|
58
|
|
|
58
|
|
23256
|
use MongoDB::QueryResult::Filtered; |
|
58
|
|
|
|
|
174
|
|
|
58
|
|
|
|
|
1935
|
|
32
|
58
|
|
|
58
|
|
430
|
use MongoDB::_Constants; |
|
58
|
|
|
|
|
134
|
|
|
58
|
|
|
|
|
7006
|
|
33
|
58
|
|
|
58
|
|
384
|
use MongoDB::_Protocol; |
|
58
|
|
|
|
|
123
|
|
|
58
|
|
|
|
|
1730
|
|
34
|
58
|
|
|
|
|
449
|
use MongoDB::_Types qw( |
35
|
|
|
|
|
|
|
Document |
36
|
|
|
|
|
|
|
CursorType |
37
|
|
|
|
|
|
|
IxHash |
38
|
|
|
|
|
|
|
to_IxHash |
39
|
58
|
|
|
58
|
|
325
|
); |
|
58
|
|
|
|
|
122
|
|
40
|
58
|
|
|
|
|
291
|
use Types::Standard qw( |
41
|
|
|
|
|
|
|
CodeRef |
42
|
|
|
|
|
|
|
HashRef |
43
|
|
|
|
|
|
|
InstanceOf |
44
|
|
|
|
|
|
|
Maybe |
45
|
|
|
|
|
|
|
Num |
46
|
|
|
|
|
|
|
Str |
47
|
58
|
|
|
58
|
|
82675
|
); |
|
58
|
|
|
|
|
137
|
|
48
|
|
|
|
|
|
|
|
49
|
58
|
|
|
58
|
|
71453
|
use namespace::clean; |
|
58
|
|
|
|
|
160
|
|
|
58
|
|
|
|
|
290
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
has client => ( |
52
|
|
|
|
|
|
|
is => 'ro', |
53
|
|
|
|
|
|
|
required => 1, |
54
|
|
|
|
|
|
|
isa => InstanceOf ['MongoDB::MongoClient'], |
55
|
|
|
|
|
|
|
); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
58
|
|
|
|
|
|
|
# Attributes based on the CRUD API spec: filter and options |
59
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
has filter => ( |
62
|
|
|
|
|
|
|
is => 'ro', |
63
|
|
|
|
|
|
|
isa => Document, |
64
|
|
|
|
|
|
|
required => 1, |
65
|
|
|
|
|
|
|
); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# XXX The provided 'options' field *MUST* be the output of the class method |
68
|
|
|
|
|
|
|
# 'precondition_options'. Normally, we'd do this in a BUILD method, but in |
69
|
|
|
|
|
|
|
# order to allow the use of the private constructor for speed, we push |
70
|
|
|
|
|
|
|
# responsibility for conditioning the options to the calling site. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
has options => ( |
73
|
|
|
|
|
|
|
is => 'ro', |
74
|
|
|
|
|
|
|
isa => HashRef, |
75
|
|
|
|
|
|
|
required => 1, |
76
|
|
|
|
|
|
|
); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Not a MongoDB query attribute; this is used during construction of a |
79
|
|
|
|
|
|
|
# result object |
80
|
|
|
|
|
|
|
has post_filter => ( |
81
|
|
|
|
|
|
|
is => 'ro', |
82
|
|
|
|
|
|
|
predicate => 'has_post_filter', |
83
|
|
|
|
|
|
|
isa => Maybe [CodeRef], |
84
|
|
|
|
|
|
|
); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
with $_ for qw( |
87
|
|
|
|
|
|
|
MongoDB::Role::_PrivateConstructor |
88
|
|
|
|
|
|
|
MongoDB::Role::_CollectionOp |
89
|
|
|
|
|
|
|
MongoDB::Role::_ReadOp |
90
|
|
|
|
|
|
|
MongoDB::Role::_CommandCursorOp |
91
|
|
|
|
|
|
|
MongoDB::Role::_OpReplyParser |
92
|
|
|
|
|
|
|
MongoDB::Role::_ReadPrefModifier |
93
|
|
|
|
|
|
|
); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub execute { |
96
|
0
|
|
|
0
|
0
|
|
my ( $self, $link, $topology ) = @_; |
97
|
|
|
|
|
|
|
|
98
|
0
|
0
|
0
|
|
|
|
if ( defined $self->{options}{collation} and !$link->supports_collation ) { |
99
|
0
|
|
|
|
|
|
MongoDB::UsageError->throw( |
100
|
|
|
|
|
|
|
"MongoDB host '" . $link->address . "' doesn't support collation" ); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
|
|
|
|
my $res = |
104
|
|
|
|
|
|
|
$link->supports_query_commands |
105
|
|
|
|
|
|
|
? $self->_command_query( $link, $topology ) |
106
|
|
|
|
|
|
|
: $self->_legacy_query( $link, $topology ); |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
|
|
|
return $res; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _command_query { |
112
|
0
|
|
|
0
|
|
|
my ( $self, $link, $topology ) = @_; |
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
|
|
|
my $op = MongoDB::Op::_Command->_new( |
115
|
|
|
|
|
|
|
db_name => $self->db_name, |
116
|
|
|
|
|
|
|
query => $self->_as_command, |
117
|
|
|
|
|
|
|
query_flags => {}, |
118
|
|
|
|
|
|
|
read_preference => $self->read_preference, |
119
|
|
|
|
|
|
|
bson_codec => $self->bson_codec, |
120
|
|
|
|
|
|
|
session => $self->session, |
121
|
|
|
|
|
|
|
monitoring_callback => $self->monitoring_callback, |
122
|
|
|
|
|
|
|
); |
123
|
0
|
|
|
|
|
|
my $res = $op->execute( $link, $topology ); |
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
|
return $self->_build_result_from_cursor($res); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub _legacy_query { |
129
|
0
|
|
|
0
|
|
|
my ( $self, $link, $topology ) = @_; |
130
|
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
|
my $opts = $self->{options}; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
my $query_flags = { |
134
|
|
|
|
|
|
|
tailable => ( $opts->{cursorType} =~ /^tailable/ ? 1 : 0 ), |
135
|
|
|
|
|
|
|
await_data => $opts->{cursorType} eq 'tailable_await', |
136
|
|
|
|
|
|
|
immortal => $opts->{noCursorTimeout}, |
137
|
|
|
|
|
|
|
partial => $opts->{allowPartialResults}, |
138
|
0
|
0
|
|
|
|
|
}; |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
my $query = $self->_as_query_document($opts); |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
|
my $full_name = $self->full_name; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# rules for calculating initial batch size |
145
|
0
|
|
0
|
|
|
|
my $limit = $opts->{limit} // 0; |
146
|
0
|
|
0
|
|
|
|
my $batch_size = $opts->{batchSize} // 0; |
147
|
0
|
0
|
|
|
|
|
my $n_to_return = |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
148
|
|
|
|
|
|
|
$limit == 0 ? $batch_size |
149
|
|
|
|
|
|
|
: $batch_size == 0 ? $limit |
150
|
|
|
|
|
|
|
: $limit < 0 ? $limit |
151
|
|
|
|
|
|
|
: min( $limit, $batch_size ); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
my $proj = |
154
|
0
|
0
|
|
|
|
|
$opts->{projection} ? $self->bson_codec->encode_one( $opts->{projection} ) : undef; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# $query is passed as a reference because it *may* be replaced |
157
|
0
|
|
|
|
|
|
$self->_apply_op_query_read_prefs( $link, $topology, $query_flags, \$query ); |
158
|
|
|
|
|
|
|
|
159
|
0
|
|
|
|
|
|
my $filter = $self->bson_codec->encode_one($query); |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
my ( $op_bson, $request_id ) = |
162
|
|
|
|
|
|
|
MongoDB::_Protocol::write_query( $full_name, $filter, $proj, $opts->{skip}, |
163
|
0
|
|
|
|
|
|
$n_to_return, $query_flags ); |
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
my $result = |
166
|
|
|
|
|
|
|
$self->_query_and_receive( $link, $op_bson, $request_id, $self->bson_codec ); |
167
|
|
|
|
|
|
|
|
168
|
0
|
0
|
|
|
|
|
my $class = |
169
|
|
|
|
|
|
|
$self->has_post_filter ? "MongoDB::QueryResult::Filtered" : "MongoDB::QueryResult"; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
return $class->_new( |
172
|
|
|
|
|
|
|
_client => $self->client, |
173
|
|
|
|
|
|
|
_address => $link->address, |
174
|
|
|
|
|
|
|
_full_name => $full_name, |
175
|
|
|
|
|
|
|
_bson_codec => $self->bson_codec, |
176
|
|
|
|
|
|
|
_batch_size => $n_to_return, |
177
|
|
|
|
|
|
|
_cursor_at => 0, |
178
|
|
|
|
|
|
|
_limit => $limit, |
179
|
|
|
|
|
|
|
_cursor_id => $result->{cursor_id}, |
180
|
|
|
|
|
|
|
_cursor_start => $result->{starting_from}, |
181
|
|
|
|
|
|
|
_cursor_flags => $result->{flags} || {}, |
182
|
|
|
|
|
|
|
_cursor_num => $result->{number_returned}, |
183
|
|
|
|
|
|
|
_docs => $result->{docs}, |
184
|
0
|
|
0
|
|
|
|
_post_filter => $self->post_filter, |
185
|
|
|
|
|
|
|
); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# awful hack: avoid calling into boolean to get true/false |
189
|
|
|
|
|
|
|
my $TRUE = boolean::true(); |
190
|
|
|
|
|
|
|
my $FALSE = boolean::false(); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _as_query_document { |
193
|
0
|
|
|
0
|
|
|
my ($self, $opts) = @_; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Reconstruct query modifiers style from options. However, we only |
196
|
|
|
|
|
|
|
# apply $maxTimeMS if we're not running a command via OP_QUERY against |
197
|
|
|
|
|
|
|
# the '$cmd' collection. For commands, we expect maxTimeMS to be in |
198
|
|
|
|
|
|
|
# the command itself. |
199
|
|
|
|
|
|
|
my $query = { |
200
|
|
|
|
|
|
|
( defined $opts->{comment} ? ( '$comment' => $opts->{comment} ) : () ), |
201
|
|
|
|
|
|
|
( defined $opts->{hint} ? ( '$hint' => $opts->{hint} ) : () ), |
202
|
|
|
|
|
|
|
( defined $opts->{max} ? ( '$max' => $opts->{max} ) : () ), |
203
|
|
|
|
|
|
|
( defined $opts->{min} ? ( '$min' => $opts->{min} ) : () ), |
204
|
|
|
|
|
|
|
( defined $opts->{sort} ? ( '$orderby' => $opts->{sort} ) : () ), |
205
|
|
|
|
|
|
|
( defined $opts->{maxScan} ? ( '$maxScan' => $opts->{maxScan} ) : () ), |
206
|
|
|
|
|
|
|
( defined $opts->{returnKey} ? ( '$returnKey' => $opts->{returnKey} ) : () ), |
207
|
|
|
|
|
|
|
( defined $opts->{showRecordId} ? ( '$showDiskLoc' => $opts->{showRecordId} ) : () ), |
208
|
|
|
|
|
|
|
( defined $opts->{snapshot} ? ( '$snapshot' => $opts->{snapshot} ) : () ), |
209
|
|
|
|
|
|
|
( |
210
|
|
|
|
|
|
|
( defined $opts->{maxTimeMS} && $self->coll_name !~ /\A\$cmd/ ) |
211
|
|
|
|
|
|
|
? ( '$maxTimeMS' => $opts->{maxTimeMS} ) |
212
|
|
|
|
|
|
|
: () |
213
|
|
|
|
|
|
|
), |
214
|
|
|
|
|
|
|
# Not a user-provided option: this is only set by MongoDB::Op::_Explain |
215
|
|
|
|
|
|
|
# for legacy $explain support |
216
|
0
|
0
|
0
|
|
|
|
( defined $opts->{explain} ? ( '$explain' => $TRUE ) : () ), |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
217
|
|
|
|
|
|
|
( '$query' => ( $self->filter || {} ) ), |
218
|
|
|
|
|
|
|
}; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# if no modifers were added and there is no 'query' key in '$query' |
221
|
|
|
|
|
|
|
# we remove the extra layer; this is necessary as some special |
222
|
|
|
|
|
|
|
# command queries will choke on '$query' |
223
|
|
|
|
|
|
|
# (see https://jira.mongodb.org/browse/SERVER-14294) |
224
|
|
|
|
|
|
|
$query = $query->{'$query'} |
225
|
|
|
|
|
|
|
if keys %$query == 1 && !( |
226
|
|
|
|
|
|
|
( ref( $query->{'$query'} ) eq 'Tie::IxHash' ) |
227
|
|
|
|
|
|
|
? $query->{'$query'}->EXISTS('query') |
228
|
|
|
|
|
|
|
: exists $query->{'$query'}{query} |
229
|
0
|
0
|
0
|
|
|
|
); |
|
|
0
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
0
|
|
|
|
|
|
return $query; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
my %options_to_prune = |
235
|
|
|
|
|
|
|
map { $_ => 1 } qw/limit batchSize cursorType maxAwaitTimeMS modifiers/; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub _as_command { |
238
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
239
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
|
my $opts = $self->{options}; |
241
|
|
|
|
|
|
|
|
242
|
0
|
|
0
|
|
|
|
my $limit = $opts->{limit} // 0; |
243
|
0
|
|
0
|
|
|
|
my $batch_size = $opts->{batchSize} // 0; |
244
|
0
|
|
0
|
|
|
|
my $single_batch = $limit < 0 || $batch_size < 0; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# find command always takes positive limit and batch size, so normalize |
247
|
|
|
|
|
|
|
# them based on rules in the "find, getmore, kill cursor" spec: |
248
|
|
|
|
|
|
|
# https://github.com/mongodb/specifications/blob/master/source/find_getmore_killcursors_commands.rst |
249
|
0
|
|
|
|
|
|
$limit = abs($limit); |
250
|
0
|
0
|
|
|
|
|
$batch_size = $limit if $single_batch; |
251
|
|
|
|
|
|
|
|
252
|
0
|
0
|
|
|
|
|
my $tailable = $opts->{cursorType} =~ /^tailable/ ? $TRUE : $FALSE; |
253
|
0
|
0
|
|
|
|
|
my $await_data = $opts->{cursorType} eq 'tailable_await' ? $TRUE : $FALSE; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
return [ |
256
|
|
|
|
|
|
|
# Always send these options |
257
|
|
|
|
|
|
|
find => $self->{coll_name}, |
258
|
|
|
|
|
|
|
filter => $self->{filter}, |
259
|
|
|
|
|
|
|
tailable => $tailable, |
260
|
|
|
|
|
|
|
awaitData => $await_data, |
261
|
|
|
|
|
|
|
singleBatch => ( $single_batch ? $TRUE : $FALSE ), |
262
|
0
|
|
|
|
|
|
@{ $self->{read_concern}->as_args( $self->session ) }, |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
( $limit ? ( limit => $limit ) : () ), |
265
|
|
|
|
|
|
|
( $batch_size ? ( batchSize => $batch_size ) : () ), |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# Merge in any server options, but cursorType and maxAwaitTimeMS aren't |
268
|
|
|
|
|
|
|
# actually a server option, so we remove it during the merge. Also |
269
|
|
|
|
|
|
|
# remove limit and batchSize as those may have been modified |
270
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
|
( map { $_ => $opts->{$_} } grep { !exists $options_to_prune{$_} } keys %$opts ) |
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
272
|
|
|
|
|
|
|
]; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# precondition_options is a class method that, given query options, |
276
|
|
|
|
|
|
|
# combines keys from the deprecated 'modifiers' option with the correct |
277
|
|
|
|
|
|
|
# precedence. It provides defaults and and coerces values if needed. |
278
|
|
|
|
|
|
|
# |
279
|
|
|
|
|
|
|
# It returns a hash reference with extracted and coerced options. |
280
|
|
|
|
|
|
|
sub precondition_options { |
281
|
0
|
|
|
0
|
0
|
|
my ( $class, $opts ) = @_; |
282
|
0
|
|
0
|
|
|
|
$opts //= {}; |
283
|
0
|
|
0
|
|
|
|
my $mods = $opts->{modifiers} // {}; |
284
|
|
|
|
|
|
|
my %merged = ( |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# |
287
|
|
|
|
|
|
|
# Keys always included in commands or used in calcuations need a |
288
|
|
|
|
|
|
|
# default value if not provided. |
289
|
|
|
|
|
|
|
# |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# integer |
292
|
|
|
|
|
|
|
( skip => $opts->{skip} // 0 ), |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# boolean |
295
|
|
|
|
|
|
|
( allowPartialResults => ( $opts->{allowPartialResults} ? $TRUE : $FALSE ) ), |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# boolean |
298
|
|
|
|
|
|
|
( noCursorTimeout => ( $opts->{noCursorTimeout} ? $TRUE : $FALSE ) ), |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# integer |
301
|
|
|
|
|
|
|
( batchSize => $opts->{batchSize} // 0 ), |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# integer |
304
|
|
|
|
|
|
|
( limit => $opts->{limit} // 0 ), |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# string |
307
|
|
|
|
|
|
|
( cursorType => $opts->{cursorType} // 'non_tailable' ), |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# |
310
|
|
|
|
|
|
|
# These are optional keys that should be included only if defined. |
311
|
|
|
|
|
|
|
# |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# integer |
314
|
|
|
|
|
|
|
( |
315
|
|
|
|
|
|
|
defined $opts->{maxAwaitTimeMS} ? ( maxAwaitTimeMS => $opts->{maxAwaitTimeMS} ) : () |
316
|
|
|
|
|
|
|
), |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# hashref |
319
|
|
|
|
|
|
|
( defined $opts->{projection} ? ( projection => $opts->{projection} ) : () ), |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# hashref |
322
|
|
|
|
|
|
|
( defined $opts->{collation} ? ( collation => $opts->{collation} ) : () ), |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# |
325
|
|
|
|
|
|
|
# These keys have equivalents in the 'modifiers' option: if an options |
326
|
|
|
|
|
|
|
# key exists it takes precedence over a modifiers key, but undefined |
327
|
|
|
|
|
|
|
# values disable the option in both cases. |
328
|
|
|
|
|
|
|
# |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# string |
331
|
|
|
|
|
|
|
( |
332
|
|
|
|
|
|
|
( exists $opts->{comment} ) |
333
|
|
|
|
|
|
|
? ( ( defined $opts->{comment} ) ? ( comment => $opts->{comment} ) : () ) |
334
|
|
|
|
|
|
|
: ( |
335
|
|
|
|
|
|
|
( defined $mods->{'$comment'} ) |
336
|
|
|
|
|
|
|
? ( comment => $mods->{'$comment'} ) |
337
|
|
|
|
|
|
|
: () |
338
|
|
|
|
|
|
|
) |
339
|
|
|
|
|
|
|
), |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# string or ordered document |
342
|
|
|
|
|
|
|
( |
343
|
|
|
|
|
|
|
( exists $opts->{hint} ) |
344
|
|
|
|
|
|
|
? ( ( defined $opts->{hint} ) ? ( hint => $opts->{hint} ) : () ) |
345
|
|
|
|
|
|
|
: ( |
346
|
|
|
|
|
|
|
( defined $mods->{'$hint'} ) |
347
|
|
|
|
|
|
|
? ( hint => $mods->{'$hint'} ) |
348
|
|
|
|
|
|
|
: () |
349
|
|
|
|
|
|
|
) |
350
|
|
|
|
|
|
|
), |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# ordered document |
353
|
|
|
|
|
|
|
( |
354
|
|
|
|
|
|
|
( exists $opts->{max} ) |
355
|
|
|
|
|
|
|
? ( ( defined $opts->{max} ) ? ( max => $opts->{max} ) : () ) |
356
|
|
|
|
|
|
|
: ( |
357
|
|
|
|
|
|
|
( defined $mods->{'$max'} ) |
358
|
|
|
|
|
|
|
? ( max => $mods->{'$max'} ) |
359
|
|
|
|
|
|
|
: () |
360
|
|
|
|
|
|
|
) |
361
|
|
|
|
|
|
|
), |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# ordered document |
364
|
|
|
|
|
|
|
( |
365
|
|
|
|
|
|
|
( exists $opts->{min} ) |
366
|
|
|
|
|
|
|
? ( ( defined $opts->{min} ) ? ( min => $opts->{min} ) : () ) |
367
|
|
|
|
|
|
|
: ( |
368
|
|
|
|
|
|
|
( defined $mods->{'$min'} ) |
369
|
|
|
|
|
|
|
? ( min => $mods->{'$min'} ) |
370
|
|
|
|
|
|
|
: () |
371
|
|
|
|
|
|
|
) |
372
|
|
|
|
|
|
|
), |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# integer |
375
|
|
|
|
|
|
|
( |
376
|
|
|
|
|
|
|
( exists $opts->{maxScan} ) |
377
|
|
|
|
|
|
|
? ( ( defined $opts->{maxScan} ) ? ( maxScan => $opts->{maxScan} ) : () ) |
378
|
|
|
|
|
|
|
: ( |
379
|
|
|
|
|
|
|
( defined $mods->{'$maxScan'} ) |
380
|
|
|
|
|
|
|
? ( maxScan => $mods->{'$maxScan'} ) |
381
|
|
|
|
|
|
|
: () |
382
|
|
|
|
|
|
|
) |
383
|
|
|
|
|
|
|
), |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# integer |
386
|
|
|
|
|
|
|
( |
387
|
|
|
|
|
|
|
( exists $opts->{maxTimeMS} ) |
388
|
|
|
|
|
|
|
? ( ( defined $opts->{maxTimeMS} ) ? ( maxTimeMS => $opts->{maxTimeMS} ) : () ) |
389
|
|
|
|
|
|
|
: ( |
390
|
|
|
|
|
|
|
( defined $mods->{'$maxTimeMS'} ) |
391
|
|
|
|
|
|
|
? ( maxTimeMS => $mods->{'$maxTimeMS'} ) |
392
|
|
|
|
|
|
|
: () |
393
|
|
|
|
|
|
|
) |
394
|
|
|
|
|
|
|
), |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# ordered document |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
( |
399
|
|
|
|
|
|
|
( exists $opts->{sort} ) |
400
|
|
|
|
|
|
|
? ( ( defined $opts->{sort} ) ? ( sort => $opts->{sort} ) : () ) |
401
|
|
|
|
|
|
|
: ( |
402
|
|
|
|
|
|
|
( defined $mods->{'$orderby'} ) |
403
|
|
|
|
|
|
|
? ( sort => $mods->{'$orderby'} ) |
404
|
|
|
|
|
|
|
: () |
405
|
|
|
|
|
|
|
) |
406
|
|
|
|
|
|
|
), |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# boolean |
409
|
|
|
|
|
|
|
( |
410
|
|
|
|
|
|
|
( exists $opts->{returnKey} ) |
411
|
|
|
|
|
|
|
? ( ( defined $opts->{returnKey} ) ? ( returnKey => $opts->{returnKey} ) : () ) |
412
|
|
|
|
|
|
|
: ( |
413
|
|
|
|
|
|
|
( defined $mods->{'$returnKey'} ) |
414
|
|
|
|
|
|
|
? ( returnKey => $mods->{'$returnKey'} ) |
415
|
|
|
|
|
|
|
: () |
416
|
|
|
|
|
|
|
) |
417
|
|
|
|
|
|
|
), |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# boolean |
420
|
|
|
|
|
|
|
( |
421
|
|
|
|
|
|
|
( exists $opts->{showRecordId} ) |
422
|
|
|
|
|
|
|
? ( |
423
|
|
|
|
|
|
|
( defined $opts->{showRecordId} ) ? ( showRecordId => $opts->{showRecordId} ) : () ) |
424
|
|
|
|
|
|
|
: ( |
425
|
|
|
|
|
|
|
( defined $mods->{'$showDiskLoc'} ) |
426
|
|
|
|
|
|
|
? ( showRecordId => $mods->{'$showDiskLoc'} ) |
427
|
|
|
|
|
|
|
: () |
428
|
|
|
|
|
|
|
) |
429
|
|
|
|
|
|
|
), |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
# boolean |
432
|
|
|
|
|
|
|
( |
433
|
|
|
|
|
|
|
( exists $opts->{snapshot} ) |
434
|
|
|
|
|
|
|
? ( ( defined $opts->{snapshot} ) ? ( snapshot => $opts->{snapshot} ) : () ) |
435
|
|
|
|
|
|
|
: ( |
436
|
|
|
|
|
|
|
( defined $mods->{'$snapshot'} ) |
437
|
0
|
0
|
0
|
|
|
|
? ( snapshot => $mods->{'$snapshot'} ) |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
438
|
|
|
|
|
|
|
: () |
439
|
|
|
|
|
|
|
) |
440
|
|
|
|
|
|
|
), |
441
|
|
|
|
|
|
|
); |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# coercions to IxHash: unrolled for efficiency |
444
|
0
|
0
|
|
|
|
|
$merged{sort} = to_IxHash( $merged{sort} ) if exists $merged{sort}; |
445
|
0
|
0
|
|
|
|
|
$merged{max} = to_IxHash( $merged{max} ) if exists $merged{max}; |
446
|
0
|
0
|
|
|
|
|
$merged{min} = to_IxHash( $merged{min} ) if exists $merged{min}; |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# optional coercion to IxHash if hint is a reference type |
449
|
0
|
0
|
|
|
|
|
$merged{hint} = to_IxHash( $merged{hint} ) if ref $merged{hint}; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# coercions to boolean (if not already coerced): unrolled for efficiency |
452
|
|
|
|
|
|
|
$merged{returnKey} = ( $merged{returnKey} ? $TRUE : $FALSE ) |
453
|
0
|
0
|
|
|
|
|
if exists $merged{returnKey}; |
|
|
0
|
|
|
|
|
|
454
|
|
|
|
|
|
|
$merged{showRecordId} = ( $merged{showRecordId} ? $TRUE : $FALSE ) |
455
|
0
|
0
|
|
|
|
|
if exists $merged{showRecordId}; |
|
|
0
|
|
|
|
|
|
456
|
|
|
|
|
|
|
$merged{snapshot} = ( $merged{snapshot} ? $TRUE : $FALSE ) |
457
|
0
|
0
|
|
|
|
|
if exists $merged{snapshot}; |
|
|
0
|
|
|
|
|
|
458
|
|
|
|
|
|
|
|
459
|
0
|
|
|
|
|
|
return \%merged; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# Setters are provided to support the MongoDB::Cursor interface that modifies |
463
|
|
|
|
|
|
|
# options prior to execution. These methods preserve the rules for each key |
464
|
|
|
|
|
|
|
# that are used in precondition_options. Specifically, if passed *undef*, |
465
|
|
|
|
|
|
|
# the options are cleared, except for options that must have a default. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
# setters for boolean options |
468
|
|
|
|
|
|
|
for my $key ( qw/returnKey showRecordId snapshot/ ) { |
469
|
58
|
|
|
58
|
|
135524
|
no strict 'refs'; |
|
58
|
|
|
|
|
171
|
|
|
58
|
|
|
|
|
8346
|
|
470
|
|
|
|
|
|
|
my $method = "set_$key"; |
471
|
|
|
|
|
|
|
*{$method} = sub { |
472
|
0
|
|
|
0
|
|
|
my ($self,$value) = @_; |
473
|
0
|
0
|
|
|
|
|
if ( defined $value ) { |
474
|
0
|
0
|
|
|
|
|
$self->{options}{$key} = $value ? $TRUE : $FALSE; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
else { |
477
|
0
|
|
|
|
|
|
delete $self->{options}{$key}; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# setters for scalar & hashref options |
483
|
|
|
|
|
|
|
for my $key ( qw/collation comment maxAwaitTimeMS maxScan maxTimeMS projection/ ) { |
484
|
58
|
|
|
58
|
|
450
|
no strict 'refs'; |
|
58
|
|
|
|
|
138
|
|
|
58
|
|
|
|
|
8627
|
|
485
|
|
|
|
|
|
|
my $method = "set_$key"; |
486
|
|
|
|
|
|
|
*{$method} = sub { |
487
|
0
|
|
|
0
|
|
|
my ($self,$value) = @_; |
488
|
0
|
0
|
|
|
|
|
if ( defined $value ) { |
489
|
0
|
|
|
|
|
|
$self->{options}{$key} = $value; |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
else { |
492
|
0
|
|
|
|
|
|
delete $self->{options}{$key}; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# setters for ordered document options |
498
|
|
|
|
|
|
|
for my $key ( qw/max min sort/ ) { |
499
|
58
|
|
|
58
|
|
439
|
no strict 'refs'; |
|
58
|
|
|
|
|
159
|
|
|
58
|
|
|
|
|
12212
|
|
500
|
|
|
|
|
|
|
my $method = "set_$key"; |
501
|
|
|
|
|
|
|
*{$method} = sub { |
502
|
0
|
|
|
0
|
|
|
my ($self,$value) = @_; |
503
|
0
|
0
|
|
|
|
|
if ( defined $value ) { |
504
|
0
|
|
|
|
|
|
$self->{options}{$key} = to_IxHash($value); |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
else { |
507
|
0
|
|
|
|
|
|
delete $self->{options}{$key}; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# setter for hint, which is an ordered document *or* scalar |
513
|
|
|
|
|
|
|
sub set_hint { |
514
|
0
|
|
|
0
|
0
|
|
my ($self,$value) = @_; |
515
|
0
|
0
|
|
|
|
|
if ( defined $value ) { |
516
|
0
|
0
|
|
|
|
|
$self->{options}{hint} = ref $value ? to_IxHash($value) : $value; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
else { |
519
|
0
|
|
|
|
|
|
delete $self->{options}{hint}; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# setters with default of 0 |
524
|
|
|
|
|
|
|
for my $key ( qw/batchSize limit skip/ ) { |
525
|
58
|
|
|
58
|
|
440
|
no strict 'refs'; |
|
58
|
|
|
|
|
131
|
|
|
58
|
|
|
|
|
6459
|
|
526
|
|
|
|
|
|
|
my $method = "set_$key"; |
527
|
|
|
|
|
|
|
*{$method} = sub { |
528
|
0
|
|
|
0
|
|
|
my ($self,$value) = @_; |
529
|
0
|
|
0
|
|
|
|
$self->{options}{$key} = $value // 0; |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# setters with default of $FALSE |
534
|
|
|
|
|
|
|
for my $key ( qw/allowPartialResults noCursorTimeout/ ) { |
535
|
58
|
|
|
58
|
|
437
|
no strict 'refs'; |
|
58
|
|
|
|
|
140
|
|
|
58
|
|
|
|
|
10902
|
|
536
|
|
|
|
|
|
|
my $method = "set_$key"; |
537
|
|
|
|
|
|
|
*{$method} = sub { |
538
|
0
|
|
|
0
|
|
|
my ($self,$value) = @_; |
539
|
0
|
0
|
|
|
|
|
$self->{options}{$key} = $value ? $TRUE : $FALSE; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# cursorType has a specific default value |
544
|
|
|
|
|
|
|
sub set_cursorType { |
545
|
0
|
|
|
0
|
0
|
|
my ($self,$value) = @_; |
546
|
0
|
|
0
|
|
|
|
$self->{options}{cursorType} = $value // 'non_tailable'; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
sub has_hint { |
550
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
551
|
0
|
|
|
|
|
|
return $self->{options}{hint}; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
1; |