See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 /* 3 * Copyright 2015-present MongoDB, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 namespace MongoDB\Operation; 19 20 use MongoDB\Driver\Command; 21 use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; 22 use MongoDB\Driver\ReadConcern; 23 use MongoDB\Driver\ReadPreference; 24 use MongoDB\Driver\Server; 25 use MongoDB\Driver\Session; 26 use MongoDB\Exception\InvalidArgumentException; 27 use MongoDB\Exception\UnexpectedValueException; 28 use MongoDB\Exception\UnsupportedException; 29 30 use function current; 31 use function is_array; 32 use function is_integer; 33 use function is_object; 34 use function MongoDB\create_field_path_type_map; 35 36 /** 37 * Operation for the distinct command. 38 * 39 * @api 40 * @see \MongoDB\Collection::distinct() 41 * @see https://mongodb.com/docs/manual/reference/command/distinct/ 42 */ 43 class Distinct implements Executable, Explainable 44 { 45 /** @var string */ 46 private $databaseName; 47 48 /** @var string */ 49 private $collectionName; 50 51 /** @var string */ 52 private $fieldName; 53 54 /** @var array|object */ 55 private $filter; 56 57 /** @var array */ 58 private $options; 59 60 /** 61 * Constructs a distinct command. 62 * 63 * Supported options: 64 * 65 * * collation (document): Collation specification. 66 * 67 * * comment (mixed): BSON value to attach as a comment to this command. 68 * 69 * This is not supported for servers versions < 4.4. 70 * 71 * * maxTimeMS (integer): The maximum amount of time to allow the query to 72 * run. 73 * 74 * * readConcern (MongoDB\Driver\ReadConcern): Read concern. 75 * 76 * * readPreference (MongoDB\Driver\ReadPreference): Read preference. 77 * 78 * * session (MongoDB\Driver\Session): Client session. 79 * 80 * * typeMap (array): Type map for BSON deserialization. 81 * 82 * @param string $databaseName Database name 83 * @param string $collectionName Collection name 84 * @param string $fieldName Field for which to return distinct values 85 * @param array|object $filter Query by which to filter documents 86 * @param array $options Command options 87 * @throws InvalidArgumentException for parameter/option parsing errors 88 */ 89 public function __construct(string $databaseName, string $collectionName, string $fieldName, $filter = [], array $options = []) 90 { 91 if (! is_array($filter) && ! is_object($filter)) { 92 throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); 93 } 94 95 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 96 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 97 } 98 99 if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { 100 throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); 101 } 102 103 if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { 104 throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); 105 } 106 107 if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { 108 throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); 109 } 110 111 if (isset($options['session']) && ! $options['session'] instanceof Session) { 112 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 113 } 114 115 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { 116 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); 117 } 118 119 if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { 120 unset($options['readConcern']); 121 } 122 123 $this->databaseName = $databaseName; 124 $this->collectionName = $collectionName; 125 $this->fieldName = $fieldName; 126 $this->filter = $filter; 127 $this->options = $options; 128 } 129 130 /** 131 * Execute the operation. 132 * 133 * @see Executable::execute() 134 * @return array 135 * @throws UnexpectedValueException if the command response was malformed 136 * @throws UnsupportedException if read concern is used and unsupported 137 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 138 */ 139 public function execute(Server $server) 140 { 141 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 142 if ($inTransaction && isset($this->options['readConcern'])) { 143 throw UnsupportedException::readConcernNotSupportedInTransaction(); 144 } 145 146 $cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); 147 148 if (isset($this->options['typeMap'])) { 149 $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'values.$')); 150 } 151 152 $result = current($cursor->toArray()); 153 154 if (! is_object($result) || ! isset($result->values) || ! is_array($result->values)) { 155 throw new UnexpectedValueException('distinct command did not return a "values" array'); 156 } 157 158 return $result->values; 159 } 160 161 /** 162 * Returns the command document for this operation. 163 * 164 * @see Explainable::getCommandDocument() 165 * @return array 166 */ 167 public function getCommandDocument(Server $server) 168 { 169 return $this->createCommandDocument(); 170 } 171 172 /** 173 * Create the distinct command document. 174 */ 175 private function createCommandDocument(): array 176 { 177 $cmd = [ 178 'distinct' => $this->collectionName, 179 'key' => $this->fieldName, 180 ]; 181 182 if (! empty($this->filter)) { 183 $cmd['query'] = (object) $this->filter; 184 } 185 186 if (isset($this->options['collation'])) { 187 $cmd['collation'] = (object) $this->options['collation']; 188 } 189 190 foreach (['comment', 'maxTimeMS'] as $option) { 191 if (isset($this->options[$option])) { 192 $cmd[$option] = $this->options[$option]; 193 } 194 } 195 196 return $cmd; 197 } 198 199 /** 200 * Create options for executing the command. 201 * 202 * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php 203 */ 204 private function createOptions(): array 205 { 206 $options = []; 207 208 if (isset($this->options['readConcern'])) { 209 $options['readConcern'] = $this->options['readConcern']; 210 } 211 212 if (isset($this->options['readPreference'])) { 213 $options['readPreference'] = $this->options['readPreference']; 214 } 215 216 if (isset($this->options['session'])) { 217 $options['session'] = $this->options['session']; 218 } 219 220 return $options; 221 } 222 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body