├── .gitignore ├── src ├── Utils │ ├── Contract │ │ └── Arrayable.php │ └── Arr.php ├── Exception │ └── MongoDBException.php ├── Command │ ├── Migrations │ │ ├── stubs │ │ │ ├── blank.stub │ │ │ ├── create.stub │ │ │ └── update.stub │ │ ├── TableGuesser.php │ │ ├── MigrateCommand.php │ │ ├── MigrationCommand.php │ │ ├── MigrationCreator.php │ │ └── BaseCommand.php │ └── ConfigCommand.php ├── ConfigProvider.php ├── Pool │ ├── PoolFactory.php │ └── MongoDBPool.php ├── Example │ └── Migrations │ │ └── CreateTestCollection.php ├── MongoDbMigration.php ├── Mongo.php ├── MongoDb.php ├── MongoTask.php └── MongoDbConnection.php ├── publish └── mongodb.php ├── LICENSE ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | -------------------------------------------------------------------------------- /src/Utils/Contract/Arrayable.php: -------------------------------------------------------------------------------- 1 | [ 11 | ], 12 | 'commands' => [ 13 | ], 14 | 'listeners' => [], 15 | // 合并到 config/autoload/annotations.php 文件 16 | 'annotations' => [ 17 | 'scan' => [ 18 | 'paths' => [ 19 | __DIR__, 20 | ], 21 | ], 22 | ], 23 | 'publish' => [ 24 | [ 25 | 'id' => 'config', 26 | 'description' => 'The config of mongodb client.', 27 | 'source' => __DIR__ . '/../publish/mongodb.php', 28 | 'destination' => BASE_PATH . '/config/autoload/mongodb.php', 29 | ], 30 | ], 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Pool/PoolFactory.php: -------------------------------------------------------------------------------- 1 | container = $container; 26 | } 27 | 28 | public function getPool(string $name): MongoDBPool 29 | { 30 | if (isset($this->pools[$name])) { 31 | return $this->pools[$name]; 32 | } 33 | 34 | if ($this->container instanceof Container) { 35 | $pool = $this->container->make(MongoDBPool::class, ['name' => $name]); 36 | } else { 37 | $pool = new MongoDBPool($this->container, $name); 38 | } 39 | return $this->pools[$name] = $pool; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /publish/mongodb.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'uri_options' => [ 6 | 'ssl' => true, 7 | 'username' => env('MONGODB_USERNAME', ''), 8 | 'password' => env('MONGODB_PASSWORD', ''), 9 | // 'authMechanism' => env('MONGODB_AUTH_MECHANISM', 'SCRAM-SHA-256'), 10 | //设置复制集,没有不设置 11 | // 'replicaSet' => 'rs0', 12 | ], 13 | 'host' => env('MONGODB_HOST', '127.0.0.1'), 14 | 'port' => env('MONGODB_PORT', 27017), 15 | 'db' => env('MONGODB_DB', 'test'), 16 | 'driver_options' => [], 17 | 'migration' => [ 18 | 'path' => BASE_PATH . '/migrations/mongodb', // 迁移文件的路径 19 | ], 20 | 'url' => '', // 支持直接使用url的方式连接mongodb 21 | 'pool' => [ 22 | 'min_connections' => 1, 23 | 'max_connections' => 100, 24 | 'connect_timeout' => 10.0, 25 | 'wait_timeout' => 3.0, 26 | 'heartbeat' => -1, 27 | 'max_idle_time' => (float)env('MONGODB_MAX_IDLE_TIME', 60), 28 | ], 29 | ], 30 | ]; 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 phper666 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Example/Migrations/CreateTestCollection.php: -------------------------------------------------------------------------------- 1 | createCollection('test'); // 创建一个表 21 | $data = [ 22 | ['dd' => 1, 'tt' => 2], 23 | ['dd' => 2, 'tt' => 4], 24 | ]; 25 | $msg[] = $this->insertMany('test', $data); // 插入多条数据 26 | $msg[] = $this->createIndex('test', ['dd' => 1, 'tt' => 1]); // 在该表上创建索引 27 | $msg[] = $this->createIndexes('test', [['dd' => 1], ['tt' => 1]]); // 在该表上批量创建索引 28 | $msg[] = $this->dropCollection('test'); // 删除一个表 29 | return $msg; 30 | } 31 | 32 | /** 33 | * 迁移失败时会执行 34 | * @throws \Phper666\MongoDb\Exception\MongoDBException 35 | */ 36 | public function down() 37 | { 38 | return 'error'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phper666/mongo-db", 3 | "license": "MIT", 4 | "authors": [ 5 | { 6 | "name": "liyuzhao", 7 | "email": "562405704@qq.com" 8 | } 9 | ], 10 | "require": { 11 | "php": ">=7.2", 12 | "psr/container": "^1.0", 13 | "mongodb/mongodb": "^1.5" 14 | }, 15 | "require-dev": { 16 | }, 17 | "suggest": { 18 | "hyperf/di": "required hyperf/di ~2.0.0 OR required hyperf/di ~1.1.0", 19 | "hyperf/pool": "required hyperf/pool ~2.0.0 OR required hyperf/pool ~1.1.0", 20 | "hyperf/cache": "required hyperf/cache ~2.0.0 OR required hyperf/cache ~1.1.0", 21 | "hyperf/utils": "required hyperf/utils ~2.0.0 OR required hyperf/utils ~1.1.0", 22 | "hyperf/contract": "required hyperf/contract ~2.0.0 OR required hyperf/contract ~1.1.0", 23 | "hyperf/command": "required hyperf/command ~2.0.0 OR required hyperf/command ~1.1.0", 24 | "hyperf/config": "required hyperf/config ~2.0.0 OR required hyperf/config ~1.1.0", 25 | "hyperf/task": "required hyperf/config ~2.0.0 OR required hyperf/task ~1.1.0" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Phper666\\MongoDb\\": "src/" 30 | }, 31 | "files": [ 32 | ] 33 | }, 34 | "extra": { 35 | "hyperf": { 36 | "config": "Phper666\\MongoDb\\ConfigProvider" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Command/Migrations/TableGuesser.php: -------------------------------------------------------------------------------- 1 | name = $name; 27 | $config = $container->get(ConfigInterface::class); 28 | $key = sprintf('mongodb.%s', $this->name); 29 | if (!$config->has($key)) { 30 | throw new \InvalidArgumentException(sprintf('config[%s] is not exist!', $key)); 31 | } 32 | 33 | $this->config = $config->get($key); 34 | $options = Arr::get($this->config, 'pool', []); 35 | 36 | parent::__construct($container, $options); 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getName(): string 43 | { 44 | return $this->name; 45 | } 46 | 47 | /** 48 | * @return ConnectionInterface 49 | * @throws \Phper666\Mongodb\Exception\MongoDBException 50 | */ 51 | protected function createConnection(): ConnectionInterface 52 | { 53 | return new MongoDbConnection($this->container, $this, $this->config); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Command/ConfigCommand.php: -------------------------------------------------------------------------------- 1 | input->getOption('config'); 26 | if ($argument) { 27 | $this->copySource(__DIR__ . '/../../publish/mongodb.php', BASE_PATH . '/config/autoload/mongodb.php'); 28 | $this->line('The mongodb configuration file has been generated', 'info'); 29 | } 30 | } 31 | 32 | // protected function getArguments() 33 | // { 34 | // return [ 35 | // ['name', InputArgument::OPTIONAL, 'Publish the configuration for jwt-auth'] 36 | // ]; 37 | // } 38 | 39 | protected function getOptions() 40 | { 41 | return [ 42 | ['config', NULL, InputOption::VALUE_NONE, 'Publish the configuration for mongodb'] 43 | ]; 44 | } 45 | 46 | /** 47 | * 复制文件到指定的目录中 48 | * @param $copySource 49 | * @param $toSource 50 | */ 51 | protected function copySource($copySource, $toSource) 52 | { 53 | copy($copySource, $toSource); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Command/Migrations/MigrateCommand.php: -------------------------------------------------------------------------------- 1 | confirmToProceed()) { 42 | return; 43 | } 44 | 45 | $this->setOutput($this->output)->runMigration($this->getMigrationPaths(), []); 46 | 47 | if ($this->input->getOption('seed') && ! $this->input->getOption('pretend')) { 48 | $this->call('mongodb:seed', ['--force' => true]); 49 | } 50 | } 51 | 52 | protected function getOptions(): array 53 | { 54 | return [ 55 | ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], 56 | ['path', null, InputOption::VALUE_OPTIONAL, 'The path to the migrations files to be executed'], 57 | ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], 58 | ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'] 59 | ]; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Utils/Arr.php: -------------------------------------------------------------------------------- 1 | $value) { 23 | if (is_array($value) || is_object($value)) { 24 | $object[$key] = static::toArray($value, $properties, true); 25 | } 26 | } 27 | } 28 | 29 | return $object; 30 | } 31 | 32 | if (is_object($object)) { 33 | if (!empty($properties)) { 34 | $className = get_class($object); 35 | if (!empty($properties[$className])) { 36 | $result = []; 37 | foreach ($properties[$className] as $key => $name) { 38 | if (is_int($key)) { 39 | $result[$name] = $object->$name; 40 | } else { 41 | $result[$key] = static::getValue($object, $name); 42 | } 43 | } 44 | 45 | return $recursive ? static::toArray($result, $properties) : $result; 46 | } 47 | } 48 | if ($object instanceof Arrayable) { 49 | $result = $object->toArray(); 50 | } else { 51 | $result = []; 52 | /** @var array $object */ 53 | foreach ($object as $key => $value) { 54 | $result[$key] = $value; 55 | } 56 | } 57 | 58 | return $recursive ? static::toArray($result, $properties) : $result; 59 | } 60 | 61 | return [$object]; 62 | } 63 | 64 | /** 65 | * @param $array 66 | * @param $key 67 | * @param null $default 68 | * @return mixed|null 69 | */ 70 | public static function getValue($array, $key, $default = null) 71 | { 72 | if ($key instanceof Closure) { 73 | return $key($array, $default); 74 | } 75 | 76 | if (is_array($key)) { 77 | $lastKey = array_pop($key); 78 | /** @var array $key */ 79 | foreach ($key as $keyPart) { 80 | $array = static::getValue($array, $keyPart); 81 | } 82 | $key = $lastKey; 83 | } 84 | 85 | if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { 86 | return $array[$key]; 87 | } 88 | 89 | if (($pos = strrpos($key, '.')) !== false) { 90 | $array = static::getValue($array, substr($key, 0, $pos), $default); 91 | $key = (string)substr($key, $pos + 1); 92 | } 93 | 94 | if (is_object($array)) { 95 | // this is expected to fail if the property does not exist, or __get() is not implemented 96 | // it is not reliably possible to check whether a property is accessable beforehand 97 | return $array->$key; 98 | } 99 | 100 | if (is_array($array)) { 101 | return (isset($array[$key]) || array_key_exists($key, $array)) ? $array[$key] : $default; 102 | } 103 | 104 | return $default; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Command/Migrations/MigrationCommand.php: -------------------------------------------------------------------------------- 1 | input->getArgument('name'))); 57 | 58 | $table = $this->input->getOption('table'); 59 | 60 | $create = $this->input->getOption('create') ?: false; 61 | 62 | // If no table was given as an option but a create option is given then we 63 | // will use the "create" option as the table name. This allows the devs 64 | // to pass a table name into this option as a short-cut for creating. 65 | if (! $table && is_string($create)) { 66 | $table = $create; 67 | 68 | $create = true; 69 | } 70 | 71 | // Next, we will attempt to guess the table name if this the migration has 72 | // "create" in the name. This will allow us to provide a convenient way 73 | // of creating migrations that create new tables for the application. 74 | if (! $table) { 75 | [$table, $create] = TableGuesser::guess($name); 76 | } 77 | 78 | // Now we are ready to write the migration out to disk. Once we've written 79 | // the migration out, we will dump-autoload for the entire framework to 80 | // make sure that the migrations are registered by the class loaders. 81 | $this->writeMigration($name, $table, $create); 82 | } 83 | 84 | protected function getArguments(): array 85 | { 86 | return [ 87 | ['name', InputArgument::REQUIRED, 'The name of the migration'], 88 | ]; 89 | } 90 | 91 | protected function getOptions(): array 92 | { 93 | return [ 94 | ['create', null, InputOption::VALUE_OPTIONAL, 'The table to be created'], 95 | ['table', null, InputOption::VALUE_OPTIONAL, 'The table to migrate'], 96 | ['path', null, InputOption::VALUE_OPTIONAL, 'The location where the migration file should be created'], 97 | ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], 98 | ]; 99 | } 100 | 101 | /** 102 | * @param string $name 103 | * @param string|null $table 104 | * @param bool $create 105 | * @throws \Exception 106 | */ 107 | protected function writeMigration(string $name, ?string $table, bool $create): void 108 | { 109 | $file = pathinfo($this->creator->create( 110 | $name, 111 | $this->getMigrationPath(), 112 | $table, 113 | $create 114 | ), PATHINFO_FILENAME); 115 | 116 | $this->info("[INFO] Created Migration: {$file}"); 117 | } 118 | 119 | /** 120 | * Get migration path (either specified by '--path' option or default location). 121 | * 122 | * @return string 123 | */ 124 | protected function getMigrationPath() 125 | { 126 | if (! is_null($targetPath = $this->input->getOption('path'))) { 127 | return ! $this->usingRealPath() 128 | ? BASE_PATH . '/' . $targetPath 129 | : $targetPath; 130 | } 131 | 132 | return parent::getMigrationPath(); 133 | } 134 | 135 | /** 136 | * Determine if the given path(s) are pre-resolved "real" paths. 137 | */ 138 | protected function usingRealPath(): bool 139 | { 140 | return $this->input->hasOption('realpath') && $this->input->getOption('realpath'); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Command/Migrations/MigrationCreator.php: -------------------------------------------------------------------------------- 1 | files = $files; 42 | } 43 | 44 | /** 45 | * Create a new migration at the given path. 46 | * 47 | * @throws \Exception 48 | */ 49 | public function create(string $name, string $path, string $table = null, bool $create = false): string 50 | { 51 | $this->ensureMigrationDoesntAlreadyExist($name); 52 | 53 | // First we will get the stub file for the migration, which serves as a type 54 | // of template for the migration. Once we have those we will populate the 55 | // various place-holders, save the file, and run the post create event. 56 | $stub = $this->getStub($table, $create); 57 | 58 | if (! file_exists($path)) { 59 | mkdir($path, 0755, true); 60 | } 61 | 62 | $name = 'Mongodb' . $this->getDatePrefix() . ucfirst($name); 63 | $name = $this->getClassName($name); 64 | $this->files->put( 65 | $path = $this->getPath($name, $path), 66 | $this->populateStub($name, $stub, $table) 67 | ); 68 | 69 | // Next, we will fire any hooks that are supposed to fire after a migration is 70 | // created. Once that is done we'll be ready to return the full path to the 71 | // migration file so it can be used however it's needed by the developer. 72 | $this->firePostCreateHooks($table); 73 | 74 | return $path; 75 | } 76 | 77 | /** 78 | * Register a post migration create hook. 79 | */ 80 | public function afterCreate(Closure $callback) 81 | { 82 | $this->postCreate[] = $callback; 83 | } 84 | 85 | /** 86 | * Get the path to the stubs. 87 | */ 88 | public function stubPath(): string 89 | { 90 | return __DIR__ . '/stubs'; 91 | } 92 | 93 | /** 94 | * Get the filesystem instance. 95 | */ 96 | public function getFilesystem(): Filesystem 97 | { 98 | return $this->files; 99 | } 100 | 101 | /** 102 | * Ensure that a migration with the given name doesn't already exist. 103 | * 104 | * @throws \InvalidArgumentException 105 | */ 106 | protected function ensureMigrationDoesntAlreadyExist(string $name) 107 | { 108 | if (class_exists($className = $this->getClassName($name))) { 109 | throw new InvalidArgumentException("A {$className} class already exists."); 110 | } 111 | } 112 | 113 | /** 114 | * Get the migration stub file. 115 | */ 116 | protected function getStub(?string $table, bool $create): string 117 | { 118 | if (is_null($table)) { 119 | return $this->files->get($this->stubPath() . '/blank.stub'); 120 | } 121 | 122 | // We also have stubs for creating new tables and modifying existing tables 123 | // to save the developer some typing when they are creating a new tables 124 | // or modifying existing tables. We'll grab the appropriate stub here. 125 | $stub = $create ? 'create.stub' : 'update.stub'; 126 | 127 | return $this->files->get($this->stubPath() . "/{$stub}"); 128 | } 129 | 130 | /** 131 | * Populate the place-holders in the migration stub. 132 | */ 133 | protected function populateStub(string $name, string $stub, ?string $table): string 134 | { 135 | $stub = str_replace('DummyClass', $this->getClassName($name), $stub); 136 | 137 | // Here we will replace the table place-holders with the table specified by 138 | // the developer, which is useful for quickly creating a tables creation 139 | // or update migration from the console instead of typing it manually. 140 | if (! is_null($table)) { 141 | $stub = str_replace('DummyTable', $table, $stub); 142 | } 143 | 144 | return $stub; 145 | } 146 | 147 | /** 148 | * Get the class name of a migration name. 149 | */ 150 | protected function getClassName(string $name): string 151 | { 152 | return Str::studly($name); 153 | } 154 | 155 | /** 156 | * Get the full path to the migration. 157 | */ 158 | protected function getPath(string $name, string $path): string 159 | { 160 | return $path . '/' . $name . '.php'; 161 | } 162 | 163 | /** 164 | * Fire the registered post create hooks. 165 | */ 166 | protected function firePostCreateHooks(?string $table) 167 | { 168 | foreach ($this->postCreate as $callback) { 169 | call_user_func($callback, $table); 170 | } 171 | } 172 | 173 | /** 174 | * Get the date prefix for the migration. 175 | */ 176 | protected function getDatePrefix(): string 177 | { 178 | return date('YmdHis'); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Command/Migrations/BaseCommand.php: -------------------------------------------------------------------------------- 1 | notes = []; 58 | $migrations = []; 59 | 60 | $files = $this->getMigrationFiles($paths); 61 | 62 | foreach ($files as $file) { 63 | $this->files->requireOnce($file); 64 | $class = $this->getMigrationName($file); 65 | $migrations[] = $class; 66 | $migration = new $class(); 67 | try { 68 | $data = $migration->up(); 69 | $this->output->write($class . ':' . json_encode($data) . PHP_EOL); 70 | }catch (\Exception $exception) { 71 | $data = $migration->down(); 72 | $this->output->write($class . ':' . json_encode($data) . PHP_EOL); 73 | } 74 | } 75 | 76 | return $migrations; 77 | } 78 | 79 | /** 80 | * Require in all the migration files in a given path. 81 | */ 82 | public function requireFiles(array $files): void 83 | { 84 | foreach ($files as $file) { 85 | $this->files->requireOnce($file); 86 | } 87 | } 88 | 89 | /** 90 | * Set the output implementation that should be used by the console. 91 | * 92 | * @return $this 93 | */ 94 | public function setOutput(OutputInterface $output) 95 | { 96 | $this->output = $output; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * Get all of the migration paths. 103 | */ 104 | protected function getMigrationPaths(): array 105 | { 106 | // Here, we will check to see if a path option has been defined. If it has we will 107 | // use the path relative to the root of the installation folder so our database 108 | // migrations may be run for any customized path from within the application. 109 | if ($this->input->hasOption('path') && $this->input->getOption('path')) { 110 | return collect($this->input->getOption('path'))->map(function ($path) { 111 | return ! $this->usingRealPath() 112 | ? BASE_PATH . DIRECTORY_SEPARATOR . $path 113 | : $path; 114 | })->all(); 115 | } 116 | 117 | return array_merge( 118 | $this->paths(), 119 | [$this->getMigrationPath()] 120 | ); 121 | } 122 | 123 | /** 124 | * Determine if the given path(s) are pre-resolved "real" paths. 125 | * 126 | * @return bool 127 | */ 128 | protected function usingRealPath() 129 | { 130 | return $this->input->hasOption('realpath') && $this->input->getOption('realpath'); 131 | } 132 | 133 | /** 134 | * Get the path to the migration directory. 135 | * 136 | * @return string 137 | */ 138 | protected function getMigrationPath() 139 | { 140 | $defaultPath = BASE_PATH . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR . 'mongodb'; 141 | $path = $this->getConfig()['migration']['path'] ?? $defaultPath; 142 | return $path; 143 | } 144 | 145 | /** 146 | * @return mixed 147 | */ 148 | public function getConfig() 149 | { 150 | $config = $this->container->get(ConfigInterface::class); 151 | return $config->get('mongodb.' . $this->poolName); 152 | } 153 | 154 | /** 155 | * Get all of the migration files in a given path. 156 | * 157 | * @param array|string $paths 158 | */ 159 | public function getMigrationFiles($paths): array 160 | { 161 | return Collection::make($paths)->flatMap(function ($path) { 162 | return Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path . '/*.php'); 163 | })->filter()->sortBy(function ($file) { 164 | return $this->getMigrationName($file); 165 | })->values()->keyBy(function ($file) { 166 | return $this->getMigrationName($file); 167 | })->all(); 168 | } 169 | 170 | /** 171 | * Get the name of the migration. 172 | */ 173 | public function getMigrationName(string $path): string 174 | { 175 | return str_replace('.php', '', basename($path)); 176 | } 177 | 178 | /** 179 | * Register a custom migration path. 180 | */ 181 | public function path(string $path): void 182 | { 183 | $this->paths = array_unique(array_merge($this->paths, [$path])); 184 | } 185 | 186 | /** 187 | * Get all of the custom migration paths. 188 | */ 189 | public function paths(): array 190 | { 191 | return $this->paths; 192 | } 193 | 194 | /** 195 | * Write a note to the conosle's output. 196 | * 197 | * @param string $message 198 | */ 199 | protected function note($message) 200 | { 201 | if ($this->output) { 202 | $this->output->writeln($message); 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 默认使用mongodb提供的库来封装,官方git地址:https://github.com/mongodb/mongo-php-library 2 | #### 1、支持类似mysql orm的一些操作 3 | #### 2、支持迁移文件 4 | #### 3、只支持hyperf框架,由于swoole协程不支持mongodb,所以所有的方法都采用task进程来实现,该包已经封装好所有的方法都会投递到task进程进行操作,task进程建议开启多一点 5 | #### 4、该包默认使用了连接池 6 | 7 | ### 注意: 8 | 1、v1.0+版本只会维护不增加功能 9 | 2、开启v2.0分支,跟1.0有很大区别,因为hyperf采用了laravel的orm,所以我会参考laravel-mongodb来封装 10 | 3、使用v1.0+时,如果你开一百多个task进程处理,你的宿主机内存不大时,并发请求多,导致内存没有被释放,这时候你的宿主机可能会down机,所以建议开启swoole的task配置,task_max_request=2,这个配置的意思是task进程处理两次请求后会自动kill这个task进程,重新拉起一个新的task进程,这样就不会导致你的宿主机内存过大而导致服务器down机 11 | 12 | v1.0+有个大bug,目前无法解决,实际是目前操作mongodb都没有丢到task处理,需要hyperf2.0才能解决,具体情况这个issue[https://github.com/hyperf/hyperf/issues/1798] 13 | 14 | 这个bug目前我们在项目中如何解决,有两种方法: 15 | 1、在model里面新增一个getMethod方法,这个方法申请要丢到task处理 16 | ``` 17 | {$method}(...$params); 39 | } 40 | } 41 | 42 | // 调用,实际跟原有的findOne调用的参数是一致的,只不过第一个参数时申明要调用申明方法 43 | $data = make(Test::class)->getMethod('findOne', ['name' => '1'], ['projection' => ['name' => 1]]); 44 | 45 | ``` 46 | 2、在你使用Test这个模型的方法直接申明丢到Task处理,这个方式不是万能的,因为Task不能处理一些需要在协程下处理的东西,例如co或者协程http,所以只能是第一种和第二种方法结合使用 47 | 48 | ### 使用 49 | #### 1、拉取包 50 | ``` 51 | composer require phper666/mongo-db 52 | ``` 53 | #### 2、发布配置 54 | ``` 55 | php bin/hyperf.php mongodb:publish --config 56 | ``` 57 | #### 3、配置介绍 58 | ``` 59 | [ 63 | 'uri_options' => [ 64 | 'ssl' => true, 65 | 'username' => env('MONGODB_USERNAME', ''), 66 | 'password' => env('MONGODB_PASSWORD', ''), 67 | // 'authMechanism' => env('MONGODB_AUTH_MECHANISM', 'SCRAM-SHA-256'), 68 | //设置复制集,没有不设置 69 | // 'replicaSet' => 'rs0', 70 | ], 71 | 'host' => env('MONGODB_HOST', '127.0.0.1'), 72 | 'port' => env('MONGODB_PORT', 27017), 73 | 'db' => env('MONGODB_DB', 'test'), 74 | 'driver_options' => [], 75 | 'migration' => [ 76 | 'path' => BASE_PATH . '/migrations/mongodb', // 迁移文件的路径 77 | ], 78 | 'pool' => [ 79 | 'min_connections' => 1, 80 | 'max_connections' => 100, 81 | 'connect_timeout' => 10.0, 82 | 'wait_timeout' => 3.0, 83 | 'heartbeat' => -1, 84 | 'max_idle_time' => (float)env('MONGODB_MAX_IDLE_TIME', 60), 85 | ], 86 | ], 87 | ]; 88 | ``` 89 | #### 4、生成迁移文件 90 | ``` 91 | php bin/hyperf.php mongodb:migration Test 92 | ``` 93 | 上面的命令会自动生成一个迁移文件,会生成一个文件到配置文件指定的迁移目录中 94 | #### 5、迁移文件例子 95 | ``` 96 | createCollection('test'); // 创建一个表 110 | $data = [ 111 | ['dd' => 1, 'tt' => 2], 112 | ['dd' => 2, 'tt' => 4], 113 | ]; 114 | $msg[] = $this->insertMany('test', $data); // 插入多条数据 115 | $msg[] = $this->createIndex('test', ['dd' => 1, 'tt' => 1]); // 在该表上创建索引 116 | $msg[] = $this->createIndexes('test', [['dd' => 1], ['tt' => 1]]); // 在该表上批量创建索引 117 | $msg[] = $this->dropCollection('test'); // 删除一个表 118 | return $msg; 119 | } 120 | 121 | /** 122 | * 迁移失败时会执行 123 | * @throws \Phper666\MongoDb\Exception\MongoDBException 124 | */ 125 | public function down() 126 | { 127 | return 'error'; 128 | } 129 | } 130 | ``` 131 | #### 6、迁移命令 132 | ``` 133 | php bin/hyperf.php mongodb:migrate 134 | ``` 135 | 上面这个命令会迁移你所有生成的文件,迁移文件路径在配置文件里面配置 136 | #### 7、开发使用 137 | 1、上面能像orm一样能进行迁移了,解决了升级的问题,下面我们来说一下开发时候怎么使用 138 | 2、在你的项目里面新建一个目录,该目录叫mongo(自行命名,类似orm的model) 139 | 3、比如我现在项目里面有一个库,叫test,test里面有两个collection,名字为co1,co2(你把它当成mysql的表一样) 140 | 4、我在mongo目录新建两个文件,叫Co1Mongo和Co2Mongo,都继承\Phper666\MongoDb\MongoDb 141 | ``` 142 | co1Mongo->findOne()); 188 | } 189 | } 190 | 191 | 调用test方法时,就能查出co1表中的一条数据了,是不是很简单! 192 | ``` 193 | 6、支持有多种方法,详细你可以到Phper666\MongoDb\MongoDb查看,或者你可以去看官方的php-mongodb文档,https://docs.mongodb.com/php-library/v1.5/reference/method/MongoDBCollection-createIndexes/ 194 | #### 8、结束 195 | 如果你有使用的问题或者建议,欢迎你提一个isset,由于太匆忙,等我开发完现在的项目,我会重新优化和迭代这个包,如果开发中有遇到问题或者有更好的写法,我会迭代到这个包这里。 196 | #### 9、注意 197 | 如果你使用的是mongodb默认生成_id,那么更新和删除我默认已经帮你使用MongoDB\BSON\ObjectId进行了转换,所以你无需再转换,你直接把_id对应的字符串传进去就好了,比如要根据_id获取某条数据,$filter=['_id' => 'xxxxxx']即可。获取数据时,我也默认帮你把_id转成了字符串 198 | 199 | #### 10、遇到问题 200 | 1、因为我使用了hyperf框架,所以基本都是单例模式。我model里面使用\Phper666\MongoDb\MongoDb 的setCollectionName方法来切换表时,必须处理完逻辑后然后再切回原始的collectionName,否则你查询的会是你切换的表 201 | -------------------------------------------------------------------------------- /src/MongoDbMigration.php: -------------------------------------------------------------------------------- 1 | container = ApplicationContext::getContainer(); 57 | $config = $this->container->get(ConfigInterface::class); 58 | $this->config = $config->get('mongodb.' . $this->poolName); 59 | $this->getConnection(); 60 | } 61 | 62 | /** 63 | * @param string $collectionName 64 | * @param array $collectionOptions 65 | * @return Collection 66 | */ 67 | protected function collection(string $collectionName, array $collectionOptions = []) 68 | { 69 | $collection = make(Collection::class, [$this->mongoClient->getManager(), $this->config['db'], $collectionName, $collectionOptions]); 70 | return $collection; 71 | } 72 | 73 | /** 74 | * @param array $options 75 | * @return Database 76 | */ 77 | protected function database(array $options = []) 78 | { 79 | $database = make(Database::class, [$this->mongoClient->getManager(), $this->config['db'], $options]); 80 | return $database; 81 | } 82 | 83 | /** 84 | * 返回满足条件的第一个数据 85 | * 86 | * @param string $collectionName 87 | * @param array $filter 88 | * @param array $options 89 | * @param array $collectionOptions 90 | * @return array 91 | * @throws MongoDBException 92 | */ 93 | public function findOne(string $collectionName, $filter = [], array $options = [], array $collectionOptions = []) 94 | { 95 | try { 96 | $this->handleFilter($filter); 97 | $options = ['limit' => 1] + $options; 98 | if (empty($options['typeMap'])) { 99 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 100 | } 101 | $cursor = $this->collection($collectionName, $collectionOptions)->find($filter, $options); 102 | $result = []; 103 | foreach ($cursor as $document) { 104 | $document = (array)$document; 105 | $document['_id'] = (string)$document['_id']; 106 | $result[] = $document; 107 | } 108 | return $result; 109 | } catch (\Exception $e) { 110 | throw new MongoDBException($this->handleErrorMsg($e)); 111 | } 112 | } 113 | 114 | /** 115 | * 返回满足filer的全部数据 116 | * 117 | * @param string $collectionName 118 | * @param array $filter 119 | * @param array $options 120 | * @param array $collectionOptions 121 | * @return array 122 | * @throws MongoDBException 123 | */ 124 | public function findAll(string $collectionName, array $filter = [], array $options = [], array $collectionOptions = []) 125 | { 126 | try { 127 | $this->handleFilter($filter); 128 | $result = []; 129 | if (empty($options['typeMap'])) { 130 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 131 | } 132 | $cursor = $this->collection($collectionName, $collectionOptions)->find($filter, $options); 133 | foreach ($cursor as $document) { 134 | $document = (array)$document; 135 | $document['_id'] = (string)$document['_id']; 136 | $result[] = $document; 137 | } 138 | return $result; 139 | } catch (\Exception $e) { 140 | throw new MongoDBException($this->handleErrorMsg($e)); 141 | } 142 | } 143 | 144 | /** 145 | * 插入多个数据 146 | * 147 | * @param string $collectionName 148 | * @param array $documents 149 | * @param array $options 150 | * @param array $collectionOptions 151 | * @return mixed[]|string 152 | */ 153 | public function insertMany(string $collectionName, array $documents = [], array $options = [], array $collectionOptions = []) 154 | { 155 | try { 156 | return $this->collection($collectionName, $collectionOptions)->insertMany($documents, $options)->getInsertedIds(); 157 | } catch (\Exception $e) { 158 | return $this->handleErrorMsg($e); 159 | } 160 | } 161 | 162 | /** 163 | * 插入一个数据 164 | * 165 | * @param string $collectionName 166 | * @param array $documents 167 | * @param array $options 168 | * @param array $collectionOptions 169 | * @return mixed|string 170 | */ 171 | public function insertOne(string $collectionName, $document = [], array $options = [], array $collectionOptions = []) 172 | { 173 | try { 174 | return $this->collection($collectionName, $collectionOptions)->insertOne($document, $options)->getInsertedId(); 175 | } catch (\Exception $e) { 176 | return $this->handleErrorMsg($e); 177 | } 178 | } 179 | 180 | /** 181 | * 更新匹配的到的所有数据 182 | * 183 | * @param string $collectionName 184 | * @param $filter 185 | * @param $update 186 | * @param array $options 187 | * @param array $collectionOptions 188 | * @return int|null|bool|string 189 | */ 190 | public function updateMany(string $collectionName, $filter, $update, array $options = [], array $collectionOptions = []) 191 | { 192 | try { 193 | $this->handleFilter($filter); 194 | return $this->collection($collectionName, $collectionOptions)->updateMany($filter, $update, $options)->getModifiedCount(); 195 | } catch (\Exception $e) { 196 | return $this->handleErrorMsg($e); 197 | } 198 | } 199 | 200 | /** 201 | * 更新匹配的到的一条数据 202 | * 203 | * @param string $collectionName 204 | * @param $filter 205 | * @param $update 206 | * @param array $options 207 | * @param array $collectionOptions 208 | * @return int|null|bool|string 209 | */ 210 | public function updateOne(string $collectionName, $filter, $update, array $options = [], array $collectionOptions = []) 211 | { 212 | try { 213 | $this->handleFilter($filter); 214 | return $this->collection($collectionName, $collectionOptions)->updateOne($filter, $update, $options)->getModifiedCount(); 215 | } catch (\Exception $e) { 216 | return $this->handleErrorMsg($e); 217 | } 218 | } 219 | 220 | /** 221 | * 数据更新,效果是满足filter的行,只更新$newObj中的$set出现的字段 222 | * http://php.net/manual/zh/mongodb-driver-bulkwrite.update.php 223 | * $bulk->update( 224 | * ['x' => 2], 225 | * ['$set' => ['y' => 3]], 226 | * ['multi' => false, 'upsert' => false] 227 | * ); 228 | * 229 | * @param string $collectionName 230 | * @param array $filter 231 | * @param array $update 232 | * @param array $options 233 | * @param array $collectionOptions 234 | * @return int|null|string 235 | */ 236 | public function updateRow(string $collectionName, array $filter = [], array $update = [], array $options = [], array $collectionOptions = []) 237 | { 238 | try { 239 | $this->handleFilter($filter); 240 | return $this->collection($collectionName, $collectionOptions)->updateMany($filter, $update, $options)->getModifiedCount(); 241 | } catch (\Exception $e) { 242 | return $this->handleErrorMsg($e); 243 | } 244 | } 245 | 246 | /** 247 | * 删除匹配到的多条数据 248 | * 249 | * @param string $collectionName 250 | * @param $filter 251 | * @param array $options 252 | * @param array $collectionOptions 253 | * @return int|bool|string 254 | */ 255 | public function deleteMany(string $collectionName, $filter, array $options = [], array $collectionOptions = []) 256 | { 257 | try { 258 | $this->handleFilter($filter); 259 | return $this->collection($collectionName, $collectionOptions)->deleteMany($filter, $options)->getDeletedCount(); 260 | } catch (\Exception $e) { 261 | return $this->handleErrorMsg($e); 262 | } 263 | } 264 | 265 | /** 266 | * 删除一条匹配的数据 267 | * 268 | * @param string $collectionName 269 | * @param $filter 270 | * @param array $options 271 | * @param array $collectionOptions 272 | * @return int|bool|string 273 | */ 274 | public function deleteOne(string $collectionName, $filter, array $options = [], array $collectionOptions = []) 275 | { 276 | try { 277 | $this->handleFilter($filter); 278 | return $this->collection($collectionName, $collectionOptions)->deleteOne($filter, $options)->getDeletedCount(); 279 | } catch (\Exception $e) { 280 | return $this->handleErrorMsg($e); 281 | } 282 | } 283 | 284 | /** 285 | * 通过ids删除 286 | * @param string $collectionName 287 | * @param array $ids 288 | * @param array $options 289 | * @param array $collectionOptions 290 | * @return bool|int|string 291 | */ 292 | public function deleteByIds(string $collectionName, array $ids, array $options = [], array $collectionOptions = []) 293 | { 294 | try { 295 | if (empty($ids)) return false; 296 | $filter = []; 297 | foreach ($ids as $k => $id) { 298 | $ids[$k] = new ObjectId($id); 299 | } 300 | $filter['_id']['$in'] = $ids; 301 | return $this->collection($collectionName, $collectionOptions)->deleteMany($filter, $options)->getDeletedCount(); 302 | } catch (\Exception $e) { 303 | return $this->handleErrorMsg($e); 304 | } 305 | } 306 | 307 | /** 308 | * 判断是否已经存在索引 309 | * 310 | * @param string $collectionName 311 | * @param $key 312 | * @return bool 313 | */ 314 | public function isExistIndex(string $collectionName, $key) 315 | { 316 | foreach ($this->listIndexes($collectionName) as $index) { 317 | $keys = array_keys($index->getKey()); 318 | if (is_array($key) && ($keys === $key)) { 319 | return true; 320 | } 321 | 322 | if (is_string($key) && in_array($key, $keys)) { 323 | return true; 324 | } 325 | } 326 | 327 | return false; 328 | } 329 | 330 | /** 331 | * 创建索引 332 | * 333 | * @param string $collectionName 334 | * @param $key 335 | * @param array $options 336 | * @param array $collectionOptions 337 | * @return string 338 | * @throws MongoDBException 339 | */ 340 | public function createIndex(string $collectionName, $key, array $options = [], array $collectionOptions = []) 341 | { 342 | try { 343 | return $this->collection($collectionName, $collectionOptions)->createIndex($key, $options); 344 | } catch (\Exception $e) { 345 | return $this->handleErrorMsg($e); 346 | } 347 | } 348 | 349 | /** 350 | * 批量创建索引 351 | * 352 | * @param string $collectionName 353 | * @param array $indexes 354 | * @param array $options 355 | * @param array $collectionOptions 356 | * @return string|string[] 357 | */ 358 | public function createIndexes(string $collectionName, array $indexes, array $options = [], array $collectionOptions = []) 359 | { 360 | try { 361 | return $this->collection($collectionName, $collectionOptions)->createIndexes($indexes, $options); 362 | } catch (\Exception $e) { 363 | return $this->handleErrorMsg($e); 364 | } 365 | } 366 | 367 | /** 368 | * 获取所有索引信息 369 | * 370 | * @param string $collectionName 371 | * @param array $options 372 | * @param array $collectionOptions 373 | * @return \MongoDB\Model\IndexInfoIterator|string 374 | */ 375 | public function listIndexes(string $collectionName, array $options = [], array $collectionOptions = []) 376 | { 377 | try { 378 | return $this->collection($collectionName, $collectionOptions)->listIndexes($options); 379 | } catch (\Exception $e) { 380 | return $this->handleErrorMsg($e); 381 | } 382 | } 383 | 384 | /** 385 | * 移除索引 386 | * 387 | * @param string $collectionName 388 | * @param $indexName 389 | * @param array $options 390 | * @param array $collectionOptions 391 | * @return array|object|string 392 | */ 393 | public function dropIndex(string $collectionName, $indexName, array $options = [], array $collectionOptions = []) 394 | { 395 | try { 396 | return $this->collection($collectionName, $collectionOptions)->dropIndex($indexName, $options); 397 | } catch (\Exception $e) { 398 | return $this->handleErrorMsg($e); 399 | } 400 | } 401 | 402 | /** 403 | * 移除集合中所有的索引 404 | * 405 | * @param string $collectionName 406 | * @param array $options 407 | * @param array $collectionOptions 408 | * @return array|object|string 409 | */ 410 | public function dropIndexes(string $collectionName, array $options = [], array $collectionOptions = []) 411 | { 412 | try { 413 | return $this->collection($collectionName, $collectionOptions)->dropIndexes($options); 414 | } catch (\Exception $e) { 415 | return $this->handleErrorMsg($e); 416 | } 417 | } 418 | 419 | /** 420 | * Executes a map-reduce aggregation on the collection. 421 | * 422 | * @param string $collectionName 423 | * @param JavascriptInterface $map 424 | * @param JavascriptInterface $reduce 425 | * @param $out 426 | * @param array $options 427 | * @param array $collectionOptions 428 | * @return \MongoDB\MapReduceResult|string 429 | */ 430 | public function mapReduce(string $collectionName, JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [], array $collectionOptions = []) 431 | { 432 | try { 433 | return $this->collection($collectionName, $collectionOptions)->mapReduce($map, $reduce, $out, $options); 434 | } catch (\Exception $e) { 435 | return $this->handleErrorMsg($e); 436 | } 437 | } 438 | 439 | /** 440 | * Replaces at most one document matching the filter. 441 | * 442 | * @param string $collectionName 443 | * @param $filter 444 | * @param $replacement 445 | * @param array $options 446 | * @param array $collectionOptions 447 | * @return \MongoDB\UpdateResult|string 448 | */ 449 | public function replaceOne(string $collectionName, $filter, $replacement, array $options = [], array $collectionOptions = []) 450 | { 451 | try { 452 | return $this->collection($collectionName, $collectionOptions)->replaceOne($filter, $replacement, $options); 453 | } catch (\Exception $e) { 454 | return $this->handleErrorMsg($e); 455 | } 456 | } 457 | 458 | /** 459 | * Explains explainable commands 460 | * 461 | * @param string $collectionName 462 | * @param Explainable $explainable 463 | * @param array $options 464 | * @param array $collectionOptions 465 | * @return array|object|string 466 | */ 467 | public function explain(string $collectionName, Explainable $explainable, array $options = [], array $collectionOptions = []) 468 | { 469 | try { 470 | return $this->collection($collectionName, $collectionOptions)->explain($explainable, $options); 471 | } catch (\Exception $e) { 472 | return $this->handleErrorMsg($e); 473 | } 474 | } 475 | 476 | /** 477 | * 判断是否已经存在表 478 | * 479 | * @param string $collectionName 480 | * @return bool 481 | */ 482 | public function isExistCollection(string $collectionName) 483 | { 484 | $collections = []; 485 | foreach ($this->listCollections() as $collectionInfo) { 486 | $collections[] = $collectionInfo->getName(); 487 | } 488 | if (array_keys($collections, $collectionName)) { 489 | return true; 490 | } 491 | 492 | return false; 493 | } 494 | 495 | /** 496 | * 创建集合 497 | * 498 | * @param string $collectionName 499 | * @param array $options 500 | * @param array $databaseOptions 501 | * @return array|object|string 502 | */ 503 | public function createCollection(string $collectionName, array $options = [], array $databaseOptions = []) 504 | { 505 | try { 506 | return $this->database($databaseOptions)->createCollection($collectionName, $options); 507 | } catch (\Exception $e) { 508 | return $this->handleErrorMsg($e); 509 | } 510 | } 511 | 512 | /** 513 | * 移除集合 514 | * 515 | * @param string $collectionName 516 | * @param array $options 517 | * @param array $databaseOptions 518 | * @return array|object|string 519 | */ 520 | public function dropCollection(string $collectionName, array $options = [], array $databaseOptions = []) 521 | { 522 | try { 523 | return $this->database($databaseOptions)->dropCollection($collectionName, $options); 524 | } catch (\Exception $e) { 525 | return $this->handleErrorMsg($e); 526 | } 527 | } 528 | 529 | /** 530 | * 修改集合 531 | * 532 | * @param string $collectionName 533 | * @param array $collectionOptions 534 | * @param array $options 535 | * @param array $databaseOptions 536 | * @return array|object|string 537 | */ 538 | public function modifyCollection(string $collectionName, array $collectionOptions = [], array $options = [], array $databaseOptions = []) 539 | { 540 | try { 541 | return $this->database($databaseOptions)->modifyCollection($collectionName, $collectionOptions, $options); 542 | } catch (\Exception $e) { 543 | return $this->handleErrorMsg($e); 544 | } 545 | } 546 | 547 | /** 548 | * 显示所有的集合 549 | * 550 | * @param array $options 551 | * @param array $databaseOptions 552 | * @return \MongoDB\Model\CollectionInfoIterator|string 553 | */ 554 | public function listCollections(array $options = [], array $databaseOptions = []) 555 | { 556 | try { 557 | return $this->database($databaseOptions)->listCollections($options); 558 | } catch (\Exception $e) { 559 | return $this->handleErrorMsg($e); 560 | } 561 | } 562 | 563 | /** 564 | * @throws MongoDBException 565 | */ 566 | private function getConnection() 567 | { 568 | try { 569 | /** 570 | * http://php.net/manual/zh/mongodb-driver-manager.construct.php 571 | */ 572 | $username = $this->config['username'] ?? ''; 573 | $password = $this->config['password'] ?? ''; 574 | $authMechanism = $this->config['authMechanism'] ?? ''; 575 | $replicaSet = $this->config['replicaSet'] ?? ''; 576 | $driverOptions = $this->config['driver_options'] ?? []; 577 | $uriOptions = $this->config['uri_options'] ?? []; 578 | if (empty($uriOptions['username'])) { 579 | $uriOptions['username'] = $username; 580 | } 581 | if (empty($uriOptions['password'])) { 582 | $uriOptions['password'] = $password; 583 | } 584 | if (empty($uriOptions['replicaSet']) && !empty($replicaSet)) { 585 | $uriOptions['replicaSet'] = $replicaSet; 586 | } 587 | if (empty($uriOptions['authMechanism']) && !empty($authMechanism)) { 588 | $uriOptions['authMechanism'] = $authMechanism; 589 | } 590 | $uri = sprintf( 591 | 'mongodb://%s:%d/%s', 592 | $this->config['host'], 593 | $this->config['port'], 594 | $this->config['db'] 595 | ); 596 | if (!empty($this->config['url'])) { 597 | $uri = $this->config['url']; 598 | $uriOptions = []; 599 | } 600 | $this->mongoClient = new Client($uri, $uriOptions, $driverOptions); 601 | } catch (InvalidArgumentException $e) { 602 | throw MongoDBException::managerError('mongodb 连接参数错误:' . $e->getMessage()); 603 | } catch (RuntimeException $e) { 604 | throw MongoDBException::managerError('mongodb uri格式错误:' . $e->getMessage()); 605 | } 606 | $this->lastUseTime = microtime(true); 607 | return $this; 608 | } 609 | 610 | /** 611 | * @param $filter 612 | * @return array 613 | */ 614 | private function handleFilter(&$filter) 615 | { 616 | if (is_array($filter) && !empty($filter['_id']) && !($filter['_id'] instanceof ObjectId)) { 617 | $filter['_id'] = new ObjectId($filter['_id']); 618 | } 619 | if (is_object($filter) && !empty($filter->_id) && !($filter->_id instanceof ObjectId)) { 620 | $filter->_id = new ObjectId($filter->_id); 621 | } 622 | return $filter; 623 | } 624 | 625 | /** 626 | * @param $e 627 | * @return string 628 | */ 629 | private function handleErrorMsg($e) 630 | { 631 | return $e->getFile() . PHP_EOL .$e->getLine(). PHP_EOL . $e->getMessage(); 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /src/Mongo.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 62 | } 63 | 64 | /** 65 | * @param $collectionName 66 | */ 67 | public function setCollectionName($collectionName) 68 | { 69 | $this->collectionName = $collectionName; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @param bool $bool 75 | */ 76 | public function setTimestamps(bool $bool) 77 | { 78 | $this->timestamps = $bool; 79 | return $this; 80 | } 81 | 82 | /** 83 | * 返回满足条件的第一个数据 84 | * @param $filter 85 | * @param array $options 86 | * @return array 87 | * @throws MongoDBException 88 | */ 89 | public function findOne($filter = [], array $options = [], array $collectionOptions = []): array 90 | { 91 | try { 92 | /** 93 | * @var $collection MongoDBConnection 94 | */ 95 | $collection = $this->getConnection(); 96 | return $collection->findOne($this->collectionName, $filter, $options, $collectionOptions); 97 | } catch (\Exception $e) { 98 | throw new MongoDBException($this->handleErrorMsg($e)); 99 | } 100 | } 101 | 102 | /** 103 | * 返回满足filer的全部数据 104 | * @param array $filter 105 | * @param array $options 106 | * @param array $collectionOptions 107 | * @return array 108 | * @throws MongoDBException 109 | */ 110 | public function findAll(array $filter = [], array $options = [], array $collectionOptions = []) 111 | { 112 | try { 113 | /** 114 | * @var $collection MongoDBConnection 115 | */ 116 | $collection = $this->getConnection(); 117 | return $collection->findAll($this->collectionName, $filter, $options, $collectionOptions); 118 | } catch (\Exception $e) { 119 | throw new MongoDBException($this->handleErrorMsg($e)); 120 | } 121 | } 122 | 123 | /** 124 | * 返回满足filer的分页数据 125 | * @param int $currentPage 126 | * @param int $limit 127 | * @param array $filter 128 | * @param array $options 129 | * @param array $collectionOptions 130 | * @return array 131 | * @throws MongoDBException 132 | */ 133 | public function findPagination(int $currentPage, int $limit, array $filter = [], array $options = [], array $collectionOptions = []) 134 | { 135 | try { 136 | /** 137 | * @var $collection MongoDBConnection 138 | */ 139 | $collection = $this->getConnection(); 140 | return $collection->findPagination($this->collectionName, $currentPage, $limit, $filter, $options, $collectionOptions); 141 | } catch (\Exception $e) { 142 | throw new MongoDBException($this->handleErrorMsg($e)); 143 | } 144 | } 145 | 146 | /** 147 | * 使用command的方式查询出数据 148 | * @param array $command 149 | * @return array 150 | * @throws MongoDBException 151 | * @throws \MongoDB\Driver\Exception\Exception 152 | */ 153 | public function findByCommand(array $command = []): array 154 | { 155 | try { 156 | /** 157 | * @var $collection MongoDBConnection 158 | */ 159 | $collection = $this->getConnection(); 160 | return $collection->queryByCommand($command); 161 | } catch (\Exception $e) { 162 | throw new MongoDBException($this->handleErrorMsg($e)); 163 | } 164 | } 165 | 166 | /** 167 | * 查找单个文档并删除它,返回原始文档 168 | * @param $filter 169 | * @param array $options 170 | * @param array $collectionOptions 171 | * @return array 172 | * @throws MongoDBException 173 | */ 174 | public function findOneAndDelete($filter, array $options = [], array $collectionOptions = []) 175 | { 176 | try { 177 | /** 178 | * @var $collection MongoDBConnection 179 | */ 180 | $collection = $this->getConnection(); 181 | return $collection->findOneAndDelete($this->collectionName, $filter, $options, $collectionOptions); 182 | } catch (\Exception $e) { 183 | throw new MongoDBException($this->handleErrorMsg($e)); 184 | } 185 | } 186 | 187 | /** 188 | * 查找单个文档并替换它,返回原始文档或替换文件 189 | * @param $filter 190 | * @param $replacement 191 | * @param array $options 192 | * @param array $collectionOptions 193 | * @return array|object|null 194 | * @throws MongoDBException 195 | */ 196 | public function findOneAndReplace($filter, $replacement, array $options = [], array $collectionOptions = []) 197 | { 198 | try { 199 | /** 200 | * @var $collection MongoDBConnection 201 | */ 202 | $collection = $this->getConnection(); 203 | return $collection->findOneAndReplace($this->collectionName, $filter, $replacement, $options, $collectionOptions); 204 | } catch (\Exception $e) { 205 | throw new MongoDBException($this->handleErrorMsg($e)); 206 | } 207 | } 208 | 209 | /** 210 | * 查找单个文档并更新它,返回原始文档或更新后的文件 211 | * @param $filter 212 | * @param $update 213 | * @param array $options 214 | * @param array $collectionOptions 215 | * @return array|object|null 216 | * @throws MongoDBException 217 | */ 218 | public function findOneAndUpdate($filter, $update, array $options = [], array $collectionOptions = []) 219 | { 220 | try { 221 | /** 222 | * @var $collection MongoDBConnection 223 | */ 224 | $collection = $this->getConnection(); 225 | return $collection->findOneAndUpdate($this->collectionName, $filter, $update, $options, $collectionOptions); 226 | } catch (\Exception $e) { 227 | throw new MongoDBException($this->handleErrorMsg($e)); 228 | } 229 | } 230 | 231 | /** 232 | * 插入多个数据 233 | * @param array $documents 234 | * @param array $options 235 | * @param array $collectionOptions 236 | * @return bool|mixed[] 237 | * @throws MongoDBException 238 | */ 239 | public function insertMany(array $documents = [], array $options = [], array $collectionOptions = []) 240 | { 241 | try { 242 | if ($this->timestamps) { 243 | $time = time(); 244 | foreach ($documents as &$document) { 245 | $document[self::CREATED_AT] = $time; 246 | $document[self::UPDATED_AT] = $time; 247 | } 248 | } 249 | /** 250 | * @var $collection MongoDBConnection 251 | */ 252 | $collection = $this->getConnection(); 253 | return $collection->insertMany($this->collectionName, $documents, $options, $collectionOptions); 254 | } catch (\Exception $e) { 255 | throw new MongoDBException($this->handleErrorMsg($e)); 256 | } 257 | } 258 | 259 | /** 260 | * 插入一个数据 261 | * @param array $document 262 | * @param array $options 263 | * @param array $collectionOptions 264 | * @return mixed 265 | * @throws MongoDBException 266 | */ 267 | public function insertOne($document = [], array $options = [], array $collectionOptions = []) 268 | { 269 | try { 270 | if ($this->timestamps) { 271 | $time = time(); 272 | $document[self::CREATED_AT] = $time; 273 | $document[self::UPDATED_AT] = $time; 274 | } 275 | /** 276 | * @var $collection MongoDBConnection 277 | */ 278 | $collection = $this->getConnection(); 279 | return $collection->insertOne($this->collectionName, $document, $options, $collectionOptions); 280 | } catch (\Exception $e) { 281 | throw new MongoDBException($this->handleErrorMsg($e)); 282 | } 283 | } 284 | 285 | /** 286 | * 更新匹配的到的所有数据 287 | * @param $filter 288 | * @param $update 289 | * @param array $options 290 | * @param array $collectionOptions 291 | * @return int|null 292 | * @throws MongoDBException 293 | */ 294 | public function updateMany($filter, $update, array $options = [], array $collectionOptions = []) 295 | { 296 | try { 297 | if ($this->timestamps) { 298 | $update['$set'][self::UPDATED_AT] = time(); 299 | } 300 | /** 301 | * @var $collection MongoDBConnection 302 | */ 303 | $collection = $this->getConnection(); 304 | return $collection->updateMany($this->collectionName, $filter, $update, $options, $collectionOptions); 305 | } catch (\Exception $e) { 306 | throw new MongoDBException($this->handleErrorMsg($e)); 307 | } 308 | } 309 | 310 | /** 311 | * 更新匹配的到的一条数据 312 | * @param $filter 313 | * @param $update 314 | * @param array $options 315 | * @param array $collectionOptions 316 | * @return int|null 317 | * @throws MongoDBException 318 | */ 319 | public function updateOne($filter, $update, array $options = [], array $collectionOptions = []) 320 | { 321 | try { 322 | if ($this->timestamps) { 323 | $update['$set'][self::UPDATED_AT] = time(); 324 | } 325 | /** 326 | * @var $collection MongoDBConnection 327 | */ 328 | $collection = $this->getConnection(); 329 | return $collection->updateOne($this->collectionName, $filter, $update, $options, $collectionOptions); 330 | } catch (\Exception $e) { 331 | throw new MongoDBException($this->handleErrorMsg($e)); 332 | } 333 | } 334 | 335 | /** 336 | * 更新数据满足$filter的行的信息成$newObject 337 | * @param array $filter 338 | * @param array $update 339 | * @param array $options 340 | * @return int|null 341 | * @throws MongoDBException 342 | */ 343 | public function updateRow(array $filter = [], array $update = [], array $options = ['multi' => false, 'upsert' => false]) 344 | { 345 | try { 346 | if ($this->timestamps) { 347 | $update['$set'][self::UPDATED_AT] = time(); 348 | } 349 | /** 350 | * @var $collection MongoDBConnection 351 | */ 352 | $collection = $this->getConnection(); 353 | return $collection->updateRow($this->collectionName, $filter, $update, $options); 354 | } catch (\Exception $e) { 355 | throw new MongoDBException($this->handleErrorMsg($e)); 356 | } 357 | } 358 | 359 | /** 360 | * 删除匹配到的多条数据 361 | * @param $filter 362 | * @param array $options 363 | * @param array $collectionOptions 364 | * @return int 365 | * @throws MongoDBException 366 | */ 367 | public function deleteMany($filter, array $options = [], array $collectionOptions = []) 368 | { 369 | try { 370 | /** 371 | * @var $collection MongoDBConnection 372 | */ 373 | $collection = $this->getConnection(); 374 | return $collection->deleteMany($this->collectionName, $filter, $options, $collectionOptions); 375 | } catch (\Exception $e) { 376 | throw new MongoDBException($this->handleErrorMsg($e)); 377 | } 378 | } 379 | 380 | /** 381 | * 删除一条匹配的数据 382 | * @param $filter 383 | * @param array $options 384 | * @param array $collectionOptions 385 | * @return int 386 | * @throws MongoDBException 387 | */ 388 | public function deleteOne($filter, array $options = [], array $collectionOptions = []) 389 | { 390 | try { 391 | /** 392 | * @var $collection MongoDBConnection 393 | */ 394 | $collection = $this->getConnection(); 395 | return $collection->deleteOne($this->collectionName, $filter, $options, $collectionOptions); 396 | } catch (\Exception $e) { 397 | throw new MongoDBException($this->handleErrorMsg($e)); 398 | } 399 | } 400 | 401 | /** 402 | * 通过多个_id删除数据 403 | * @param array $ids 404 | * @param array $options 405 | * @return int 406 | * @throws MongoDBException 407 | */ 408 | public function deleteByIds(array $ids = [], array $options = []) 409 | { 410 | try { 411 | /** 412 | * @var $collection MongoDBConnection 413 | */ 414 | $collection = $this->getConnection(); 415 | return $collection->deleteByIds($this->collectionName, $ids, $options); 416 | } catch (\Exception $e) { 417 | throw new MongoDBException($this->handleErrorMsg($e)); 418 | } 419 | } 420 | 421 | /** 422 | * 查找集合中指定字段的不同值 423 | * @param string $fieldName 424 | * @param array $filter 425 | * @param array $options 426 | * @param array $collectionOptions 427 | * @return array|mixed[] 428 | * @throws MongoDBException 429 | */ 430 | public function distinct(string $fieldName, $filter = [], array $options = [], array $collectionOptions = []) 431 | { 432 | try { 433 | /** 434 | * @var $collection MongoDBConnection 435 | */ 436 | $collection = $this->getConnection(); 437 | return $collection->distinct($this->collectionName, $fieldName, $filter, $options, $collectionOptions); 438 | } catch (\Exception $e) { 439 | throw new MongoDBException($this->handleErrorMsg($e)); 440 | } 441 | } 442 | 443 | /** 444 | * 聚合查询 445 | * @param array $pipeline 446 | * @param array $options 447 | * @param array $collectionOptions 448 | * @return \Traversable 449 | * @throws MongoDBException 450 | */ 451 | public function aggregate(array $pipeline = [], array $options = [], array $collectionOptions = []) 452 | { 453 | try { 454 | /** 455 | * @var $collection MongoDBConnection 456 | */ 457 | $collection = $this->getConnection(); 458 | return $collection->aggregate($this->collectionName, $pipeline, $options, $collectionOptions); 459 | } catch (\Exception $e) { 460 | throw new MongoDBException($this->handleErrorMsg($e)); 461 | } 462 | } 463 | 464 | /** 465 | * 获取查询满足的的数量 466 | * @param $filter 467 | * @param array $options 468 | * @param array $collectionOptions 469 | * @return int 470 | * @throws MongoDBException 471 | */ 472 | public function countDocuments($filter = [], array $options = [], array $collectionOptions = []) 473 | { 474 | try { 475 | /** 476 | * @var $collection MongoDBConnection 477 | */ 478 | $collection = $this->getConnection(); 479 | return $collection->countDocuments($this->collectionName, $filter, $options, $collectionOptions); 480 | } catch (\Exception $e) { 481 | throw new MongoDBException($this->handleErrorMsg($e)); 482 | } 483 | } 484 | 485 | /** 486 | * 使用集合元数据获取集合中文档的估计数量 487 | * @param array $options 488 | * @param array $collectionOptions 489 | * @return int 490 | * @throws MongoDBException 491 | */ 492 | public function estimatedDocumentCount(array $options = [], array $collectionOptions = []) 493 | { 494 | try { 495 | /** 496 | * @var $collection MongoDBConnection 497 | */ 498 | $collection = $this->getConnection(); 499 | return $collection->estimatedDocumentCount($this->collectionName, $options, $collectionOptions); 500 | } catch (\Exception $e) { 501 | throw new MongoDBException($this->handleErrorMsg($e)); 502 | } 503 | } 504 | 505 | /** 506 | * 创建索引 507 | * @param $key 508 | * @param array $options 509 | * @param array $collectionOptions 510 | * @return mixed 511 | * @throws MongoDBException 512 | */ 513 | public function createIndex($key, array $options = [], array $collectionOptions = []) 514 | { 515 | try { 516 | /** 517 | * @var $collection MongoDBConnection 518 | */ 519 | $collection = $this->getConnection(); 520 | return $collection->createIndex($this->collectionName, $key, $options, $collectionOptions); 521 | } catch (\Exception $e) { 522 | throw new MongoDBException($this->handleErrorMsg($e)); 523 | } 524 | } 525 | 526 | /** 527 | * 批量创建索引 528 | * @param array $indexes 529 | * @param array $options 530 | * @param array $collectionOptions 531 | * @return string[] 532 | * @throws MongoDBException 533 | */ 534 | public function createIndexes(array $indexes, array $options = [], array $collectionOptions = []) 535 | { 536 | try { 537 | /** 538 | * @var $collection MongoDBConnection 539 | */ 540 | $collection = $this->getConnection(); 541 | return $collection->createIndexes($this->collectionName, $indexes, $options, $collectionOptions); 542 | } catch (\Exception $e) { 543 | throw new MongoDBException($this->handleErrorMsg($e)); 544 | } 545 | } 546 | 547 | /** 548 | * 获取所有索引信息 549 | * @param array $options 550 | * @param array $collectionOptions 551 | * @return \MongoDB\Model\IndexInfoIterator 552 | * @throws MongoDBException 553 | */ 554 | public function listIndexes(array $options = [], array $collectionOptions = []) 555 | { 556 | try { 557 | /** 558 | * @var $collection MongoDBConnection 559 | */ 560 | $collection = $this->getConnection(); 561 | return $collection->listIndexes($this->collectionName, $options, $collectionOptions); 562 | } catch (\Exception $e) { 563 | throw new MongoDBException($this->handleErrorMsg($e)); 564 | } 565 | } 566 | 567 | /** 568 | * 移除索引 569 | * @param $indexName 570 | * @param array $options 571 | * @param array $collectionOptions 572 | * @return array|object 573 | * @throws MongoDBException 574 | */ 575 | public function dropIndex($indexName, array $options = [], array $collectionOptions = []) 576 | { 577 | try { 578 | /** 579 | * @var $collection MongoDBConnection 580 | */ 581 | $collection = $this->getConnection(); 582 | return $collection->dropIndex($this->collectionName, $indexName, $options, $collectionOptions); 583 | } catch (\Exception $e) { 584 | throw new MongoDBException($this->handleErrorMsg($e)); 585 | } 586 | } 587 | 588 | /** 589 | * 移除集合中所有的索引 590 | * @param array $options 591 | * @param array $collectionOptions 592 | * @return array|object 593 | * @throws MongoDBException 594 | */ 595 | public function dropIndexes(array $options = [], array $collectionOptions = []) 596 | { 597 | try { 598 | /** 599 | * @var $collection MongoDBConnection 600 | */ 601 | $collection = $this->getConnection(); 602 | return $collection->dropIndexes($this->collectionName, $options, $collectionOptions); 603 | } catch (\Exception $e) { 604 | throw new MongoDBException($this->handleErrorMsg($e)); 605 | } 606 | } 607 | 608 | /** 609 | * Executes a map-reduce aggregation on the collection. 610 | * @param JavascriptInterface $map 611 | * @param JavascriptInterface $reduce 612 | * @param $out 613 | * @param array $options 614 | * @param array $collectionOptions 615 | * @return array 616 | * @throws MongoDBException 617 | */ 618 | public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [], array $collectionOptions = []) 619 | { 620 | try { 621 | /** 622 | * @var $collection MongoDBConnection 623 | */ 624 | $collection = $this->getConnection(); 625 | $data = $collection->mapReduce($this->collectionName, $map, $reduce, $out, $options, $collectionOptions); 626 | return Arr::toArray($data); 627 | } catch (\Exception $e) { 628 | throw new MongoDBException($this->handleErrorMsg($e)); 629 | } 630 | } 631 | 632 | /** 633 | * Replaces at most one document matching the filter. 634 | * @param $filter 635 | * @param $replacement 636 | * @param array $options 637 | * @param array $collectionOptions 638 | * @return int|null 639 | * @throws MongoDBException 640 | */ 641 | public function replaceOne($filter, $replacement, array $options = [], array $collectionOptions = []) 642 | { 643 | try { 644 | /** 645 | * @var $collection MongoDBConnection 646 | */ 647 | $collection = $this->getConnection(); 648 | return $collection->replaceOne($this->collectionName, $filter, $replacement, $options, $collectionOptions); 649 | } catch (\Exception $e) { 650 | throw new MongoDBException($this->handleErrorMsg($e)); 651 | } 652 | } 653 | 654 | /** 655 | * Explains explainable commands 656 | * @param Explainable $explainable Command on which to run explain 657 | * @param array $options 658 | * @param array $collectionOptions 659 | * @return mixed 660 | * @throws MongoDBException 661 | */ 662 | public function explain(Explainable $explainable, array $options = [], array $collectionOptions = []) 663 | { 664 | try { 665 | /** 666 | * @var $collection MongoDBConnection 667 | */ 668 | $collection = $this->getConnection(); 669 | return current($collection->explain($this->collectionName, $explainable, $options, $collectionOptions)); 670 | } catch (\Exception $e) { 671 | throw new MongoDBException($this->handleErrorMsg($e)); 672 | } 673 | } 674 | 675 | /** 676 | * 创建集合 677 | * @param string $collectionName 678 | * @param array $options 679 | * @param array $databaseOptions 680 | * @return array|object 681 | * @throws MongoDBException 682 | */ 683 | public function createCollection(array $options = [], array $databaseOptions = []) 684 | { 685 | try { 686 | /** 687 | * @var $collection MongoDBConnection 688 | */ 689 | $collection = $this->getConnection(); 690 | return $collection->createCollection($this->collectionName, $options, $databaseOptions); 691 | } catch (\Exception $e) { 692 | throw new MongoDBException($this->handleErrorMsg($e)); 693 | } 694 | } 695 | 696 | /** 697 | * 移除集合 698 | * @param string $collectionName 699 | * @param array $options 700 | * @param array $databaseOptions 701 | * @return array|object 702 | * @throws MongoDBException 703 | */ 704 | public function dropCollection(array $options = [], array $databaseOptions = []) 705 | { 706 | try { 707 | /** 708 | * @var $collection MongoDBConnection 709 | */ 710 | $collection = $this->getConnection(); 711 | return $collection->dropCollection($this->collectionName, $options, $databaseOptions); 712 | } catch (\Exception $e) { 713 | throw new MongoDBException($this->handleErrorMsg($e)); 714 | } 715 | } 716 | 717 | /** 718 | * 修改集合 719 | * @param string $collectionName 720 | * @param array $collectionOptions 721 | * @param array $options 722 | * @param array $databaseOptions 723 | * @return array|object 724 | * @throws MongoDBException 725 | */ 726 | public function modifyCollection(array $collectionOptions = [], array $options = [], array $databaseOptions = []) 727 | { 728 | try { 729 | /** 730 | * @var $collection MongoDBConnection 731 | */ 732 | $collection = $this->getConnection(); 733 | return $collection->modifyCollection($this->collectionName, $collectionOptions, $options, $databaseOptions); 734 | } catch (\Exception $e) { 735 | throw new MongoDBException($this->handleErrorMsg($e)); 736 | } 737 | } 738 | 739 | /** 740 | * 显示所有的集合 TODO 741 | * @param array $options 742 | * @param array $databaseOptions 743 | * @return array|\MongoDB\Model\CollectionInfoIterator 744 | * @throws MongoDBException 745 | */ 746 | public function listCollections(array $options = [], array $databaseOptions = []) 747 | { 748 | try { 749 | /** 750 | * @var $collection MongoDBConnection 751 | */ 752 | $collection = $this->getConnection(); 753 | return $collection->listCollections($options, $databaseOptions); 754 | } catch (\Exception $e) { 755 | throw new MongoDBException($this->handleErrorMsg($e)); 756 | } 757 | } 758 | 759 | /** 760 | * 使用command的方式获取个数 761 | * 762 | * @param array $command 763 | * @return int 764 | * @throws MongoDBException 765 | * @throws \MongoDB\Driver\Exception\Exception 766 | */ 767 | public function countByCommand(array $command = []) 768 | { 769 | try { 770 | /** 771 | * @var $collection MongoDBConnection 772 | */ 773 | $collection = $this->getConnection(); 774 | return $collection->countByCommand($command); 775 | } catch (\Exception $e) { 776 | throw new MongoDBException($this->handleErrorMsg($e)); 777 | } 778 | } 779 | 780 | /** 781 | * 聚合查询 782 | * @param array $command 783 | * @return array 784 | * @throws MongoDBException 785 | * @throws \MongoDB\Driver\Exception\Exception 786 | */ 787 | public function command(array $command = []) 788 | { 789 | try { 790 | /** 791 | * @var $collection MongoDBConnection 792 | */ 793 | $collection = $this->getConnection(); 794 | return $collection->command($command); 795 | } catch (\Exception $e) { 796 | throw new MongoDBException($this->handleErrorMsg($e)); 797 | } 798 | } 799 | 800 | /** 801 | * @return mixed|null 802 | */ 803 | private function getConnection() 804 | { 805 | $connection = null; 806 | $hasContextConnection = Context::has($this->getContextKey()); 807 | if ($hasContextConnection) { 808 | $connection = Context::get($this->getContextKey()); 809 | } 810 | if (!$connection instanceof MongoDbConnection) { 811 | $pool = $this->factory->getPool($this->poolName); 812 | $connection = $pool->get()->getConnection(); 813 | } 814 | return $connection; 815 | } 816 | 817 | /** 818 | * The key to identify the connection object in coroutine context. 819 | */ 820 | private function getContextKey(): string 821 | { 822 | return sprintf('mongodb.connection.%s', $this->poolName); 823 | } 824 | 825 | /** 826 | * @param $e 827 | * @return string 828 | */ 829 | private function handleErrorMsg($e) 830 | { 831 | return $e->getFile() . $e->getLine() . $e->getMessage(); 832 | } 833 | 834 | public function __set($name, $value) 835 | { 836 | $this->$name = $value; 837 | return $this; 838 | } 839 | } 840 | -------------------------------------------------------------------------------- /src/MongoDb.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 63 | } 64 | 65 | /** 66 | * @param $collectionName 67 | */ 68 | public function setCollectionName($collectionName) 69 | { 70 | $this->collectionName = $collectionName; 71 | return $this; 72 | } 73 | 74 | /** 75 | * @param bool $bool 76 | */ 77 | public function setTimestamps(bool $bool) 78 | { 79 | $this->timestamps = $bool; 80 | return $this; 81 | } 82 | 83 | /** 84 | * 返回满足条件的第一个数据 85 | * 86 | * @Task(timeout=30) 87 | * @param $filter 88 | * @param array $options 89 | * @return array 90 | * @throws MongoDBException 91 | */ 92 | public function findOne($filter = [], array $options = [], array $collectionOptions = []): array 93 | { 94 | try { 95 | /** 96 | * @var $collection MongoDBConnection 97 | */ 98 | $collection = $this->getConnection(); 99 | return $collection->findOne($this->collectionName, $filter, $options, $collectionOptions); 100 | } catch (\Exception $e) { 101 | throw new MongoDBException($this->handleErrorMsg($e)); 102 | } 103 | } 104 | 105 | /** 106 | * 返回满足filer的全部数据 107 | * 108 | * @Task(timeout=30) 109 | * @param array $filter 110 | * @param array $options 111 | * @param array $collectionOptions 112 | * @return array 113 | * @throws MongoDBException 114 | */ 115 | public function findAll(array $filter = [], array $options = [], array $collectionOptions = []) 116 | { 117 | try { 118 | /** 119 | * @var $collection MongoDBConnection 120 | */ 121 | $collection = $this->getConnection(); 122 | return $collection->findAll($this->collectionName, $filter, $options, $collectionOptions); 123 | } catch (\Exception $e) { 124 | throw new MongoDBException($this->handleErrorMsg($e)); 125 | } 126 | } 127 | 128 | /** 129 | * 返回满足filer的分页数据 130 | * 131 | * @Task(timeout=30) 132 | * @param int $currentPage 133 | * @param int $limit 134 | * @param array $filter 135 | * @param array $options 136 | * @param array $collectionOptions 137 | * @return array 138 | * @throws MongoDBException 139 | */ 140 | public function findPagination(int $currentPage, int $limit, array $filter = [], array $options = [], array $collectionOptions = []) 141 | { 142 | try { 143 | /** 144 | * @var $collection MongoDBConnection 145 | */ 146 | $collection = $this->getConnection(); 147 | return $collection->findPagination($this->collectionName, $currentPage, $limit, $filter, $options, $collectionOptions); 148 | } catch (\Exception $e) { 149 | throw new MongoDBException($this->handleErrorMsg($e)); 150 | } 151 | } 152 | 153 | /** 154 | * 使用command的方式查询出数据 155 | * 156 | * @Task(timeout=30) 157 | * @param array $command 158 | * @return array 159 | * @throws MongoDBException 160 | * @throws \MongoDB\Driver\Exception\Exception 161 | */ 162 | public function findByCommand(array $command = []): array 163 | { 164 | try { 165 | /** 166 | * @var $collection MongoDBConnection 167 | */ 168 | $collection = $this->getConnection(); 169 | return $collection->queryByCommand($command); 170 | } catch (\Exception $e) { 171 | throw new MongoDBException($this->handleErrorMsg($e)); 172 | } 173 | } 174 | 175 | /** 176 | * 查找单个文档并删除它,返回原始文档 177 | * 178 | * @Task(timeout=30) 179 | * @param $filter 180 | * @param array $options 181 | * @param array $collectionOptions 182 | * @return array 183 | * @throws MongoDBException 184 | */ 185 | public function findOneAndDelete($filter, array $options = [], array $collectionOptions = []) 186 | { 187 | try { 188 | /** 189 | * @var $collection MongoDBConnection 190 | */ 191 | $collection = $this->getConnection(); 192 | return $collection->findOneAndDelete($this->collectionName, $filter, $options, $collectionOptions); 193 | } catch (\Exception $e) { 194 | throw new MongoDBException($this->handleErrorMsg($e)); 195 | } 196 | } 197 | 198 | /** 199 | * 查找单个文档并替换它,返回原始文档或替换文件 200 | * 201 | * @Task(timeout=30) 202 | * @param $filter 203 | * @param $replacement 204 | * @param array $options 205 | * @param array $collectionOptions 206 | * @return array|object|null 207 | * @throws MongoDBException 208 | */ 209 | public function findOneAndReplace($filter, $replacement, array $options = [], array $collectionOptions = []) 210 | { 211 | try { 212 | /** 213 | * @var $collection MongoDBConnection 214 | */ 215 | $collection = $this->getConnection(); 216 | return $collection->findOneAndReplace($this->collectionName, $filter, $replacement, $options, $collectionOptions); 217 | } catch (\Exception $e) { 218 | throw new MongoDBException($this->handleErrorMsg($e)); 219 | } 220 | } 221 | 222 | /** 223 | * 查找单个文档并更新它,返回原始文档或更新后的文件 224 | * 225 | * @Task(timeout=30) 226 | * @param $filter 227 | * @param $update 228 | * @param array $options 229 | * @param array $collectionOptions 230 | * @return array|object|null 231 | * @throws MongoDBException 232 | */ 233 | public function findOneAndUpdate($filter, $update, array $options = [], array $collectionOptions = []) 234 | { 235 | try { 236 | /** 237 | * @var $collection MongoDBConnection 238 | */ 239 | $collection = $this->getConnection(); 240 | return $collection->findOneAndUpdate($this->collectionName, $filter, $update, $options, $collectionOptions); 241 | } catch (\Exception $e) { 242 | throw new MongoDBException($this->handleErrorMsg($e)); 243 | } 244 | } 245 | 246 | /** 247 | * 插入多个数据 248 | * 249 | * @Task(timeout=30) 250 | * @param array $documents 251 | * @param array $options 252 | * @param array $collectionOptions 253 | * @return bool|mixed[] 254 | * @throws MongoDBException 255 | */ 256 | public function insertMany(array $documents = [], array $options = [], array $collectionOptions = []) 257 | { 258 | try { 259 | if ($this->timestamps) { 260 | $time = time(); 261 | foreach ($documents as &$document) { 262 | $document[self::CREATED_AT] = $time; 263 | $document[self::UPDATED_AT] = $time; 264 | } 265 | } 266 | /** 267 | * @var $collection MongoDBConnection 268 | */ 269 | $collection = $this->getConnection(); 270 | return $collection->insertMany($this->collectionName, $documents, $options, $collectionOptions); 271 | } catch (\Exception $e) { 272 | throw new MongoDBException($this->handleErrorMsg($e)); 273 | } 274 | } 275 | 276 | /** 277 | * 插入一个数据 278 | * 279 | * @Task(timeout=30) 280 | * @param array $document 281 | * @param array $options 282 | * @param array $collectionOptions 283 | * @return mixed 284 | * @throws MongoDBException 285 | */ 286 | public function insertOne($document = [], array $options = [], array $collectionOptions = []) 287 | { 288 | try { 289 | if ($this->timestamps) { 290 | $time = time(); 291 | $document[self::CREATED_AT] = $time; 292 | $document[self::UPDATED_AT] = $time; 293 | } 294 | /** 295 | * @var $collection MongoDBConnection 296 | */ 297 | $collection = $this->getConnection(); 298 | return $collection->insertOne($this->collectionName, $document, $options, $collectionOptions); 299 | } catch (\Exception $e) { 300 | throw new MongoDBException($this->handleErrorMsg($e)); 301 | } 302 | } 303 | 304 | /** 305 | * 更新匹配的到的所有数据 306 | * 307 | * @Task(timeout=30) 308 | * @param $filter 309 | * @param $update 310 | * @param array $options 311 | * @param array $collectionOptions 312 | * @return int|null 313 | * @throws MongoDBException 314 | */ 315 | public function updateMany($filter, $update, array $options = [], array $collectionOptions = []) 316 | { 317 | try { 318 | if ($this->timestamps) { 319 | $update['$set'][self::UPDATED_AT] = time(); 320 | } 321 | /** 322 | * @var $collection MongoDBConnection 323 | */ 324 | $collection = $this->getConnection(); 325 | return $collection->updateMany($this->collectionName, $filter, $update, $options, $collectionOptions); 326 | } catch (\Exception $e) { 327 | throw new MongoDBException($this->handleErrorMsg($e)); 328 | } 329 | } 330 | 331 | /** 332 | * 更新匹配的到的一条数据 333 | * 334 | * @Task(timeout=30) 335 | * @param $filter 336 | * @param $update 337 | * @param array $options 338 | * @param array $collectionOptions 339 | * @return int|null 340 | * @throws MongoDBException 341 | */ 342 | public function updateOne($filter, $update, array $options = [], array $collectionOptions = []) 343 | { 344 | try { 345 | if ($this->timestamps) { 346 | $update['$set'][self::UPDATED_AT] = time(); 347 | } 348 | /** 349 | * @var $collection MongoDBConnection 350 | */ 351 | $collection = $this->getConnection(); 352 | return $collection->updateOne($this->collectionName, $filter, $update, $options, $collectionOptions); 353 | } catch (\Exception $e) { 354 | throw new MongoDBException($this->handleErrorMsg($e)); 355 | } 356 | } 357 | 358 | /** 359 | * 更新数据满足$filter的行的信息成$newObject 360 | * 361 | * @Task(timeout=30) 362 | * @param array $filter 363 | * @param array $update 364 | * @param array $options 365 | * @return int|null 366 | * @throws MongoDBException 367 | */ 368 | public function updateRow(array $filter = [], array $update = [], array $options = ['multi' => false, 'upsert' => false]) 369 | { 370 | try { 371 | if ($this->timestamps) { 372 | $update['$set'][self::UPDATED_AT] = time(); 373 | } 374 | /** 375 | * @var $collection MongoDBConnection 376 | */ 377 | $collection = $this->getConnection(); 378 | return $collection->updateRow($this->collectionName, $filter, $update, $options); 379 | } catch (\Exception $e) { 380 | throw new MongoDBException($this->handleErrorMsg($e)); 381 | } 382 | } 383 | 384 | /** 385 | * 删除匹配到的多条数据 386 | * 387 | * @Task(timeout=30) 388 | * @param $filter 389 | * @param array $options 390 | * @param array $collectionOptions 391 | * @return int 392 | * @throws MongoDBException 393 | */ 394 | public function deleteMany($filter, array $options = [], array $collectionOptions = []) 395 | { 396 | try { 397 | /** 398 | * @var $collection MongoDBConnection 399 | */ 400 | $collection = $this->getConnection(); 401 | return $collection->deleteMany($this->collectionName, $filter, $options, $collectionOptions); 402 | } catch (\Exception $e) { 403 | throw new MongoDBException($this->handleErrorMsg($e)); 404 | } 405 | } 406 | 407 | /** 408 | * 删除一条匹配的数据 409 | * 410 | * @Task(timeout=30) 411 | * @param $filter 412 | * @param array $options 413 | * @param array $collectionOptions 414 | * @return int 415 | * @throws MongoDBException 416 | */ 417 | public function deleteOne($filter, array $options = [], array $collectionOptions = []) 418 | { 419 | try { 420 | /** 421 | * @var $collection MongoDBConnection 422 | */ 423 | $collection = $this->getConnection(); 424 | return $collection->deleteOne($this->collectionName, $filter, $options, $collectionOptions); 425 | } catch (\Exception $e) { 426 | throw new MongoDBException($this->handleErrorMsg($e)); 427 | } 428 | } 429 | 430 | /** 431 | * 通过多个_id删除数据 432 | * 433 | * @Task(timeout=30) 434 | * @param array $ids 435 | * @param array $options 436 | * @return int 437 | * @throws MongoDBException 438 | */ 439 | public function deleteByIds(array $ids = [], array $options = []) 440 | { 441 | try { 442 | /** 443 | * @var $collection MongoDBConnection 444 | */ 445 | $collection = $this->getConnection(); 446 | return $collection->deleteByIds($this->collectionName, $ids, $options); 447 | } catch (\Exception $e) { 448 | throw new MongoDBException($this->handleErrorMsg($e)); 449 | } 450 | } 451 | 452 | /** 453 | * 查找集合中指定字段的不同值 454 | * 455 | * @Task(timeout=30) 456 | * @param string $fieldName 457 | * @param array $filter 458 | * @param array $options 459 | * @param array $collectionOptions 460 | * @return array|mixed[] 461 | * @throws MongoDBException 462 | */ 463 | public function distinct(string $fieldName, $filter = [], array $options = [], array $collectionOptions = []) 464 | { 465 | try { 466 | /** 467 | * @var $collection MongoDBConnection 468 | */ 469 | $collection = $this->getConnection(); 470 | return $collection->distinct($this->collectionName, $fieldName, $filter, $options, $collectionOptions); 471 | } catch (\Exception $e) { 472 | throw new MongoDBException($this->handleErrorMsg($e)); 473 | } 474 | } 475 | 476 | /** 477 | * 聚合查询 478 | * 479 | * @Task(timeout=30) 480 | * @param array $pipeline 481 | * @param array $options 482 | * @param array $collectionOptions 483 | * @return \Traversable 484 | * @throws MongoDBException 485 | */ 486 | public function aggregate(array $pipeline = [], array $options = [], array $collectionOptions = []) 487 | { 488 | try { 489 | /** 490 | * @var $collection MongoDBConnection 491 | */ 492 | $collection = $this->getConnection(); 493 | return $collection->aggregate($this->collectionName, $pipeline, $options, $collectionOptions); 494 | } catch (\Exception $e) { 495 | throw new MongoDBException($this->handleErrorMsg($e)); 496 | } 497 | } 498 | 499 | /** 500 | * 获取查询满足的的数量 501 | * 502 | * @Task(timeout=30) 503 | * @param $filter 504 | * @param array $options 505 | * @param array $collectionOptions 506 | * @return int 507 | * @throws MongoDBException 508 | */ 509 | public function countDocuments($filter = [], array $options = [], array $collectionOptions = []) 510 | { 511 | try { 512 | /** 513 | * @var $collection MongoDBConnection 514 | */ 515 | $collection = $this->getConnection(); 516 | return $collection->countDocuments($this->collectionName, $filter, $options, $collectionOptions); 517 | } catch (\Exception $e) { 518 | throw new MongoDBException($this->handleErrorMsg($e)); 519 | } 520 | } 521 | 522 | /** 523 | * 使用集合元数据获取集合中文档的估计数量 524 | * 525 | * @Task(timeout=30) 526 | * @param array $options 527 | * @param array $collectionOptions 528 | * @return int 529 | * @throws MongoDBException 530 | */ 531 | public function estimatedDocumentCount(array $options = [], array $collectionOptions = []) 532 | { 533 | try { 534 | /** 535 | * @var $collection MongoDBConnection 536 | */ 537 | $collection = $this->getConnection(); 538 | return $collection->estimatedDocumentCount($this->collectionName, $options, $collectionOptions); 539 | } catch (\Exception $e) { 540 | throw new MongoDBException($this->handleErrorMsg($e)); 541 | } 542 | } 543 | 544 | /** 545 | * 创建索引 546 | * 547 | * @Task(timeout=30) 548 | * @param $key 549 | * @param array $options 550 | * @param array $collectionOptions 551 | * @return mixed 552 | * @throws MongoDBException 553 | */ 554 | public function createIndex($key, array $options = [], array $collectionOptions = []) 555 | { 556 | try { 557 | /** 558 | * @var $collection MongoDBConnection 559 | */ 560 | $collection = $this->getConnection(); 561 | return $collection->createIndex($this->collectionName, $key, $options, $collectionOptions); 562 | } catch (\Exception $e) { 563 | throw new MongoDBException($this->handleErrorMsg($e)); 564 | } 565 | } 566 | 567 | /** 568 | * 批量创建索引 569 | * 570 | * @Task(timeout=30) 571 | * @param array $indexes 572 | * @param array $options 573 | * @param array $collectionOptions 574 | * @return string[] 575 | * @throws MongoDBException 576 | */ 577 | public function createIndexes(array $indexes, array $options = [], array $collectionOptions = []) 578 | { 579 | try { 580 | /** 581 | * @var $collection MongoDBConnection 582 | */ 583 | $collection = $this->getConnection(); 584 | return $collection->createIndexes($this->collectionName, $indexes, $options, $collectionOptions); 585 | } catch (\Exception $e) { 586 | throw new MongoDBException($this->handleErrorMsg($e)); 587 | } 588 | } 589 | 590 | /** 591 | * 获取所有索引信息 592 | * 593 | * @Task(timeout=30) 594 | * @param array $options 595 | * @param array $collectionOptions 596 | * @return \MongoDB\Model\IndexInfoIterator 597 | * @throws MongoDBException 598 | */ 599 | public function listIndexes(array $options = [], array $collectionOptions = []) 600 | { 601 | try { 602 | /** 603 | * @var $collection MongoDBConnection 604 | */ 605 | $collection = $this->getConnection(); 606 | return $collection->listIndexes($this->collectionName, $options, $collectionOptions); 607 | } catch (\Exception $e) { 608 | throw new MongoDBException($this->handleErrorMsg($e)); 609 | } 610 | } 611 | 612 | /** 613 | * 移除索引 614 | * 615 | * @Task(timeout=30) 616 | * @param $indexName 617 | * @param array $options 618 | * @param array $collectionOptions 619 | * @return array|object 620 | * @throws MongoDBException 621 | */ 622 | public function dropIndex($indexName, array $options = [], array $collectionOptions = []) 623 | { 624 | try { 625 | /** 626 | * @var $collection MongoDBConnection 627 | */ 628 | $collection = $this->getConnection(); 629 | return $collection->dropIndex($this->collectionName, $indexName, $options, $collectionOptions); 630 | } catch (\Exception $e) { 631 | throw new MongoDBException($this->handleErrorMsg($e)); 632 | } 633 | } 634 | 635 | /** 636 | * 移除集合中所有的索引 637 | * 638 | * @Task(timeout=30) 639 | * @param array $options 640 | * @param array $collectionOptions 641 | * @return array|object 642 | * @throws MongoDBException 643 | */ 644 | public function dropIndexes(array $options = [], array $collectionOptions = []) 645 | { 646 | try { 647 | /** 648 | * @var $collection MongoDBConnection 649 | */ 650 | $collection = $this->getConnection(); 651 | return $collection->dropIndexes($this->collectionName, $options, $collectionOptions); 652 | } catch (\Exception $e) { 653 | throw new MongoDBException($this->handleErrorMsg($e)); 654 | } 655 | } 656 | 657 | /** 658 | * Executes a map-reduce aggregation on the collection. 659 | * 660 | * @Task(timeout=30) 661 | * @param JavascriptInterface $map 662 | * @param JavascriptInterface $reduce 663 | * @param $out 664 | * @param array $options 665 | * @param array $collectionOptions 666 | * @return array 667 | * @throws MongoDBException 668 | */ 669 | public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [], array $collectionOptions = []) 670 | { 671 | try { 672 | /** 673 | * @var $collection MongoDBConnection 674 | */ 675 | $collection = $this->getConnection(); 676 | $data = $collection->mapReduce($this->collectionName, $map, $reduce, $out, $options, $collectionOptions); 677 | return Arr::toArray($data); 678 | } catch (\Exception $e) { 679 | throw new MongoDBException($this->handleErrorMsg($e)); 680 | } 681 | } 682 | 683 | /** 684 | * Replaces at most one document matching the filter. 685 | * 686 | * @Task(timeout=30) 687 | * @param $filter 688 | * @param $replacement 689 | * @param array $options 690 | * @param array $collectionOptions 691 | * @return int|null 692 | * @throws MongoDBException 693 | */ 694 | public function replaceOne($filter, $replacement, array $options = [], array $collectionOptions = []) 695 | { 696 | try { 697 | /** 698 | * @var $collection MongoDBConnection 699 | */ 700 | $collection = $this->getConnection(); 701 | return $collection->replaceOne($this->collectionName, $filter, $replacement, $options, $collectionOptions); 702 | } catch (\Exception $e) { 703 | throw new MongoDBException($this->handleErrorMsg($e)); 704 | } 705 | } 706 | 707 | /** 708 | * Explains explainable commands 709 | * 710 | * @Task(timeout=30) 711 | * @param Explainable $explainable Command on which to run explain 712 | * @param array $options 713 | * @param array $collectionOptions 714 | * @return mixed 715 | * @throws MongoDBException 716 | */ 717 | public function explain(Explainable $explainable, array $options = [], array $collectionOptions = []) 718 | { 719 | try { 720 | /** 721 | * @var $collection MongoDBConnection 722 | */ 723 | $collection = $this->getConnection(); 724 | return current($collection->explain($this->collectionName, $explainable, $options, $collectionOptions)); 725 | } catch (\Exception $e) { 726 | throw new MongoDBException($this->handleErrorMsg($e)); 727 | } 728 | } 729 | 730 | /** 731 | * 创建集合 732 | * 733 | * @Task(timeout=30) 734 | * @param string $collectionName 735 | * @param array $options 736 | * @param array $databaseOptions 737 | * @return array|object 738 | * @throws MongoDBException 739 | */ 740 | public function createCollection(array $options = [], array $databaseOptions = []) 741 | { 742 | try { 743 | /** 744 | * @var $collection MongoDBConnection 745 | */ 746 | $collection = $this->getConnection(); 747 | return $collection->createCollection($this->collectionName, $options, $databaseOptions); 748 | } catch (\Exception $e) { 749 | throw new MongoDBException($this->handleErrorMsg($e)); 750 | } 751 | } 752 | 753 | /** 754 | * 移除集合 755 | * 756 | * @Task(timeout=30) 757 | * @param string $collectionName 758 | * @param array $options 759 | * @param array $databaseOptions 760 | * @return array|object 761 | * @throws MongoDBException 762 | */ 763 | public function dropCollection(array $options = [], array $databaseOptions = []) 764 | { 765 | try { 766 | /** 767 | * @var $collection MongoDBConnection 768 | */ 769 | $collection = $this->getConnection(); 770 | return $collection->dropCollection($this->collectionName, $options, $databaseOptions); 771 | } catch (\Exception $e) { 772 | throw new MongoDBException($this->handleErrorMsg($e)); 773 | } 774 | } 775 | 776 | /** 777 | * 修改集合 778 | * 779 | * @Task(timeout=30) 780 | * @param string $collectionName 781 | * @param array $collectionOptions 782 | * @param array $options 783 | * @param array $databaseOptions 784 | * @return array|object 785 | * @throws MongoDBException 786 | */ 787 | public function modifyCollection(array $collectionOptions = [], array $options = [], array $databaseOptions = []) 788 | { 789 | try { 790 | /** 791 | * @var $collection MongoDBConnection 792 | */ 793 | $collection = $this->getConnection(); 794 | return $collection->modifyCollection($this->collectionName, $collectionOptions, $options, $databaseOptions); 795 | } catch (\Exception $e) { 796 | throw new MongoDBException($this->handleErrorMsg($e)); 797 | } 798 | } 799 | 800 | /** 801 | * 显示所有的集合 TODO 802 | * 803 | * @Task(timeout=30) 804 | * @param array $options 805 | * @param array $databaseOptions 806 | * @return array|\MongoDB\Model\CollectionInfoIterator 807 | * @throws MongoDBException 808 | */ 809 | public function listCollections(array $options = [], array $databaseOptions = []) 810 | { 811 | try { 812 | /** 813 | * @var $collection MongoDBConnection 814 | */ 815 | $collection = $this->getConnection(); 816 | return $collection->listCollections($options, $databaseOptions); 817 | } catch (\Exception $e) { 818 | throw new MongoDBException($this->handleErrorMsg($e)); 819 | } 820 | } 821 | 822 | /** 823 | * 使用command的方式获取个数 824 | * 825 | * @param array $command 826 | * @return int 827 | * @throws MongoDBException 828 | * @throws \MongoDB\Driver\Exception\Exception 829 | */ 830 | public function countByCommand(array $command = []) 831 | { 832 | try { 833 | /** 834 | * @var $collection MongoDBConnection 835 | */ 836 | $collection = $this->getConnection(); 837 | return $collection->countByCommand($command); 838 | } catch (\Exception $e) { 839 | throw new MongoDBException($this->handleErrorMsg($e)); 840 | } 841 | } 842 | 843 | /** 844 | * 聚合查询 845 | * 846 | * @Task(timeout=30) 847 | * @param array $command 848 | * @return array 849 | * @throws MongoDBException 850 | * @throws \MongoDB\Driver\Exception\Exception 851 | */ 852 | public function command(array $command = []) 853 | { 854 | try { 855 | /** 856 | * @var $collection MongoDBConnection 857 | */ 858 | $collection = $this->getConnection(); 859 | return $collection->command($command); 860 | } catch (\Exception $e) { 861 | throw new MongoDBException($this->handleErrorMsg($e)); 862 | } 863 | } 864 | 865 | /** 866 | * @return mixed|null 867 | */ 868 | private function getConnection() 869 | { 870 | $connection = null; 871 | $hasContextConnection = Context::has($this->getContextKey()); 872 | if ($hasContextConnection) { 873 | $connection = Context::get($this->getContextKey()); 874 | } 875 | if (!$connection instanceof MongoDbConnection) { 876 | $pool = $this->factory->getPool($this->poolName); 877 | $connection = $pool->get()->getConnection(); 878 | } 879 | return $connection; 880 | } 881 | 882 | /** 883 | * The key to identify the connection object in coroutine context. 884 | */ 885 | private function getContextKey(): string 886 | { 887 | return sprintf('mongodb.connection.%s', $this->poolName); 888 | } 889 | 890 | /** 891 | * @param $e 892 | * @return string 893 | */ 894 | private function handleErrorMsg($e) 895 | { 896 | return $e->getFile() . $e->getLine() . $e->getMessage(); 897 | } 898 | 899 | public function __set($name, $value) 900 | { 901 | $this->$name = $value; 902 | return $this; 903 | } 904 | } 905 | -------------------------------------------------------------------------------- /src/MongoTask.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 63 | } 64 | 65 | /** 66 | * @param $collectionName 67 | */ 68 | public function setCollectionName($collectionName) 69 | { 70 | $this->collectionName = $collectionName; 71 | return $this; 72 | } 73 | 74 | /** 75 | * @param bool $bool 76 | */ 77 | public function setTimestamps(bool $bool) 78 | { 79 | $this->timestamps = $bool; 80 | return $this; 81 | } 82 | 83 | /** 84 | * 返回满足条件的第一个数据 85 | * 86 | * @Task(timeout=30) 87 | * @param $filter 88 | * @param array $options 89 | * @return array 90 | * @throws MongoDBException 91 | */ 92 | public function findOne($filter = [], array $options = [], array $collectionOptions = []): array 93 | { 94 | try { 95 | /** 96 | * @var $collection MongoDBConnection 97 | */ 98 | $collection = $this->getConnection(); 99 | return $collection->findOne($this->collectionName, $filter, $options, $collectionOptions); 100 | } catch (\Exception $e) { 101 | throw new MongoDBException($this->handleErrorMsg($e)); 102 | } 103 | } 104 | 105 | /** 106 | * 返回满足filer的全部数据 107 | * 108 | * @Task(timeout=30) 109 | * @param array $filter 110 | * @param array $options 111 | * @param array $collectionOptions 112 | * @return array 113 | * @throws MongoDBException 114 | */ 115 | public function findAll(array $filter = [], array $options = [], array $collectionOptions = []) 116 | { 117 | try { 118 | /** 119 | * @var $collection MongoDBConnection 120 | */ 121 | $collection = $this->getConnection(); 122 | return $collection->findAll($this->collectionName, $filter, $options, $collectionOptions); 123 | } catch (\Exception $e) { 124 | throw new MongoDBException($this->handleErrorMsg($e)); 125 | } 126 | } 127 | 128 | /** 129 | * 返回满足filer的分页数据 130 | * 131 | * @Task(timeout=30) 132 | * @param int $currentPage 133 | * @param int $limit 134 | * @param array $filter 135 | * @param array $options 136 | * @param array $collectionOptions 137 | * @return array 138 | * @throws MongoDBException 139 | */ 140 | public function findPagination(int $currentPage, int $limit, array $filter = [], array $options = [], array $collectionOptions = []) 141 | { 142 | try { 143 | /** 144 | * @var $collection MongoDBConnection 145 | */ 146 | $collection = $this->getConnection(); 147 | return $collection->findPagination($this->collectionName, $currentPage, $limit, $filter, $options, $collectionOptions); 148 | } catch (\Exception $e) { 149 | throw new MongoDBException($this->handleErrorMsg($e)); 150 | } 151 | } 152 | 153 | /** 154 | * 使用command的方式查询出数据 155 | * 156 | * @Task(timeout=30) 157 | * @param array $command 158 | * @return array 159 | * @throws MongoDBException 160 | * @throws \MongoDB\Driver\Exception\Exception 161 | */ 162 | public function findByCommand(array $command = []): array 163 | { 164 | try { 165 | /** 166 | * @var $collection MongoDBConnection 167 | */ 168 | $collection = $this->getConnection(); 169 | return $collection->queryByCommand($command); 170 | } catch (\Exception $e) { 171 | throw new MongoDBException($this->handleErrorMsg($e)); 172 | } 173 | } 174 | 175 | /** 176 | * 查找单个文档并删除它,返回原始文档 177 | * 178 | * @Task(timeout=30) 179 | * @param $filter 180 | * @param array $options 181 | * @param array $collectionOptions 182 | * @return array 183 | * @throws MongoDBException 184 | */ 185 | public function findOneAndDelete($filter, array $options = [], array $collectionOptions = []) 186 | { 187 | try { 188 | /** 189 | * @var $collection MongoDBConnection 190 | */ 191 | $collection = $this->getConnection(); 192 | return $collection->findOneAndDelete($this->collectionName, $filter, $options, $collectionOptions); 193 | } catch (\Exception $e) { 194 | throw new MongoDBException($this->handleErrorMsg($e)); 195 | } 196 | } 197 | 198 | /** 199 | * 查找单个文档并替换它,返回原始文档或替换文件 200 | * 201 | * @Task(timeout=30) 202 | * @param $filter 203 | * @param $replacement 204 | * @param array $options 205 | * @param array $collectionOptions 206 | * @return array|object|null 207 | * @throws MongoDBException 208 | */ 209 | public function findOneAndReplace($filter, $replacement, array $options = [], array $collectionOptions = []) 210 | { 211 | try { 212 | /** 213 | * @var $collection MongoDBConnection 214 | */ 215 | $collection = $this->getConnection(); 216 | return $collection->findOneAndReplace($this->collectionName, $filter, $replacement, $options, $collectionOptions); 217 | } catch (\Exception $e) { 218 | throw new MongoDBException($this->handleErrorMsg($e)); 219 | } 220 | } 221 | 222 | /** 223 | * 查找单个文档并更新它,返回原始文档或更新后的文件 224 | * 225 | * @Task(timeout=30) 226 | * @param $filter 227 | * @param $update 228 | * @param array $options 229 | * @param array $collectionOptions 230 | * @return array|object|null 231 | * @throws MongoDBException 232 | */ 233 | public function findOneAndUpdate($filter, $update, array $options = [], array $collectionOptions = []) 234 | { 235 | try { 236 | /** 237 | * @var $collection MongoDBConnection 238 | */ 239 | $collection = $this->getConnection(); 240 | return $collection->findOneAndUpdate($this->collectionName, $filter, $update, $options, $collectionOptions); 241 | } catch (\Exception $e) { 242 | throw new MongoDBException($this->handleErrorMsg($e)); 243 | } 244 | } 245 | 246 | /** 247 | * 插入多个数据 248 | * 249 | * @Task(timeout=30) 250 | * @param array $documents 251 | * @param array $options 252 | * @param array $collectionOptions 253 | * @return bool|mixed[] 254 | * @throws MongoDBException 255 | */ 256 | public function insertMany(array $documents = [], array $options = [], array $collectionOptions = []) 257 | { 258 | try { 259 | if ($this->timestamps) { 260 | $time = time(); 261 | foreach ($documents as &$document) { 262 | $document[self::CREATED_AT] = $time; 263 | $document[self::UPDATED_AT] = $time; 264 | } 265 | } 266 | /** 267 | * @var $collection MongoDBConnection 268 | */ 269 | $collection = $this->getConnection(); 270 | return $collection->insertMany($this->collectionName, $documents, $options, $collectionOptions); 271 | } catch (\Exception $e) { 272 | throw new MongoDBException($this->handleErrorMsg($e)); 273 | } 274 | } 275 | 276 | /** 277 | * 插入一个数据 278 | * 279 | * @Task(timeout=30) 280 | * @param array $document 281 | * @param array $options 282 | * @param array $collectionOptions 283 | * @return mixed 284 | * @throws MongoDBException 285 | */ 286 | public function insertOne($document = [], array $options = [], array $collectionOptions = []) 287 | { 288 | try { 289 | if ($this->timestamps) { 290 | $time = time(); 291 | $document[self::CREATED_AT] = $time; 292 | $document[self::UPDATED_AT] = $time; 293 | } 294 | /** 295 | * @var $collection MongoDBConnection 296 | */ 297 | $collection = $this->getConnection(); 298 | return $collection->insertOne($this->collectionName, $document, $options, $collectionOptions); 299 | } catch (\Exception $e) { 300 | throw new MongoDBException($this->handleErrorMsg($e)); 301 | } 302 | } 303 | 304 | /** 305 | * 更新匹配的到的所有数据 306 | * 307 | * @Task(timeout=30) 308 | * @param $filter 309 | * @param $update 310 | * @param array $options 311 | * @param array $collectionOptions 312 | * @return int|null 313 | * @throws MongoDBException 314 | */ 315 | public function updateMany($filter, $update, array $options = [], array $collectionOptions = []) 316 | { 317 | try { 318 | if ($this->timestamps) { 319 | $update['$set'][self::UPDATED_AT] = time(); 320 | } 321 | /** 322 | * @var $collection MongoDBConnection 323 | */ 324 | $collection = $this->getConnection(); 325 | return $collection->updateMany($this->collectionName, $filter, $update, $options, $collectionOptions); 326 | } catch (\Exception $e) { 327 | throw new MongoDBException($this->handleErrorMsg($e)); 328 | } 329 | } 330 | 331 | /** 332 | * 更新匹配的到的一条数据 333 | * 334 | * @Task(timeout=30) 335 | * @param $filter 336 | * @param $update 337 | * @param array $options 338 | * @param array $collectionOptions 339 | * @return int|null 340 | * @throws MongoDBException 341 | */ 342 | public function updateOne($filter, $update, array $options = [], array $collectionOptions = []) 343 | { 344 | try { 345 | if ($this->timestamps) { 346 | $update['$set'][self::UPDATED_AT] = time(); 347 | } 348 | /** 349 | * @var $collection MongoDBConnection 350 | */ 351 | $collection = $this->getConnection(); 352 | return $collection->updateOne($this->collectionName, $filter, $update, $options, $collectionOptions); 353 | } catch (\Exception $e) { 354 | throw new MongoDBException($this->handleErrorMsg($e)); 355 | } 356 | } 357 | 358 | /** 359 | * 更新数据满足$filter的行的信息成$newObject 360 | * 361 | * @Task(timeout=30) 362 | * @param array $filter 363 | * @param array $update 364 | * @param array $options 365 | * @return int|null 366 | * @throws MongoDBException 367 | */ 368 | public function updateRow(array $filter = [], array $update = [], array $options = ['multi' => false, 'upsert' => false]) 369 | { 370 | try { 371 | if ($this->timestamps) { 372 | $update['$set'][self::UPDATED_AT] = time(); 373 | } 374 | /** 375 | * @var $collection MongoDBConnection 376 | */ 377 | $collection = $this->getConnection(); 378 | return $collection->updateRow($this->collectionName, $filter, $update, $options); 379 | } catch (\Exception $e) { 380 | throw new MongoDBException($this->handleErrorMsg($e)); 381 | } 382 | } 383 | 384 | /** 385 | * 删除匹配到的多条数据 386 | * 387 | * @Task(timeout=30) 388 | * @param $filter 389 | * @param array $options 390 | * @param array $collectionOptions 391 | * @return int 392 | * @throws MongoDBException 393 | */ 394 | public function deleteMany($filter, array $options = [], array $collectionOptions = []) 395 | { 396 | try { 397 | /** 398 | * @var $collection MongoDBConnection 399 | */ 400 | $collection = $this->getConnection(); 401 | return $collection->deleteMany($this->collectionName, $filter, $options, $collectionOptions); 402 | } catch (\Exception $e) { 403 | throw new MongoDBException($this->handleErrorMsg($e)); 404 | } 405 | } 406 | 407 | /** 408 | * 删除一条匹配的数据 409 | * 410 | * @Task(timeout=30) 411 | * @param $filter 412 | * @param array $options 413 | * @param array $collectionOptions 414 | * @return int 415 | * @throws MongoDBException 416 | */ 417 | public function deleteOne($filter, array $options = [], array $collectionOptions = []) 418 | { 419 | try { 420 | /** 421 | * @var $collection MongoDBConnection 422 | */ 423 | $collection = $this->getConnection(); 424 | return $collection->deleteOne($this->collectionName, $filter, $options, $collectionOptions); 425 | } catch (\Exception $e) { 426 | throw new MongoDBException($this->handleErrorMsg($e)); 427 | } 428 | } 429 | 430 | /** 431 | * 通过多个_id删除数据 432 | * 433 | * @Task(timeout=30) 434 | * @param array $ids 435 | * @param array $options 436 | * @return int 437 | * @throws MongoDBException 438 | */ 439 | public function deleteByIds(array $ids = [], array $options = []) 440 | { 441 | try { 442 | /** 443 | * @var $collection MongoDBConnection 444 | */ 445 | $collection = $this->getConnection(); 446 | return $collection->deleteByIds($this->collectionName, $ids, $options); 447 | } catch (\Exception $e) { 448 | throw new MongoDBException($this->handleErrorMsg($e)); 449 | } 450 | } 451 | 452 | /** 453 | * 查找集合中指定字段的不同值 454 | * 455 | * @Task(timeout=30) 456 | * @param string $fieldName 457 | * @param array $filter 458 | * @param array $options 459 | * @param array $collectionOptions 460 | * @return array|mixed[] 461 | * @throws MongoDBException 462 | */ 463 | public function distinct(string $fieldName, $filter = [], array $options = [], array $collectionOptions = []) 464 | { 465 | try { 466 | /** 467 | * @var $collection MongoDBConnection 468 | */ 469 | $collection = $this->getConnection(); 470 | return $collection->distinct($this->collectionName, $fieldName, $filter, $options, $collectionOptions); 471 | } catch (\Exception $e) { 472 | throw new MongoDBException($this->handleErrorMsg($e)); 473 | } 474 | } 475 | 476 | /** 477 | * 聚合查询 478 | * 479 | * @Task(timeout=30) 480 | * @param array $pipeline 481 | * @param array $options 482 | * @param array $collectionOptions 483 | * @return \Traversable 484 | * @throws MongoDBException 485 | */ 486 | public function aggregate(array $pipeline = [], array $options = [], array $collectionOptions = []) 487 | { 488 | try { 489 | /** 490 | * @var $collection MongoDBConnection 491 | */ 492 | $collection = $this->getConnection(); 493 | return $collection->aggregate($this->collectionName, $pipeline, $options, $collectionOptions); 494 | } catch (\Exception $e) { 495 | throw new MongoDBException($this->handleErrorMsg($e)); 496 | } 497 | } 498 | 499 | /** 500 | * 获取查询满足的的数量 501 | * 502 | * @Task(timeout=30) 503 | * @param $filter 504 | * @param array $options 505 | * @param array $collectionOptions 506 | * @return int 507 | * @throws MongoDBException 508 | */ 509 | public function countDocuments($filter = [], array $options = [], array $collectionOptions = []) 510 | { 511 | try { 512 | /** 513 | * @var $collection MongoDBConnection 514 | */ 515 | $collection = $this->getConnection(); 516 | return $collection->countDocuments($this->collectionName, $filter, $options, $collectionOptions); 517 | } catch (\Exception $e) { 518 | throw new MongoDBException($this->handleErrorMsg($e)); 519 | } 520 | } 521 | 522 | /** 523 | * 使用集合元数据获取集合中文档的估计数量 524 | * 525 | * @Task(timeout=30) 526 | * @param array $options 527 | * @param array $collectionOptions 528 | * @return int 529 | * @throws MongoDBException 530 | */ 531 | public function estimatedDocumentCount(array $options = [], array $collectionOptions = []) 532 | { 533 | try { 534 | /** 535 | * @var $collection MongoDBConnection 536 | */ 537 | $collection = $this->getConnection(); 538 | return $collection->estimatedDocumentCount($this->collectionName, $options, $collectionOptions); 539 | } catch (\Exception $e) { 540 | throw new MongoDBException($this->handleErrorMsg($e)); 541 | } 542 | } 543 | 544 | /** 545 | * 创建索引 546 | * 547 | * @Task(timeout=30) 548 | * @param $key 549 | * @param array $options 550 | * @param array $collectionOptions 551 | * @return mixed 552 | * @throws MongoDBException 553 | */ 554 | public function createIndex($key, array $options = [], array $collectionOptions = []) 555 | { 556 | try { 557 | /** 558 | * @var $collection MongoDBConnection 559 | */ 560 | $collection = $this->getConnection(); 561 | return $collection->createIndex($this->collectionName, $key, $options, $collectionOptions); 562 | } catch (\Exception $e) { 563 | throw new MongoDBException($this->handleErrorMsg($e)); 564 | } 565 | } 566 | 567 | /** 568 | * 批量创建索引 569 | * 570 | * @Task(timeout=30) 571 | * @param array $indexes 572 | * @param array $options 573 | * @param array $collectionOptions 574 | * @return string[] 575 | * @throws MongoDBException 576 | */ 577 | public function createIndexes(array $indexes, array $options = [], array $collectionOptions = []) 578 | { 579 | try { 580 | /** 581 | * @var $collection MongoDBConnection 582 | */ 583 | $collection = $this->getConnection(); 584 | return $collection->createIndexes($this->collectionName, $indexes, $options, $collectionOptions); 585 | } catch (\Exception $e) { 586 | throw new MongoDBException($this->handleErrorMsg($e)); 587 | } 588 | } 589 | 590 | /** 591 | * 获取所有索引信息 592 | * 593 | * @Task(timeout=30) 594 | * @param array $options 595 | * @param array $collectionOptions 596 | * @return \MongoDB\Model\IndexInfoIterator 597 | * @throws MongoDBException 598 | */ 599 | public function listIndexes(array $options = [], array $collectionOptions = []) 600 | { 601 | try { 602 | /** 603 | * @var $collection MongoDBConnection 604 | */ 605 | $collection = $this->getConnection(); 606 | return $collection->listIndexes($this->collectionName, $options, $collectionOptions); 607 | } catch (\Exception $e) { 608 | throw new MongoDBException($this->handleErrorMsg($e)); 609 | } 610 | } 611 | 612 | /** 613 | * 移除索引 614 | * 615 | * @Task(timeout=30) 616 | * @param $indexName 617 | * @param array $options 618 | * @param array $collectionOptions 619 | * @return array|object 620 | * @throws MongoDBException 621 | */ 622 | public function dropIndex($indexName, array $options = [], array $collectionOptions = []) 623 | { 624 | try { 625 | /** 626 | * @var $collection MongoDBConnection 627 | */ 628 | $collection = $this->getConnection(); 629 | return $collection->dropIndex($this->collectionName, $indexName, $options, $collectionOptions); 630 | } catch (\Exception $e) { 631 | throw new MongoDBException($this->handleErrorMsg($e)); 632 | } 633 | } 634 | 635 | /** 636 | * 移除集合中所有的索引 637 | * 638 | * @Task(timeout=30) 639 | * @param array $options 640 | * @param array $collectionOptions 641 | * @return array|object 642 | * @throws MongoDBException 643 | */ 644 | public function dropIndexes(array $options = [], array $collectionOptions = []) 645 | { 646 | try { 647 | /** 648 | * @var $collection MongoDBConnection 649 | */ 650 | $collection = $this->getConnection(); 651 | return $collection->dropIndexes($this->collectionName, $options, $collectionOptions); 652 | } catch (\Exception $e) { 653 | throw new MongoDBException($this->handleErrorMsg($e)); 654 | } 655 | } 656 | 657 | /** 658 | * Executes a map-reduce aggregation on the collection. 659 | * 660 | * @Task(timeout=30) 661 | * @param JavascriptInterface $map 662 | * @param JavascriptInterface $reduce 663 | * @param $out 664 | * @param array $options 665 | * @param array $collectionOptions 666 | * @return array 667 | * @throws MongoDBException 668 | */ 669 | public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [], array $collectionOptions = []) 670 | { 671 | try { 672 | /** 673 | * @var $collection MongoDBConnection 674 | */ 675 | $collection = $this->getConnection(); 676 | $data = $collection->mapReduce($this->collectionName, $map, $reduce, $out, $options, $collectionOptions); 677 | return Arr::toArray($data); 678 | } catch (\Exception $e) { 679 | throw new MongoDBException($this->handleErrorMsg($e)); 680 | } 681 | } 682 | 683 | /** 684 | * Replaces at most one document matching the filter. 685 | * 686 | * @Task(timeout=30) 687 | * @param $filter 688 | * @param $replacement 689 | * @param array $options 690 | * @param array $collectionOptions 691 | * @return int|null 692 | * @throws MongoDBException 693 | */ 694 | public function replaceOne($filter, $replacement, array $options = [], array $collectionOptions = []) 695 | { 696 | try { 697 | /** 698 | * @var $collection MongoDBConnection 699 | */ 700 | $collection = $this->getConnection(); 701 | return $collection->replaceOne($this->collectionName, $filter, $replacement, $options, $collectionOptions); 702 | } catch (\Exception $e) { 703 | throw new MongoDBException($this->handleErrorMsg($e)); 704 | } 705 | } 706 | 707 | /** 708 | * Explains explainable commands 709 | * 710 | * @Task(timeout=30) 711 | * @param Explainable $explainable Command on which to run explain 712 | * @param array $options 713 | * @param array $collectionOptions 714 | * @return mixed 715 | * @throws MongoDBException 716 | */ 717 | public function explain(Explainable $explainable, array $options = [], array $collectionOptions = []) 718 | { 719 | try { 720 | /** 721 | * @var $collection MongoDBConnection 722 | */ 723 | $collection = $this->getConnection(); 724 | return current($collection->explain($this->collectionName, $explainable, $options, $collectionOptions)); 725 | } catch (\Exception $e) { 726 | throw new MongoDBException($this->handleErrorMsg($e)); 727 | } 728 | } 729 | 730 | /** 731 | * 创建集合 732 | * 733 | * @Task(timeout=30) 734 | * @param string $collectionName 735 | * @param array $options 736 | * @param array $databaseOptions 737 | * @return array|object 738 | * @throws MongoDBException 739 | */ 740 | public function createCollection(array $options = [], array $databaseOptions = []) 741 | { 742 | try { 743 | /** 744 | * @var $collection MongoDBConnection 745 | */ 746 | $collection = $this->getConnection(); 747 | return $collection->createCollection($this->collectionName, $options, $databaseOptions); 748 | } catch (\Exception $e) { 749 | throw new MongoDBException($this->handleErrorMsg($e)); 750 | } 751 | } 752 | 753 | /** 754 | * 移除集合 755 | * 756 | * @Task(timeout=30) 757 | * @param string $collectionName 758 | * @param array $options 759 | * @param array $databaseOptions 760 | * @return array|object 761 | * @throws MongoDBException 762 | */ 763 | public function dropCollection(array $options = [], array $databaseOptions = []) 764 | { 765 | try { 766 | /** 767 | * @var $collection MongoDBConnection 768 | */ 769 | $collection = $this->getConnection(); 770 | return $collection->dropCollection($this->collectionName, $options, $databaseOptions); 771 | } catch (\Exception $e) { 772 | throw new MongoDBException($this->handleErrorMsg($e)); 773 | } 774 | } 775 | 776 | /** 777 | * 修改集合 778 | * 779 | * @Task(timeout=30) 780 | * @param string $collectionName 781 | * @param array $collectionOptions 782 | * @param array $options 783 | * @param array $databaseOptions 784 | * @return array|object 785 | * @throws MongoDBException 786 | */ 787 | public function modifyCollection(array $collectionOptions = [], array $options = [], array $databaseOptions = []) 788 | { 789 | try { 790 | /** 791 | * @var $collection MongoDBConnection 792 | */ 793 | $collection = $this->getConnection(); 794 | return $collection->modifyCollection($this->collectionName, $collectionOptions, $options, $databaseOptions); 795 | } catch (\Exception $e) { 796 | throw new MongoDBException($this->handleErrorMsg($e)); 797 | } 798 | } 799 | 800 | /** 801 | * 显示所有的集合 TODO 802 | * 803 | * @Task(timeout=30) 804 | * @param array $options 805 | * @param array $databaseOptions 806 | * @return array|\MongoDB\Model\CollectionInfoIterator 807 | * @throws MongoDBException 808 | */ 809 | public function listCollections(array $options = [], array $databaseOptions = []) 810 | { 811 | try { 812 | /** 813 | * @var $collection MongoDBConnection 814 | */ 815 | $collection = $this->getConnection(); 816 | return $collection->listCollections($options, $databaseOptions); 817 | } catch (\Exception $e) { 818 | throw new MongoDBException($this->handleErrorMsg($e)); 819 | } 820 | } 821 | 822 | /** 823 | * 使用command的方式获取个数 824 | * 825 | * @param array $command 826 | * @return int 827 | * @throws MongoDBException 828 | * @throws \MongoDB\Driver\Exception\Exception 829 | */ 830 | public function countByCommand(array $command = []) 831 | { 832 | try { 833 | /** 834 | * @var $collection MongoDBConnection 835 | */ 836 | $collection = $this->getConnection(); 837 | return $collection->countByCommand($command); 838 | } catch (\Exception $e) { 839 | throw new MongoDBException($this->handleErrorMsg($e)); 840 | } 841 | } 842 | 843 | /** 844 | * 聚合查询 845 | * 846 | * @Task(timeout=30) 847 | * @param array $command 848 | * @return array 849 | * @throws MongoDBException 850 | * @throws \MongoDB\Driver\Exception\Exception 851 | */ 852 | public function command(array $command = []) 853 | { 854 | try { 855 | /** 856 | * @var $collection MongoDBConnection 857 | */ 858 | $collection = $this->getConnection(); 859 | return $collection->command($command); 860 | } catch (\Exception $e) { 861 | throw new MongoDBException($this->handleErrorMsg($e)); 862 | } 863 | } 864 | 865 | /** 866 | * @return mixed|null 867 | */ 868 | private function getConnection() 869 | { 870 | $connection = null; 871 | $hasContextConnection = Context::has($this->getContextKey()); 872 | if ($hasContextConnection) { 873 | $connection = Context::get($this->getContextKey()); 874 | } 875 | if (!$connection instanceof MongoDbConnection) { 876 | $pool = $this->factory->getPool($this->poolName); 877 | $connection = $pool->get()->getConnection(); 878 | } 879 | return $connection; 880 | } 881 | 882 | /** 883 | * The key to identify the connection object in coroutine context. 884 | */ 885 | private function getContextKey(): string 886 | { 887 | return sprintf('mongodb.connection.%s', $this->poolName); 888 | } 889 | 890 | /** 891 | * @param $e 892 | * @return string 893 | */ 894 | private function handleErrorMsg($e) 895 | { 896 | return $e->getFile() . $e->getLine() . $e->getMessage(); 897 | } 898 | 899 | public function __set($name, $value) 900 | { 901 | $this->$name = $value; 902 | return $this; 903 | } 904 | } 905 | -------------------------------------------------------------------------------- /src/MongoDbConnection.php: -------------------------------------------------------------------------------- 1 | config = $config; 53 | $this->reconnect(); 54 | } 55 | 56 | /** 57 | * @return array 58 | */ 59 | public function getConfig() 60 | { 61 | return $this->config; 62 | } 63 | 64 | /** 65 | * @param string $namespace 66 | * @param array $collectionOptions 67 | * @return Collection 68 | */ 69 | protected function collection(string $namespace, array $collectionOptions = []) 70 | { 71 | $collection = make(Collection::class, [$this->mongoClient->getManager(), $this->config['db'], $namespace, $collectionOptions]); 72 | return $collection; 73 | } 74 | 75 | /** 76 | * @param array $options 77 | * @return Database 78 | */ 79 | protected function database(array $options = []) 80 | { 81 | $database = make(Database::class, [$this->mongoClient->getManager(), $this->config['db'], $options]); 82 | return $database; 83 | } 84 | 85 | /** 86 | * @return $this 87 | * @throws ConnectionException 88 | * @throws Exception 89 | * @throws MongoDBException 90 | */ 91 | public function getActiveConnection() 92 | { 93 | // TODO: Implement getActiveConnection() method. 94 | if ($this->check()) { 95 | return $this; 96 | } 97 | if (!$this->reconnect()) { 98 | throw new ConnectionException('Connection reconnect failed.'); 99 | } 100 | return $this; 101 | } 102 | 103 | /** 104 | * Reconnect the connection. 105 | * @return bool 106 | * @throws MongoDBException 107 | */ 108 | public function reconnect(): bool 109 | { 110 | // TODO: Implement reconnect() method. 111 | try { 112 | /** 113 | * http://php.net/manual/zh/mongodb-driver-manager.construct.php 114 | */ 115 | $username = $this->config['username'] ?? ''; 116 | $password = $this->config['password'] ?? ''; 117 | $authMechanism = $this->config['authMechanism'] ?? ''; 118 | $replicaSet = $this->config['replicaSet'] ?? ''; 119 | $driverOptions = $this->config['driver_options'] ?? []; 120 | $uriOptions = $this->config['uri_options'] ?? []; 121 | if (empty($uriOptions['username'])) { 122 | $uriOptions['username'] = $username; 123 | } 124 | if (empty($uriOptions['password'])) { 125 | $uriOptions['password'] = $password; 126 | } 127 | if (empty($uriOptions['replicaSet']) && !empty($replicaSet)) { 128 | $uriOptions['replicaSet'] = $replicaSet; 129 | } 130 | if (empty($uriOptions['authMechanism']) && !empty($authMechanism)) { 131 | $uriOptions['authMechanism'] = $authMechanism; 132 | } 133 | $uri = sprintf( 134 | 'mongodb://%s:%d/%s', 135 | $this->config['host'], 136 | $this->config['port'], 137 | $this->config['db'] 138 | ); 139 | if (!empty($this->config['url'])) { 140 | $uri = $this->config['url']; 141 | $uriOptions = []; 142 | } 143 | $this->mongoClient = new Client($uri, $uriOptions, $driverOptions); 144 | } catch (InvalidArgumentException $e) { 145 | throw MongoDBException::managerError('mongodb 连接参数错误:' . $e->getMessage()); 146 | } catch (RuntimeException $e) { 147 | throw MongoDBException::managerError('mongodb uri格式错误:' . $e->getMessage()); 148 | } 149 | $this->lastUseTime = microtime(true); 150 | return true; 151 | } 152 | 153 | /** 154 | * @return Client 155 | */ 156 | public function getMongoClient() 157 | { 158 | return $this->mongoClient; 159 | } 160 | 161 | /** 162 | * Close the mongoClient. 163 | */ 164 | public function close(): bool 165 | { 166 | // TODO: Implement close() method. 167 | return true; 168 | } 169 | 170 | /** 171 | * 返回满足条件的第一个数据 172 | * 173 | * @param string $namespace 174 | * @param array|object $filter Query by which to filter documents 175 | * @param array $options 176 | * @param array $collectionOptions 177 | * @return array 178 | * @throws MongoDBException 179 | */ 180 | public function findOne(string $namespace, $filter = [], array $options = [], array $collectionOptions = []) 181 | { 182 | try { 183 | $isException = false; 184 | $this->handleFilter($filter); 185 | $options = ['limit' => 1] + $options; 186 | if (empty($options['typeMap'])) { 187 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 188 | } 189 | $cursor = $this->collection($namespace, $collectionOptions)->find($filter, $options); 190 | $result = []; 191 | 192 | foreach ($cursor as $document) { 193 | if (!empty($document['_id'])) { 194 | $document['_id'] = (string)$document['_id']; 195 | } 196 | $document = (array)$document; 197 | $result = $document; 198 | } 199 | } catch (\Exception $e) { 200 | $isException = true; 201 | } finally { 202 | $this->pool->release($this); 203 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 204 | return $result; 205 | } 206 | } 207 | 208 | /** 209 | * 返回满足filer的全部数据 210 | * 211 | * @param string $namespace 212 | * @param array $filter 213 | * @param array $options 214 | * @param array $collectionOptions 215 | * @return array 216 | * @throws MongoDBException 217 | */ 218 | public function findAll(string $namespace, array $filter = [], array $options = [], array $collectionOptions = []) 219 | { 220 | try { 221 | $isException = false; 222 | $this->handleFilter($filter); 223 | $result = []; 224 | if (empty($options['typeMap'])) { 225 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 226 | } 227 | $cursor = $this->collection($namespace, $collectionOptions)->find($filter, $options); 228 | foreach ($cursor as $document) { 229 | if (!empty($document['_id'])) { 230 | $document['_id'] = (string)$document['_id']; 231 | } 232 | $document = (array)$document; 233 | $result[] = $document; 234 | } 235 | } catch (\Exception $e) { 236 | $isException = true; 237 | } finally { 238 | $this->pool->release($this); 239 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 240 | return $result; 241 | } 242 | } 243 | 244 | /** 245 | * 返回分页数据,默认每页10条 246 | * 247 | * @param string $namespace 248 | * @param int $limit 249 | * @param int $currentPage 250 | * @param array $filter 251 | * @param array $options 252 | * @return array 253 | * @throws MongoDBException 254 | */ 255 | public function findPagination(string $namespace, int $currentPage = 0, int $limit = 10, array $filter = [], array $options = [], array $collectionOptions = []) 256 | { 257 | try { 258 | $isException = false; 259 | $this->handleFilter($filter); 260 | // 查询数据 261 | $data = []; 262 | $result = []; 263 | 264 | //每次最多返回10条记录 265 | if (!isset($options['limit']) || (int)$options['limit'] <= 0) { 266 | $options['limit'] = $limit; 267 | } 268 | 269 | if (!isset($options['skip']) || (int)$options['skip'] <= 0) { 270 | if ($currentPage <= 1) { 271 | $options['skip'] = 0; 272 | } else { 273 | $options['skip'] = ($currentPage - 1) * $limit; 274 | } 275 | } 276 | if (empty($options['typeMap'])) { 277 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 278 | } 279 | $collection = $this->collection($namespace, $collectionOptions); 280 | $cursor = $collection->find($filter, $options); 281 | foreach ($cursor as $document) { 282 | if (!empty($document['_id'])) { 283 | $document['_id'] = (string)$document['_id']; 284 | } 285 | $document = (array)$document; 286 | $data[] = $document; 287 | } 288 | $result['total'] = $collection->countDocuments($filter); 289 | $result['page_no'] = $currentPage; 290 | $result['page_size'] = $limit; 291 | $result['rows'] = $data; 292 | 293 | } catch (\Exception $e) { 294 | $isException = true; 295 | } finally { 296 | $this->pool->release($this); 297 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 298 | return $result; 299 | } 300 | } 301 | 302 | 303 | /** 304 | * 查找单个文档并删除它,返回原始文档 305 | * 306 | * @param string $namespace 307 | * @param $filter 308 | * @param array $options 309 | * @param array $collectionOptions 310 | * @return array|object|null|bool 311 | * @throws MongoDBException 312 | */ 313 | public function findOneAndDelete(string $namespace, $filter, array $options = [], array $collectionOptions = []) 314 | { 315 | try { 316 | $isException = false; 317 | $this->handleFilter($filter); 318 | if (empty($options['typeMap'])) { 319 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 320 | } 321 | $result = $this->collection($namespace, $collectionOptions)->findOneAndDelete($filter, $options); 322 | } catch (\Exception $e) { 323 | $isException = true; 324 | } finally { 325 | $this->pool->release($this); 326 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 327 | return $result; 328 | } 329 | } 330 | 331 | /** 332 | * 查找单个文档并替换它,返回原始文档或替换文件 333 | * 334 | * @param string $namespace 335 | * @param $filter 336 | * @param $replacement 337 | * @param array $options 338 | * @param array $collectionOptions 339 | * @return array|object|null|bool 340 | * @throws MongoDBException 341 | */ 342 | public function findOneAndReplace(string $namespace, $filter, $replacement, array $options = [], array $collectionOptions = []) 343 | { 344 | try { 345 | $isException = false; 346 | $this->handleFilter($filter); 347 | if (empty($options['typeMap'])) { 348 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 349 | } 350 | $result = $this->collection($namespace, $collectionOptions)->findOneAndReplace($filter, $replacement, $options); 351 | } catch (\Exception $e) { 352 | $isException = true; 353 | } finally { 354 | $this->pool->release($this); 355 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 356 | return $result; 357 | } 358 | } 359 | 360 | /** 361 | * 查找单个文档并更新它,返回原始文档或更新后的文件 362 | * 363 | * @param string $namespace 364 | * @param $filter 365 | * @param $update 366 | * @param array $options 367 | * @param array $collectionOptions 368 | * @return array|object|null|bool 369 | * @throws MongoDBException 370 | */ 371 | public function findOneAndUpdate(string $namespace, $filter, $update, array $options = [], array $collectionOptions = []) 372 | { 373 | try { 374 | $isException = false; 375 | $this->handleFilter($filter); 376 | if (empty($options['typeMap'])) { 377 | $options['typeMap'] = ['root' => 'array', 'document' => 'array', 'array' => 'array']; 378 | } 379 | $result = $this->collection($namespace, $collectionOptions)->findOneAndUpdate($filter, $update, $options); 380 | } catch (\Exception $e) { 381 | $isException = true; 382 | } finally { 383 | $this->pool->release($this); 384 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 385 | return $result; 386 | } 387 | } 388 | 389 | /** 390 | * 插入多个数据 391 | * 392 | * @param string $namespace 393 | * @param array $documents 394 | * @param array $options 395 | * @param array $collectionOptions 396 | * @return mixed[] 397 | * @throws MongoDBException 398 | */ 399 | public function insertMany(string $namespace, array $documents = [], array $options = [], array $collectionOptions = []) 400 | { 401 | try { 402 | $isException = false; 403 | $result = $this->collection($namespace, $collectionOptions)->insertMany($documents, $options)->getInsertedIds(); 404 | foreach ($result as $k => $v) { 405 | $result[$k] = (string)$v; 406 | } 407 | } catch (\Exception $e) { 408 | $isException = true; 409 | } finally { 410 | $this->pool->release($this); 411 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 412 | return $result; 413 | } 414 | } 415 | 416 | /** 417 | * 插入一个数据 418 | * 419 | * @param string $namespace 420 | * @param array $documents 421 | * @param array $options 422 | * @param array $collectionOptions 423 | * @return mixed 424 | * @throws MongoDBException 425 | */ 426 | public function insertOne(string $namespace, $document = [], array $options = [], array $collectionOptions = []) 427 | { 428 | try { 429 | $isException = false; 430 | $result = (string)$this->collection($namespace, $collectionOptions)->insertOne($document, $options)->getInsertedId(); 431 | } catch (\Exception $e) { 432 | $isException = true; 433 | } finally { 434 | $this->pool->release($this); 435 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 436 | return $result; 437 | } 438 | } 439 | 440 | /** 441 | * 更新匹配的到的所有数据 442 | * 443 | * @param string $namespace 444 | * @param $filter 445 | * @param $update 446 | * @param array $options 447 | * @param array $collectionOptions 448 | * @return int|null 449 | * @throws MongoDBException 450 | */ 451 | public function updateMany(string $namespace, $filter, $update, array $options = [], array $collectionOptions = []) 452 | { 453 | try { 454 | $isException = false; 455 | $this->handleFilter($filter); 456 | $result = $this->collection($namespace, $collectionOptions)->updateMany($filter, $update, $options)->getModifiedCount(); 457 | } catch (\Exception $e) { 458 | $isException = true; 459 | } finally { 460 | $this->pool->release($this); 461 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 462 | return $result; 463 | } 464 | } 465 | 466 | /** 467 | * 更新匹配的到的一条数据 468 | * 469 | * @param string $namespace 470 | * @param $filter 471 | * @param $update 472 | * @param array $options 473 | * @param array $collectionOptions 474 | * @return int|null 475 | * @throws MongoDBException 476 | */ 477 | public function updateOne(string $namespace, $filter, $update, array $options = [], array $collectionOptions = []) 478 | { 479 | try { 480 | $isException = false; 481 | $this->handleFilter($filter); 482 | $result = $this->collection($namespace, $collectionOptions)->updateOne($filter, $update, $options)->getModifiedCount(); 483 | } catch (\Exception $e) { 484 | $isException = true; 485 | } finally { 486 | $this->pool->release($this); 487 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 488 | return $result; 489 | } 490 | } 491 | 492 | /** 493 | * 数据更新,效果是满足filter的行,只更新$newObj中的$set出现的字段 494 | * http://php.net/manual/zh/mongodb-driver-bulkwrite.update.php 495 | * $bulk->update( 496 | * ['x' => 2], 497 | * ['$set' => ['y' => 3]], 498 | * ['multi' => false, 'upsert' => false] 499 | * ); 500 | * 501 | * @param string $namespace 502 | * @param array $filter 503 | * @param array $update 504 | * @param array $options 505 | * ---multi 是否开启批量更新,upsert 不存在是否插入 506 | * @return int|null 507 | * @throws MongoDBException 508 | */ 509 | public function updateRow(string $namespace, array $filter = [], array $update = [], array $options = [], array $collectionOptions = []) 510 | { 511 | try { 512 | $isException = false; 513 | $this->handleFilter($filter); 514 | $result = $this->collection($namespace, $collectionOptions)->updateMany($filter, $update, $options)->getModifiedCount(); 515 | } catch (\Exception $e) { 516 | $isException = true; 517 | } finally { 518 | $this->pool->release($this); 519 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 520 | return $result; 521 | } 522 | } 523 | 524 | /** 525 | * 删除匹配到的多条数据 526 | * 527 | * @param string $namespace 528 | * @param $filter 529 | * @param array $options 530 | * @param array $collectionOptions 531 | * @return int 532 | * @throws MongoDBException 533 | */ 534 | public function deleteMany(string $namespace, $filter, array $options = [], array $collectionOptions = []) 535 | { 536 | try { 537 | $isException = false; 538 | $this->handleFilter($filter); 539 | $result = $this->collection($namespace, $collectionOptions)->deleteMany($filter, $options)->getDeletedCount(); 540 | } catch (\Exception $e) { 541 | $isException = true; 542 | } finally { 543 | $this->pool->release($this); 544 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 545 | return $result; 546 | } 547 | } 548 | 549 | /** 550 | * 删除一条匹配的数据 551 | * 552 | * @param string $namespace 553 | * @param $filter 554 | * @param array $options 555 | * @param array $collectionOptions 556 | * @return int 557 | * @throws MongoDBException 558 | */ 559 | public function deleteOne(string $namespace, $filter, array $options = [], array $collectionOptions = []) 560 | { 561 | try { 562 | $isException = false; 563 | $this->handleFilter($filter); 564 | $result = $this->collection($namespace, $collectionOptions)->deleteOne($filter, $options)->getDeletedCount(); 565 | } catch (\Exception $e) { 566 | $isException = true; 567 | } finally { 568 | $this->pool->release($this); 569 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 570 | return $result; 571 | } 572 | } 573 | 574 | /** 575 | * 通过ids删除 576 | * 577 | * @param string $namespace 578 | * @param array $ids 579 | * @param array $options 580 | * @return int 581 | * @throws MongoDBException 582 | */ 583 | public function deleteByIds(string $namespace, array $ids, array $options = [], array $collectionOptions = []) 584 | { 585 | try { 586 | $isException = false; 587 | $filter = []; 588 | foreach ($ids as $k => $id) { 589 | $ids[$k] = new ObjectId($id); 590 | } 591 | $filter['_id']['$in'] = $ids; 592 | $result = $this->collection($namespace, $collectionOptions)->deleteMany($filter, $options)->getDeletedCount(); 593 | } catch (\Exception $e) { 594 | $isException = true; 595 | } finally { 596 | $this->pool->release($this); 597 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 598 | return $result; 599 | } 600 | } 601 | 602 | /** 603 | * 查找集合中指定字段的不同值 604 | * 605 | * @param string $namespace 606 | * @param string $fieldName 607 | * @param array $filter 608 | * @param array $options 609 | * @param array $collectionOptions 610 | * @return array|mixed[] 611 | * @throws MongoDBException 612 | */ 613 | public function distinct(string $namespace, string $fieldName, $filter = [], array $options = [], array $collectionOptions = []) 614 | { 615 | try { 616 | $isException = false; 617 | $result = $this->collection($namespace, $collectionOptions)->distinct($fieldName, $filter, $options); 618 | } catch (\Exception $e) { 619 | $isException = true; 620 | } finally { 621 | $this->pool->release($this); 622 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 623 | return $result; 624 | } 625 | } 626 | 627 | /** 628 | * 聚合查询 629 | * 630 | * @param string $namespace 631 | * @param array $pipeline 632 | * @param array $options 633 | * @param array $collectionOptions 634 | * @return \Traversable 635 | * @throws MongoDBException 636 | */ 637 | public function aggregate(string $namespace, array $pipeline = [], array $options = [], array $collectionOptions = []) 638 | { 639 | try { 640 | $isException = false; 641 | $result = $this->collection($namespace, $collectionOptions)->aggregate($pipeline, $options); 642 | } catch (\Exception $e) { 643 | $isException = true; 644 | } finally { 645 | $this->pool->release($this); 646 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 647 | return $result; 648 | } 649 | } 650 | 651 | /** 652 | * 获取查询满足的的数量 653 | * 654 | * @param string $namespace 655 | * @param $filter 656 | * @param array $options 657 | * @param array $collectionOptions 658 | * @return int 659 | * @throws MongoDBException 660 | */ 661 | public function countDocuments(string $namespace, $filter = [], array $options = [], array $collectionOptions = []) 662 | { 663 | try { 664 | $isException = false; 665 | $result = $this->collection($namespace, $collectionOptions)->countDocuments($filter, $options); 666 | } catch (\Exception $e) { 667 | $isException = true; 668 | } finally { 669 | $this->pool->release($this); 670 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 671 | return $result; 672 | } 673 | } 674 | 675 | /** 676 | * 使用集合元数据获取集合中文档的估计数量 677 | * 678 | * @param string $namespace 679 | * @param array $options 680 | * @param array $collectionOptions 681 | * @return int 682 | * @throws MongoDBException 683 | */ 684 | public function estimatedDocumentCount(string $namespace, array $options = [], array $collectionOptions = []) 685 | { 686 | try { 687 | $isException = false; 688 | $result = $this->collection($namespace, $collectionOptions)->estimatedDocumentCount($options); 689 | } catch (\Exception $e) { 690 | $isException = true; 691 | } finally { 692 | $this->pool->release($this); 693 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 694 | return $result; 695 | } 696 | } 697 | 698 | /** 699 | * 创建索引 700 | * 701 | * @param string $namespace 702 | * @param $key 703 | * @param array $options 704 | * @param array $collectionOptions 705 | * @return array|mixed 706 | * @throws MongoDBException 707 | */ 708 | public function createIndex(string $namespace, $key, array $options = [], array $collectionOptions = []) 709 | { 710 | try { 711 | $isException = false; 712 | $result = $this->collection($namespace, $collectionOptions)->createIndex($key, $options); 713 | } catch (\Exception $e) { 714 | $isException = true; 715 | } finally { 716 | $this->pool->release($this); 717 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 718 | return $result; 719 | } 720 | } 721 | 722 | /** 723 | * 批量创建索引 724 | * 725 | * @param string $namespace 726 | * @param array $indexes 727 | * @param array $options 728 | * @param array $collectionOptions 729 | * @return string[] 730 | * @throws MongoDBException 731 | */ 732 | public function createIndexes(string $namespace, array $indexes, array $options = [], array $collectionOptions = []) 733 | { 734 | try { 735 | $isException = false; 736 | $result = $this->collection($namespace, $collectionOptions)->createIndexes($indexes, $options); 737 | } catch (\Exception $e) { 738 | $isException = true; 739 | } finally { 740 | $this->pool->release($this); 741 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 742 | return $result; 743 | } 744 | } 745 | 746 | /** 747 | * 获取所有索引信息 748 | * 749 | * @param string $namespace 750 | * @param array $options 751 | * @param array $collectionOptions 752 | * @return \MongoDB\Model\IndexInfoIterator 753 | * @throws MongoDBException 754 | */ 755 | public function listIndexes(string $namespace, array $options = [], array $collectionOptions = []) 756 | { 757 | try { 758 | $isException = false; 759 | $result = $this->collection($namespace, $collectionOptions)->listIndexes($options); 760 | } catch (\Exception $e) { 761 | $isException = true; 762 | } finally { 763 | $this->pool->release($this); 764 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 765 | return $result; 766 | } 767 | } 768 | 769 | /** 770 | * 移除索引 771 | * 772 | * @param string $namespace 773 | * @param $indexName 774 | * @param array $options 775 | * @param array $collectionOptions 776 | * @return array|object 777 | * @throws MongoDBException 778 | */ 779 | public function dropIndex(string $namespace, $indexName, array $options = [], array $collectionOptions = []) 780 | { 781 | try { 782 | $isException = false; 783 | $result = $this->collection($namespace, $collectionOptions)->dropIndex($indexName, $options); 784 | } catch (\Exception $e) { 785 | $isException = true; 786 | } finally { 787 | $this->pool->release($this); 788 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 789 | return $result; 790 | } 791 | } 792 | 793 | /** 794 | * 移除集合中所有的索引 795 | * 796 | * @param string $namespace 797 | * @param array $options 798 | * @param array $collectionOptions 799 | * @return array|object 800 | * @throws MongoDBException 801 | */ 802 | public function dropIndexes(string $namespace, array $options = [], array $collectionOptions = []) 803 | { 804 | try { 805 | $isException = false; 806 | $result = $this->collection($namespace, $collectionOptions)->dropIndexes($options); 807 | } catch (\Exception $e) { 808 | $isException = true; 809 | } finally { 810 | $this->pool->release($this); 811 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 812 | return $result; 813 | } 814 | } 815 | 816 | /** 817 | * 创建collection 818 | * 819 | * @param string $collectionName 820 | * @param array $options 821 | * @param array $databaseOptions 822 | * @return array|object 823 | * @throws MongoDBException 824 | */ 825 | public function createCollection(string $collectionName, array $options = [], array $databaseOptions = []) 826 | { 827 | try { 828 | $isException = false; 829 | $result = $this->database($databaseOptions)->createCollection($collectionName, $options); 830 | } catch (\Exception $e) { 831 | $isException = true; 832 | } finally { 833 | $this->pool->release($this); 834 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 835 | return $result; 836 | } 837 | } 838 | 839 | /** 840 | * 移除collection 841 | * 842 | * @param string $collectionName 843 | * @param array $options 844 | * @param array $databaseOptions 845 | * @return array|object 846 | * @throws MongoDBException 847 | */ 848 | public function dropCollection(string $collectionName, array $options = [], array $databaseOptions = []) 849 | { 850 | try { 851 | $isException = false; 852 | $result = $this->database($databaseOptions)->dropCollection($collectionName, $options); 853 | } catch (\Exception $e) { 854 | $isException = true; 855 | } finally { 856 | $this->pool->release($this); 857 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 858 | return $result; 859 | } 860 | } 861 | 862 | /** 863 | * 修改collection 864 | * 865 | * @param string $collectionName 866 | * @param array $collectionOptions 867 | * @param array $options 868 | * @param array $databaseOptions 869 | * @return array|object 870 | * @throws MongoDBException 871 | */ 872 | public function modifyCollection(string $collectionName, array $collectionOptions = [], array $options = [], array $databaseOptions = []) 873 | { 874 | try { 875 | $isException = false; 876 | $result = $this->database($databaseOptions)->modifyCollection($collectionName, $collectionOptions, $options); 877 | } catch (\Exception $e) { 878 | $isException = true; 879 | } finally { 880 | $this->pool->release($this); 881 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 882 | return $result; 883 | } 884 | } 885 | 886 | /** 887 | * 显示所有的集合 TODO 888 | * 889 | * @param array $options 890 | * @param array $databaseOptions 891 | * @return array|\MongoDB\Model\CollectionInfoIterator 892 | * @throws MongoDBException 893 | */ 894 | public function listCollections( array $options = [], array $databaseOptions = []) 895 | { 896 | try { 897 | $isException = false; 898 | $result = $this->database($databaseOptions)->listCollections($options); 899 | } catch (\Exception $e) { 900 | $isException = true; 901 | } finally { 902 | $this->pool->release($this); 903 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 904 | return $result; 905 | } 906 | } 907 | 908 | /** 909 | * Executes a map-reduce aggregation on the collection. 910 | * 911 | * @param string $namespace 912 | * @param JavascriptInterface $map 913 | * @param JavascriptInterface $reduce 914 | * @param $out 915 | * @param array $options 916 | * @param array $collectionOptions 917 | * @return array 918 | * @throws MongoDBException 919 | */ 920 | public function mapReduce(string $namespace, JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = [], array $collectionOptions = []) 921 | { 922 | try { 923 | $isException = false; 924 | $data = $this->collection($namespace, $collectionOptions)->mapReduce($map, $reduce, $out, $options)->getIterator(); 925 | $result = Arr::toArray($data); 926 | } catch (\Exception $e) { 927 | $isException = true; 928 | } finally { 929 | $this->pool->release($this); 930 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 931 | return $result; 932 | } 933 | } 934 | 935 | /** 936 | * Replaces at most one document matching the filter. 937 | * 938 | * @param string $namespace 939 | * @param $filter 940 | * @param $replacement 941 | * @param array $options 942 | * @param array $collectionOptions 943 | * @return int|null 944 | * @throws MongoDBException 945 | */ 946 | public function replaceOne(string $namespace, $filter, $replacement, array $options = [], array $collectionOptions = []) 947 | { 948 | try { 949 | $isException = false; 950 | $result = $this->collection($namespace, $collectionOptions)->replaceOne($filter, $replacement, $options)->getModifiedCount(); 951 | } catch (\Exception $e) { 952 | $isException = true; 953 | } finally { 954 | $this->pool->release($this); 955 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 956 | return $result; 957 | } 958 | } 959 | 960 | /** 961 | * Explains explainable commands 962 | * 963 | * @param string $namespace 964 | * @param Explainable $explainable Command on which to run explain 965 | * @param array $options 966 | * @param array $collectionOptions 967 | * @return mixed 968 | * @throws MongoDBException 969 | */ 970 | public function explain(string $namespace, Explainable $explainable, array $options = [], array $collectionOptions = []) 971 | { 972 | try { 973 | $isException = false; 974 | $result = current($this->collection($namespace, $collectionOptions)->explain($explainable, $options)); 975 | } catch (\Exception $e) { 976 | $isException = true; 977 | } finally { 978 | $this->pool->release($this); 979 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 980 | return $result; 981 | } 982 | } 983 | 984 | /** 985 | * 使用command的方式查询出数据 986 | * 987 | * @param string $namespace 988 | * @param array $command 989 | * @return array 990 | * @throws Exception 991 | */ 992 | public function queryByCommand(array $command) 993 | { 994 | $command = new \MongoDB\Driver\Command($command); 995 | return $this->mongoClient->getManager()->executeCommand($this->config['db'], $command)->toArray(); 996 | } 997 | 998 | /** 999 | * 获取collection 中满足条件的条数 1000 | * 1001 | * @param string $namespace 1002 | * @param array $filter 1003 | * @return int 1004 | * @throws MongoDBException 1005 | */ 1006 | public function count(string $namespace, array $filter = []) 1007 | { 1008 | try { 1009 | $isException = false; 1010 | $command = new Command([ 1011 | 'count' => $namespace, 1012 | 'query' => $filter 1013 | ]); 1014 | $cursor = $this->mongoClient->getManager()->executeCommand($this->config['db'], $command); 1015 | $count = $cursor->toArray()[0]->n; 1016 | } catch (\Exception $e) { 1017 | $isException = true; 1018 | } catch (Exception $e) { 1019 | $isException = true; 1020 | } finally { 1021 | $this->pool->release($this); 1022 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 1023 | return $count; 1024 | } 1025 | } 1026 | 1027 | /** 1028 | * 获取查询的个数 1029 | * 1030 | * @param string $namespace 1031 | * @param array $command 1032 | * @return int 1033 | * @throws Exception 1034 | */ 1035 | public function countByCommand(array $command = []) 1036 | { 1037 | $command = new \MongoDB\Driver\Command($command); 1038 | return count($this->mongoClient->getManager()->executeCommand($this->config['db'], $command)->toArray()); 1039 | } 1040 | 1041 | /** 1042 | * @param array $command 1043 | * @return array 1044 | * @throws Exception 1045 | * @throws MongoDBException 1046 | */ 1047 | public function command(array $command = []) 1048 | { 1049 | try { 1050 | $isException = false; 1051 | $command = new Command($command); 1052 | $res = $this->mongoClient->getManager()->executeCommand($this->config['db'], $command)->toArray(); 1053 | } catch (\Exception $e) { 1054 | $isException = true; 1055 | } finally { 1056 | $this->pool->release($this); 1057 | if ($isException) throw new MongoDBException($this->handleErrorMsg($e), 400, $e->getPrevious()); 1058 | return $res; 1059 | } 1060 | } 1061 | 1062 | /** 1063 | * 判断当前的数据库连接是否已经超时 1064 | * 1065 | * @return bool 1066 | * @throws \MongoDB\Driver\Exception\Exception 1067 | * @throws MongoDBException 1068 | */ 1069 | public function check(): bool 1070 | { 1071 | try { 1072 | $command = new Command(['ping' => 1]); 1073 | $this->mongoClient->getManager()->executeCommand($this->config['db'], $command); 1074 | return true; 1075 | } catch (\Throwable $e) { 1076 | return $this->catchMongoException($e); 1077 | } 1078 | } 1079 | 1080 | /** 1081 | * @param \Throwable $e 1082 | * @return bool 1083 | * @throws MongoDBException 1084 | */ 1085 | private function catchMongoException(\Throwable $e) 1086 | { 1087 | switch ($e) { 1088 | case ($e instanceof InvalidArgumentException): 1089 | { 1090 | throw new MongoDBException('mongo argument exception: ' . $e->getMessage(), 400, $e->getPrevious()); 1091 | } 1092 | case ($e instanceof AuthenticationException): 1093 | { 1094 | throw new MongoDBException('mongo数据库连接授权失败:' . $e->getMessage(), 400, $e->getPrevious()); 1095 | } 1096 | case ($e instanceof ConnectionException): 1097 | { 1098 | /** 1099 | * https://cloud.tencent.com/document/product/240/4980 1100 | * 存在连接失败的,那么进行重连 1101 | */ 1102 | for ($counts = 1; $counts <= 5; $counts++) { 1103 | try { 1104 | $this->reconnect(); 1105 | } catch (\Exception $e) { 1106 | continue; 1107 | } 1108 | break; 1109 | } 1110 | return true; 1111 | } 1112 | case ($e instanceof RuntimeException): 1113 | { 1114 | throw new MongoDBException('mongo runtime exception: ' . $e->getMessage(), 400, $e->getPrevious()); 1115 | } 1116 | default: 1117 | { 1118 | throw new MongoDBException('mongo unexpected exception: ' . $e->getMessage(), 400, $e->getPrevious()); 1119 | } 1120 | } 1121 | } 1122 | 1123 | /** 1124 | * @param $filter 1125 | * @return array 1126 | */ 1127 | public function handleFilter(&$filter) 1128 | { 1129 | if (is_array($filter) && !empty($filter['_id']) && !($filter['_id'] instanceof ObjectId)) { 1130 | if (is_array($filter['_id'])) { 1131 | foreach ($filter['_id'] as $k => $ids) { 1132 | if ($k == '$in') { 1133 | foreach ($ids as $k1 => $id) { 1134 | (!$id instanceof ObjectId) ? $filter['_id']['$in'][$k1] = new ObjectId($id) : ''; 1135 | } 1136 | } elseif ($k == '$nin') { 1137 | foreach ($ids as $k1 => $id) { 1138 | (!$id instanceof ObjectId) ? $filter['_id']['$nin'][$k1] = new ObjectId($id) : ''; 1139 | } 1140 | } elseif (is_numeric($k)){ // 如果是索引数字 1141 | (!$ids instanceof ObjectId) ? $filter['_id'][$k] = new ObjectId($ids) : ''; 1142 | } 1143 | } 1144 | } else { 1145 | $filter['_id'] = new ObjectId($filter['_id']); 1146 | } 1147 | } 1148 | if (is_object($filter) && !empty($filter->_id) && !($filter->_id instanceof ObjectId)) { 1149 | if (is_array($filter->_id)) { 1150 | foreach ($filter->_id as $k => $ids) { 1151 | if ($k == '$in') { 1152 | foreach ($ids as $k1 => $id) { 1153 | (!$id instanceof ObjectId) ? $filter->_id['$in'][$k1] = new ObjectId($id) : ''; 1154 | } 1155 | } elseif ($k == '$nin') { 1156 | foreach ($ids as $k1 => $id) { 1157 | (!$id instanceof ObjectId) ? $filter->_id['$nin'][$k1] = new ObjectId($id) : ''; 1158 | } 1159 | } elseif (is_numeric($k)){ // 如果是索引数字 1160 | (!$ids instanceof ObjectId) ? $filter->_id[$k] = new ObjectId($ids) : ''; 1161 | } 1162 | } 1163 | } else { 1164 | $filter->_id = new ObjectId($filter->_id); 1165 | } 1166 | } 1167 | return $filter; 1168 | } 1169 | 1170 | /** 1171 | * @param $e 1172 | * @return string 1173 | */ 1174 | public function handleErrorMsg($e) 1175 | { 1176 | return $e->getFile() . $e->getLine() . $e->getMessage(); 1177 | } 1178 | } 1179 | --------------------------------------------------------------------------------