MongoDB

未匹配的标注

MongoDB

Yii2 框架的官方扩展 yii2-mongodb,为我们提供了 MongoDB 数据库相关功能的整合。

安装

这个扩展要求 您的 PHP 安装了 MongoDB 扩展,且版本要求 >= 1.0.0。同时也要求 MongoDB 服务端版本 >= 3.0。

安装此扩展的首选方式是通过 composer,命令如下:

composer require yiisoft/yii2-mongodb

或者在您的 composer.json 文件中的 require 部分增加以下内容:

"yiisoft/yii2-mongodb": "~2.1.0"

然后执行 composer install 命令即可。

配置应用

要使用这个扩展,只需在您的应用程序配置中添加以下代码:

return [
    //....
    'components' => [
        'mongodb' => [
            'class' => '\yii\mongodb\Connection',
            'dsn' => 'mongodb://localhost:27017/mydatabase',
            'options' => [
                "username" => "Username",
                "password" => "Password"
            ],
            //以上dsn与options,可以直接这样写
            //'dsn' => 'mongodb://Username:Password@localhost:27017/mydatabase',
        ],
    ],
];

基本用法

Once you have a MongoDB connection instance, you can execute a MongoDB commands and queries using [[yii\mongodb\Command]]:

// execute command:
$result = Yii::$app->mongodb->createCommand(['listIndexes' => 'some_collection'])->execute();

// execute query (find):
$cursor = Yii::$app->mongodb->createCommand(['projection' => ['name' => true]])->query('some_collection');

// execute batch (bulk) operations:
Yii::$app->mongodb->createCommand()
    ->addInsert(['name' => 'new'])
    ->addUpdate(['name' => 'existing'], ['name' => 'updated'])
    ->addDelete(['name' => 'old'])
    ->executeBatch('customer');

Using the connection instance you may access databases and collections. Most of the MongoDB commands are accessible via [[\yii\mongodb\Collection]] instance:

$collection = Yii::$app->mongodb->getCollection('customer');
$collection->insert(['name' => 'John Smith', 'status' => 1]);

To perform “find” queries, you should use [[\yii\mongodb\Query]]:

use yii\mongodb\Query;

$query = new Query();
// compose the query
$query->select(['name', 'status'])
    ->from('customer')
    ->limit(10);
// execute the query
$rows = $query->all();

MongoId specifics

Getting scalar from document ID

Remember: MongoDB document id (_id field) is not scalar, but an instance of [[\MongoDB\BSON\ObjectID]] class. To get actual Mongo ID string your should typecast [[\MongoDB\BSON\ObjectID]] instance to string:

$query = new Query();
$row = $query->from('customer')->one();
var_dump($row['_id']); // outputs: "object(\MongoDB\BSON\ObjectID)"
var_dump((string) $row['_id']); // outputs "string 'acdfgdacdhcbdafa'"

Although this fact is very useful sometimes, it often produces some problems. You may face them in URL composition or attempt of saving “_id” to other storage. In these cases, ensure you have converted [[\MongoDB\BSON\ObjectID]] into the string:

/* @var $this yii\web\View */
echo $this->createUrl(['item/update', 'id' => (string) $row['_id']]);

Same applies to implementing user identity which is stored in MongoDB. When implementing [[\yii\web\IdentityInterface::getId()]] you should typecast [[\MongoDB\BSON\ObjectID]] class to scalar in order for authentication to work.

Getting document ID from scalar

While building condition, values for the key ‘_id’ will be automatically cast to [[\MongoDB\BSON\ObjectID]] instance, even if they are plain strings. So it is not necessary for you to perform back cast of string ‘_id’ representation:

use yii\web\Controller;
use yii\mongodb\Query;

class ItemController extends Controller
{
    /**
     * @param string $id MongoId string (not object)
     */
    public function actionUpdate($id)
    {
        $query = new Query;
        $row = $query->from('item')
            ->where(['_id' => $id]) // implicit typecast to [[\MongoDB\BSON\ObjectID]]
            ->one();
        ...
    }
}

However, if you have other columns, containing [[\MongoDB\BSON\ObjectID]], you should take care of possible typecast on your own.

使用 MongoDB ActiveRecord

This extension provides ActiveRecord solution similar ot the [[\yii\db\ActiveRecord]]. To declare an ActiveRecord class you need to extend [[\yii\mongodb\ActiveRecord]] and implement the collectionName and ‘attributes’ methods:

use yii\mongodb\ActiveRecord;

class Customer extends ActiveRecord
{
    /**
     * @return string the name of the index associated with this ActiveRecord class.
     */
    public static function collectionName()
    {
        return 'customer';
    }

    /**
     * @return array list of attribute names.
     */
    public function attributes()
    {
        return ['_id', 'name', 'email', 'address', 'status'];
    }
}

Note: collection primary key name (‘_id’) should be always explicitly setup as an attribute.

You can use [[\yii\data\ActiveDataProvider]] with [[\yii\mongodb\Query]] and [[\yii\mongodb\ActiveQuery]]:

use yii\data\ActiveDataProvider;
use yii\mongodb\Query;

$query = new Query();
$query->from('customer')->where(['status' => 2]);
$provider = new ActiveDataProvider([
    'query' => $query,
    'pagination' => [
        'pageSize' => 10,
    ]
]);
$models = $provider->getModels();
use yii\data\ActiveDataProvider;
use app\models\Customer;

$provider = new ActiveDataProvider([
    'query' => Customer::find(),
    'pagination' => [
        'pageSize' => 10,
    ]
]);
$models = $provider->getModels();

使用嵌入式文档

This extension does not provide any special way to work with embedded documents (sub-documents) at the moment. General recommendation is avoiding it if possible. For example: instead of:

{
    content: "some content",
    author: {
        name: author1,
        email: author1@domain.com
    }
}

use following:

{
    content: "some content",
    author_name: author1,
    author_email: author1@domain.com
}

Yii Model designed assuming single attribute is a scalar. Validation and attribute processing based on this suggestion. Still any attribute can be an array of any depth and complexity, however you should handle its validation on your own.

While there is no explicit support for embedded documents, there is also no explicit restriction on it. You may create your own solution or use third-party extension like yii2tech/embedded for this feature.

聚合

This extension provides support for the MongoDB aggregation functionality wrapping corresponding commands into PHP methods of [[\yii\mongodb\Command]].

Single Purpose Aggregation Operations

The simplest MongoDB aggregation operations are count and distinct, which are available via [[\yii\mongodb\Command::count()]] and [[\yii\mongodb\Command::distinct()]] correspondingly. For example:

$booksCount = Yii::$app->mongodb->createCommand()->count('books', ['category' => 'programming']);

You may as well use [[\yii\mongodb\Collection::count()]] and [[\yii\mongodb\Collection::distinct()]] shortcut methods:

$booksCount = Yii::$app->mongodb->getCollection('books')->count(['category' => 'programming']);

Methods count() and distinct() are also available at [[\yii\mongodb\Query]] class:

$booksCount = (new Query())
    ->from('books')
    ->where(['category' => 'programming'])
    ->count();

Pipeline

Aggregation Pipeline can be executed via [[\yii\mongodb\Command::aggregate()]]. The following example display how you can group books by authorId field:

$authors = Yii::$app->mongodb->createCommand()->aggregate('books', [
    [
        '$group' => [
            '_id' => '$authorId',
        ],
    ],
]);

You may as well use [[\yii\mongodb\Collection::aggregate()]] as shortcut. In the following example we are grouping books by both authorId and category fields:

$collection = Yii::$app->mongodb->getCollection('books');
$authors = $collection->aggregate([
    [
        '$group'   => [
            '_id'      => '$authorId',
            'category' => '$category',
        ],
    ],
]);

Multiple pipelines can be specified for more sophisticated aggregation. In the following example we are grouping books by authorId field, sorting them by createdAt field descending and then we are limiting the result to 100 documents skipping first 300 records.

$collection = Yii::$app->mongodb->getCollection('books');
$authors = $collection->aggregate([
    [
        '$match' => [
            'name' => ['$ne' => ''],
        ],
    ],
    [
        '$group' => [
            '_id' => '$authorId',
        ],
    ],
    [
        '$sort' => ['createdAt' => -1]
    ],
    [
        '$skip' => 300
    ],
    [
        '$limit' => 100
    ],
]);

Please refer to MongoDB Aggregation Pipeline Docs for detailed information about pipeline specifications.

Aggregation via [[\yii\mongodb\Query]]

Simple aggregations can be performed via following methods of the [[\yii\mongodb\Query]] class:

  • sum() - returns the sum of the specified column values.
  • average() - returns the average of the specified column values.
  • min() - returns the minimum of the specified column values.
  • max() - returns the maximum of the specified column values.

In case of these methods invocation [[\yii\mongodb\Query::$where]] will be used for $match pipeline composition.

use yii\mongodb\Query;

$maxPrice = (new Query())
    ->from('books')
    ->where(['name' => ['$ne' => '']])
    ->max('price', $db);

Map Reduce

Map Reduce can be executed via [[\yii\mongodb\Command::mapReduce()]].

$result = Yii::$app->mongodb->createCommand()->mapReduce('books',
    'function () {emit(this.status, this.amount)}',
    'function (key, values) {return Array.sum(values)}',
    'mapReduceOut',
    ['status' => ['$lt' => 3]]
);

You may as well use [[\yii\mongodb\Collection::mapReduce()]] as shortcut.

$result = Yii::$app->mongodb->getCollection('books')->mapReduce(
    'function () {emit(this.status, this.amount)}',
    'function (key, values) {return Array.sum(values)}',
    'mapReduceOut',
    ['status' => ['$lt' => 3]]
);

使用 GridFS

This extension supports MongoGridFS via classes under namespace “\yii\mongodb\file”. There you will find specific Collection, Query and ActiveRecord classes.

You can upload a file using [[\yii\mongodb\file\Upload]]:

$document = Yii::$app->mongodb->getFileCollection()->createUpload()
    ->addContent('Part 1')
    ->addContent('Part 2')
    // ...
    ->complete();

You can download the file using [[\yii\mongodb\file\Download]]:

Yii::$app->mongodb->getFileCollection()->createDownload($document['_id'])->toFile('/path/to/file.dat');

Each row of the file query result contains [[\yii\mongodb\file\Download]] instance at the key ‘file’:

use yii\mongodb\file\Query;

$query = new Query();
$rows = $query->from('fs')
    ->limit(10)
    ->all();

foreach ($rows as $row) {
    var_dump($row['file']); // outputs: "object(\yii\mongodb\file\Download)"
    echo $row['file']->toString(); // outputs file content
}

Using [\yii\mongodb\file\ActiveRecord]] you can manipulate the file using ‘file’ property:

use yii\mongodb\file\ActiveRecord;

class ImageFile extends ActiveRecord
{
    //...
}

$record = new ImageFile();
$record->number = 15;
$record->file = '/path/to/some/file.jpg'; // upload local file to GridFS
$record->save();

$record = ImageFile::find()->where(['number' => 15])->one();
var_dump($record->file); // outputs: "object(\yii\mongodb\file\Download)"
echo $record->file->toString(); // outputs file content

You may as well operate GridFS files via regular PHP stream resource. You will need to register a stream wrapper provided by this extension - [[\yii\mongodb\file\StreamWrapper]]. This can be done via [[\yii\mongodb\file\Connection::registerFileStreamWrapper()]]. Once stream wrapper is registered, you may open a stream resource using following format:

'protocol://databaseName.fileCollectionPrefix?file_attribute=value'

For example:

Yii::$app->mongodb->registerFileStreamWrapper(); // register stream wrapper

// write a file:
$resource = fopen('gridfs://mydatabase.fs?filename=new_file.txt', 'w');
fwrite($resource, 'some content');
// ...
fclose($resource);

// write file with several fields:
$resource = fopen('gridfs://mydatabase.fs?filename=new_file.txt&number=17&status=active', 'w');
fwrite($resource, 'file number 17 with status "active"');
fclose($resource);

// read a file:
$resource = fopen('gridfs://mydatabase.fs?filename=my_file.txt', 'r');
$fileContent = stream_get_contents($resource);

日志和分析

This extension provides logging for executed commands or queries. Logging is optional and can be enabled or disabled at [[\yii\mongodb\Connection]] level:

return [
    //....
    'components' => [
        'mongodb' => [
            'class' => '\yii\mongodb\Connection',
            'dsn' => 'mongodb://developer:password@localhost:27017/mydatabase',
            'enableLogging' => true, // enable logging
            'enableProfiling' => true, // enable profiling
        ],
    ],
];

Note: log messages generated for the commands and queries do not contain actual text of the performed queries, they contains only a close approximation of it, composed on the values which can be extracted from PHP MongoDB extension classes. If you need to see actual query text, you should use specific tools for that.

Tip: Keep in mind that composition of the log messages take some time and program resources. Thus it make sense to disable logging at the production environment.

使用 Cache 组件

To use the Cache component, in addition to configuring the connection as described in Installation section, you also have to configure the cache component to be yii\mongodb\Cache:

return [
    //....
    'components' => [
        // ...
        'cache' => [
            'class' => 'yii\mongodb\Cache',
        ],
    ]
];

使用 Session 组件

To use the Session component, in addition to configuring the connection as described in Installation section, you also have to configure the session component to be yii\mongodb\Session:

return [
    //....
    'components' => [
        // ...
        'session' => [
            'class' => 'yii\mongodb\Session',
        ],
    ]
];

使用 I18N 消息源

You may use [[\yii\mongodb\i18n\MongoDbMessageSource]] for the i18n message translations storage. Application configuration example:

return [
    //....
    'components' => [
        // ...
        'i18n' => [
            'translations' => [
                '*' => [
                    'class' => 'yii\mongodb\i18n\MongoDbMessageSource'
                ]
            ]
        ],
    ]
];

[[\yii\mongodb\i18n\MongoDbMessageSource]] uses single collection to store all translations. Each entry in this collection should have 3 fields:

  • language: string, translation language
  • category: string, name translation category
  • messages: array, list of actual message translations, in each element: the ‘message’ key is raw message name and ‘translation’ key - message translation.

For example:

{
    "category": "app",
    "language": "de",
    "messages": [
        {
            "message": "Hello world!",
            "translation": "Hallo Welt!"
        },
        {
            "message": "The dog runs fast.",
            "translation": "Der Hund rennt schnell.",
        },
        ...
    ],
}

You also can specify ‘messages’ using source message as a direct BSON key, while its value holds the translation. For example:

{
    "category": "app",
    "language": "de",
    "messages": {
        "Hello world!": "Hallo Welt!",
        "See more": "Mehr sehen",
        ...
    },
}

However such approach is not recommended as BSON keys can not contain symbols like . or $.

Please refer to [[\yii\mongodb\i18n\MongoDbMessageSource]] for more details about configuration and translation collection data structure.

使用 Gii generator

This extension provides a code generator, which can be integrated with yii ‘gii’ module. It allows generation of the Active Record code. In order to enable it, you should adjust your application configuration in following way:

return [
    //....
    'modules' => [
        // ...
        'gii' => [
            'class' => 'yii\gii\Module',
            'generators' => [
                'mongoDbModel' => [
                    'class' => 'yii\mongodb\gii\model\Generator'
                ]
            ],
        ],
    ]
];

Note: since MongoDB is schemaless, there is not much information, which generated code may base on. So generated code is very basic and definitely requires adjustments.

使用 MongoDB Debug 面板

The yii2 MongoDB extensions provides a debug panel that can be integrated with the yii debug module and shows the executed MongoDB queries.

Add the following to you application config to enable it (if you already have the debug module enabled, it is sufficient to just add the panels configuration):

    // ...
    'bootstrap' => ['debug'],
    'modules' => [
        'debug' => [
            'class' => 'yii\\debug\\Module',
            'panels' => [
                'mongodb' => [
                    'class' => 'yii\\mongodb\\debug\\MongoDbPanel',
                    // 'db' => 'mongodb', // MongoDB component ID, defaults to `db`. Uncomment and change this line, if you registered MongoDB component with a different ID.
                ],
            ],
        ],
    ],
    // ...

使用数据库迁移

MongoDB is schemaless and will create any missing collection on the first demand. However there are many cases, when you may need applying persistent changes to the MongoDB database. For example: you may need to create a collection with some specific options or create indexes. MongoDB migrations are managed via [[yii\mongodb\console\controllers\MigrateController]], which is an analog of regular [[\yii\console\controllers\MigrateController]].

In order to enable this command you should adjust the configuration of your console application:

return [
    // ...
    'controllerMap' => [
        'mongodb-migrate' => 'yii\mongodb\console\controllers\MigrateController'
    ],
];

Below are some common usages of this command:

# creates a new migration named 'create_user_collection'
yii mongodb-migrate/create create_user_collection

# applies ALL new migrations
yii mongodb-migrate

# reverts the last applied migration
yii mongodb-migrate/down

Special configuration for an application that uses more than one DB engine

In case your application uses multiple databases, example:

  • MySQL + MongoDB

If you run the migration commands, it will evaluate both MySQL and MongoDB migration files at the same time since both by default share the same folder.

Problem: MongoDB will try to run MySQL’s migration files and the other way around.

In order to avoid that behavior, you can create a new folder called mongodb under your migrations folder, and then setup your console application like this:

return [
    // ...
    'controllerMap' => [
        'mongodb-migrate' => [
          'class' => 'yii\mongodb\console\controllers\MigrateController',
          'migrationPath' => '@app/migrations/mongodb',
        ],
    ],
];

💖喜欢本文档的,欢迎点赞、收藏、留言或转发,谢谢支持!
作者邮箱:zhuzixian520@126.com,github地址:github.com/zhuzixian520

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
zhuzixian520
讨论数量: 0
发起讨论 只看当前版本


暂无话题~