See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 401]
1 <?php 2 /* 3 * Copyright 2015-2017 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 * http://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 use function current; 30 use function is_array; 31 use function is_integer; 32 use function is_object; 33 use function MongoDB\create_field_path_type_map; 34 use function MongoDB\server_supports_feature; 35 36 /** 37 * Operation for the distinct command. 38 * 39 * @api 40 * @see \MongoDB\Collection::distinct() 41 * @see http://docs.mongodb.org/manual/reference/command/distinct/ 42 */ 43 class Distinct implements Executable, Explainable 44 { 45 /** @var integer */ 46 private static $wireVersionForCollation = 5; 47 48 /** @var integer */ 49 private static $wireVersionForReadConcern = 4; 50 51 /** @var string */ 52 private $databaseName; 53 54 /** @var string */ 55 private $collectionName; 56 57 /** @var string */ 58 private $fieldName; 59 60 /** @var array|object */ 61 private $filter; 62 63 /** @var array */ 64 private $options; 65 66 /** 67 * Constructs a distinct command. 68 * 69 * Supported options: 70 * 71 * * collation (document): Collation specification. 72 * 73 * This is not supported for server versions < 3.4 and will result in an 74 * exception at execution time if used. 75 * 76 * * maxTimeMS (integer): The maximum amount of time to allow the query to 77 * run. 78 * 79 * * readConcern (MongoDB\Driver\ReadConcern): Read concern. 80 * 81 * This is not supported for server versions < 3.2 and will result in an 82 * exception at execution time if used. 83 * 84 * * readPreference (MongoDB\Driver\ReadPreference): Read preference. 85 * 86 * * session (MongoDB\Driver\Session): Client session. 87 * 88 * Sessions are not supported for server versions < 3.6. 89 * 90 * * typeMap (array): Type map for BSON deserialization. 91 * 92 * @param string $databaseName Database name 93 * @param string $collectionName Collection name 94 * @param string $fieldName Field for which to return distinct values 95 * @param array|object $filter Query by which to filter documents 96 * @param array $options Command options 97 * @throws InvalidArgumentException for parameter/option parsing errors 98 */ 99 public function __construct($databaseName, $collectionName, $fieldName, $filter = [], array $options = []) 100 { 101 if (! is_array($filter) && ! is_object($filter)) { 102 throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); 103 } 104 105 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 106 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 107 } 108 109 if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { 110 throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); 111 } 112 113 if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { 114 throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); 115 } 116 117 if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { 118 throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); 119 } 120 121 if (isset($options['session']) && ! $options['session'] instanceof Session) { 122 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 123 } 124 125 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { 126 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); 127 } 128 129 if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { 130 unset($options['readConcern']); 131 } 132 133 $this->databaseName = (string) $databaseName; 134 $this->collectionName = (string) $collectionName; 135 $this->fieldName = (string) $fieldName; 136 $this->filter = $filter; 137 $this->options = $options; 138 } 139 140 /** 141 * Execute the operation. 142 * 143 * @see Executable::execute() 144 * @param Server $server 145 * @return mixed[] 146 * @throws UnexpectedValueException if the command response was malformed 147 * @throws UnsupportedException if collation or read concern is used and unsupported 148 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 149 */ 150 public function execute(Server $server) 151 { 152 if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { 153 throw UnsupportedException::collationNotSupported(); 154 } 155 156 if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { 157 throw UnsupportedException::readConcernNotSupported(); 158 } 159 160 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 161 if ($inTransaction && isset($this->options['readConcern'])) { 162 throw UnsupportedException::readConcernNotSupportedInTransaction(); 163 } 164 165 $cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); 166 167 if (isset($this->options['typeMap'])) { 168 $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'values.$')); 169 } 170 171 $result = current($cursor->toArray()); 172 173 if (! isset($result->values) || ! is_array($result->values)) { 174 throw new UnexpectedValueException('distinct command did not return a "values" array'); 175 } 176 177 return $result->values; 178 } 179 180 public function getCommandDocument(Server $server) 181 { 182 return $this->createCommandDocument(); 183 } 184 185 /** 186 * Create the distinct command document. 187 * 188 * @return array 189 */ 190 private function createCommandDocument() 191 { 192 $cmd = [ 193 'distinct' => $this->collectionName, 194 'key' => $this->fieldName, 195 ]; 196 197 if (! empty($this->filter)) { 198 $cmd['query'] = (object) $this->filter; 199 } 200 201 if (isset($this->options['collation'])) { 202 $cmd['collation'] = (object) $this->options['collation']; 203 } 204 205 if (isset($this->options['maxTimeMS'])) { 206 $cmd['maxTimeMS'] = $this->options['maxTimeMS']; 207 } 208 209 return $cmd; 210 } 211 212 /** 213 * Create options for executing the command. 214 * 215 * @see http://php.net/manual/en/mongodb-driver-server.executereadcommand.php 216 * @return array 217 */ 218 private function createOptions() 219 { 220 $options = []; 221 222 if (isset($this->options['readConcern'])) { 223 $options['readConcern'] = $this->options['readConcern']; 224 } 225 226 if (isset($this->options['readPreference'])) { 227 $options['readPreference'] = $this->options['readPreference']; 228 } 229 230 if (isset($this->options['session'])) { 231 $options['session'] = $this->options['session']; 232 } 233 234 return $options; 235 } 236 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body