MongoDB 是一个基于分布式文件存储的数据库,也介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的
它支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类型
Mongo 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
使用 MongoDB 的用户很多,因为它的文档型存储一些变化的内容很方便
在 PHP5 及以前,官方提供了两个扩展,Mongo 和 MongoDB,其中 Mongo 是对以 MongoClient
等几个核心类为基础的类群进行操作,封装得很方便,所以基本上都会选择 Mongo 扩展。
但是随着 PHP 升级到 PHP7,官方不再支持 Mongo 扩展,只支持 MongoDB,所以怎么把 Mongo 无缝替换成 MongoDB 成为了一个亟待解决的问题
MongoDB 引入了命名空间,但是功能封装非常差,如果非要用原生的扩展,几乎意味着写原生的 Mongo 语句
这种想法很违背 ORM 的初衷:简化 DB 操作带来的语法问题而专注逻辑优化
MongoDB 驱动
使用新驱动的部分代码如下:
use MongoDB\Driver\WriteConcern;
class MongoDb
{
//属性略
public function __construct($config)
{
//解析拼接配置过程略
$this->mongodb = new \MongoDB\Driver\Manager($mongoServer);
$this->database = $config['database'];
$this->collection = $config['collection'];
$this->bulk = new \MongoDB\Driver\BulkWrite();
$this->writeConcern = new WriteConcern(WriteConcern::MAJORITY, 100);
}
public function insert($data = [])
{
$this->bulk->insert($data);
$result = $this->mongodb->executeBulkWrite(
"$this->database.$this->collection",
$this->bulk,
$this->writeConcern
);
return $result->getInsertedCount();
}
public function delete($where = [], $limit = 1)
{
$this->bulk->delete($where, ['limit' => $limit]);
$result = $this->mongodb->executeBulkWrite(
"$this->database.$this->collection",
$this->bulk,
$this->writeConcern
);
return $result->getDeletedCount();
}
public function update($where = [], $update = [], $upsert = false)
{
$this->bulk->update($where, ['$set' => $update], ['multi' => true, 'upsert' => $upsert]);
$result = $this->mongodb->executeBulkWrite(
"$this->database.$this->collection",
$this->bulk,
$this->writeConcern
);
return $result->getModifiedCount();
}
public function query($where = [], $option = [])
{
$query = new \MongoDB\Driver\Query($where, $option);
$result = $this->mongodb->executeQuery("$this->database.$this->collection", $query);
return json_encode($result);
}
}
这样的语法和之前差异太大,改动不方便
在这种情况之下,MongoDB 官方为了方便使用,增加市场占有率,推出了基于 MongoDB 扩展的库:mongo-php-library
该库的详细文档见:docs.mongodb.com
composer 下载:
composer require mongodb/mongodb
MongoDB 操作包与旧 Mongo 对比
连接
//old
new MongoClient();
//new
new MongoDB\Client();
新增
//old
$collention->insert($array, $options);
//new
$resultOne = $collention->insertOne($array, $options);//单
$lastId = $resultOne->getInsertedId();
$resultMany = $collention->insertMany($array, $options);//多
$count = $resultMany->getInsertedCount();
修改
//old
$collention->update($condition, [
'$set' => $values
,[
'multiple' => true//多条,单条false
]);
//new
$collection->updateOne(
['state' => 'ny'],
['$set' => ['country' => 'us']]
);
$updateResult = $collection->updateMany(
['state' => 'ny'],
['$set' => ['country' => 'us']]
);
$count = $updateResult->getModifiedCount();
查询
//old
$cursor = $collection->find($condition, [
'name' => true//指定字段
]);
$cursor->skip(5);
$cursor->limit(5);
$cursor->sort([
'time' => -1
]);
//new
$cursor = $collection->find($condition, [
'skip' => 5,
'limit' => 5,
'sort' => [
'time' => -1
],//排序
'projection' => [
'name' => 1//指定字段
]
]);
删除
//old
$collention->remove($condition, [
'justOne' => false//删单条
]);
$collention->remove([]);//删所有
//new
$result = $collention->deleteOne($condition, $options);
$collention->deleteMany($condition, $options);
$result->getDeletedCount();
有业务可能需要以类似 MySQL 的自增 ID 来处理数据,PHP5 可能使用的 findAndModify()
方法来查询并修改:
$collention->findAndModify([
'_id' => $tableName//我在自增表中用其它的表名作主键
], [
'$inc' => ['id' => 1]//自增
], [
'_id' => 0
], [
'new' => 1//返回修改后的结果,默认是修改前的
]);
现在使用 MongoDB 库的话需要修改为:
$collention->findOneAndUpdate([
'_id' => $tableName
], [
'$inc' => ['id' => 1]
], [
'projection' => ['id' => 1],
'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER
]);
类似的还有 findOneAndDelete()
findOneAndReplace()
,更多内容可见 findOneAndUpdate文档
ODM
在开发一个业务中,由于条件非常复杂,会出现各种 $and
$or
嵌套,想添加新条件时容易出问题
所以在基类中引入了 ODM,支持链式查询的写法,使查询条件直观易理解
改造了项目中老的 Mongo 操作类,引入了新 Trait
trait MongoODM
{
public $mongoOdm;
public function connectOdm($db)
{
//解析拼接配置过程略
$client = new \Sokil\Mongo\Client($mongoServer);
$database = $client->getDatabase($config['database']);
$this->mongoOdm = new MongoOdm($database);
}
}
在原有操作类中新增一行 ODM 连接
use MongoODM;
$this->connectOdm(self::Key);
查询时部分示例代码如下:
$collection = $bean->mongoOdm->collection;
$builder = $bean->mongoOdm->find();
$builder->whereIn('sale_id', [1,2,3]);
$builder->where('identity', ['$bitsAnySet' => [$bit]]);
$builder->whereAnd(
$collection->expression()->whereOr(
$collection->expression()->whereGreaterOrEqual('last_time', strtotime(date('Y-m-d'))),
$collection->expression()->whereLike('desc', "{$desc}", true)
)
);
$total = $builder->count();
$result = $builder->fields(explode(','), $fields)
->sort($sort)
->limit($size)
->skip($offset)
->findAll();