123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157 |
- <?php
- // +----------------------------------------------------------------------
- // | ThinkPHP [ WE CAN DO IT JUST THINK ]
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: liu21st <liu21st@gmail.com>
- // +----------------------------------------------------------------------
- namespace think\mongo;
- use MongoDB\BSON\ObjectID;
- use MongoDB\Driver\BulkWrite;
- use MongoDB\Driver\Command;
- use MongoDB\Driver\Cursor;
- use MongoDB\Driver\Exception\AuthenticationException;
- use MongoDB\Driver\Exception\BulkWriteException;
- use MongoDB\Driver\Exception\ConnectionException;
- use MongoDB\Driver\Exception\InvalidArgumentException;
- use MongoDB\Driver\Exception\RuntimeException;
- use MongoDB\Driver\Query as MongoQuery;
- use MongoDB\Driver\ReadPreference;
- use MongoDB\Driver\WriteConcern;
- use think\Cache;
- use think\Collection;
- use think\Config;
- use think\Db;
- use think\db\exception\DataNotFoundException;
- use think\db\exception\ModelNotFoundException;
- use think\Exception;
- use think\exception\DbException;
- use think\Loader;
- use think\Model;
- use think\Paginator;
- class Query
- {
- // 数据库Connection对象实例
- protected $connection;
- // 数据库Builder对象实例
- protected $builder;
- // 当前模型类名称
- protected $model;
- // 当前数据表名称(含前缀)
- protected $table = '';
- // 当前数据表名称(不含前缀)
- protected $name = '';
- // 当前数据表主键
- protected $pk;
- // 当前数据表前缀
- protected $prefix = '';
- // 查询参数
- protected $options = [];
- // 数据表信息
- protected static $info = [];
- // 回调事件
- private static $event = [];
- /**
- * 架构函数
- * @access public
- * @param Connection $connection 数据库对象实例
- * @param string $model 模型名
- */
- public function __construct(Connection $connection = null, $model = '')
- {
- $this->connection = $connection ?: Db::connect([], true);
- $this->prefix = $this->connection->getConfig('prefix');
- $this->model = $model;
- // 设置当前连接的Builder对象
- $this->setBuilder();
- }
- /**
- * 利用__call方法实现一些特殊的Model方法
- * @access public
- * @param string $method 方法名称
- * @param array $args 调用参数
- * @return mixed
- * @throws DbException
- * @throws Exception
- */
- public function __call($method, $args)
- {
- if (strtolower(substr($method, 0, 5)) == 'getby') {
- // 根据某个字段获取记录
- $field = Loader::parseName(substr($method, 5));
- $where[$field] = $args[0];
- return $this->where($where)->find();
- } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
- // 根据某个字段获取记录的某个值
- $name = Loader::parseName(substr($method, 10));
- $where[$name] = $args[0];
- return $this->where($where)->value($args[1]);
- } else {
- throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
- }
- }
- /**
- * 获取当前的数据库Connection对象
- * @access public
- * @return Connection
- */
- public function getConnection()
- {
- return $this->connection;
- }
- /**
- * 切换当前的数据库连接
- * @access public
- * @param mixed $config
- * @return $this
- */
- public function connect($config)
- {
- $this->connection = Db::connect($config);
- $this->setBuilder();
- return $this;
- }
- /**
- * 设置当前的数据库Builder对象
- * @access protected
- * @return void
- */
- protected function setBuilder()
- {
- $this->builder = new Builder($this->connection, $this);
- }
- /**
- * 指定默认的数据表名(不含前缀)
- * @access public
- * @param string $name
- * @return $this
- */
- public function name($name)
- {
- $this->name = $name;
- return $this;
- }
- /**
- * 指定默认数据表名(含前缀)
- * @access public
- * @param string $table 表名
- * @return $this
- */
- public function setTable($table)
- {
- $this->table = $table;
- return $this;
- }
- /**
- * 得到当前或者指定名称的数据表
- * @access public
- * @param string $name
- * @return string
- */
- public function getTable($name = '')
- {
- if ($name || empty($this->table)) {
- $name = $name ?: $this->name;
- $tableName = $this->prefix;
- if ($name) {
- $tableName .= Loader::parseName($name);
- }
- } else {
- $tableName = $this->table;
- }
- return $tableName;
- }
- /**
- * 指定数据表主键
- * @access public
- * @param string $pk 主键
- * @return $this
- */
- public function pk($pk)
- {
- $this->pk = $pk;
- return $this;
- }
- /**
- * 去除某个查询条件
- * @access public
- * @param string $field 查询字段
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function removeWhereField($field, $logic = 'and')
- {
- $logic = '$' . strtoupper($logic);
- if (isset($this->options['where'][$logic][$field])) {
- unset($this->options['where'][$logic][$field]);
- }
- return $this;
- }
- /**
- * 去除查询参数
- * @access public
- * @param string|bool $option 参数名 true 表示去除所有参数
- * @return $this
- */
- public function removeOption($option = true)
- {
- if (true === $option) {
- $this->options = [];
- } elseif (is_string($option) && isset($this->options[$option])) {
- unset($this->options[$option]);
- }
- return $this;
- }
- /**
- * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写)
- * @access public
- * @param string $sql sql语句
- * @return string
- */
- public function parseSqlTable($sql)
- {
- if (false !== strpos($sql, '__')) {
- $prefix = $this->prefix;
- $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) {
- return $prefix . strtolower($match[1]);
- }, $sql);
- }
- return $sql;
- }
- /**
- * 执行查询 返回数据集
- * @access public
- * @param string $namespace
- * @param MongoQuery $query 查询对象
- * @param ReadPreference $readPreference readPreference
- * @param bool|string $class 指定返回的数据集对象
- * @param string|array $typeMap 指定返回的typeMap
- * @return mixed
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function query($namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null)
- {
- return $this->connection->query($namespace, $query, $readPreference, $class, $typeMap);
- }
- /**
- * 执行指令 返回数据集
- * @access public
- * @param Command $command 指令
- * @param string $dbName
- * @param ReadPreference $readPreference readPreference
- * @param bool|string $class 指定返回的数据集对象
- * @param string|array $typeMap 指定返回的typeMap
- * @return mixed
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function command(Command $command, $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null)
- {
- return $this->connection->command($command, $dbName, $readPreference, $class, $typeMap);
- }
- /**
- * 执行语句
- * @access public
- * @param string $namespace
- * @param BulkWrite $bulk
- * @param WriteConcern $writeConcern
- * @return int
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function execute($namespace, BulkWrite $bulk, WriteConcern $writeConcern = null)
- {
- return $this->connection->execute($namespace, $bulk, $writeConcern);
- }
- /**
- * 获取最近插入的ID
- * @access public
- * @return string
- */
- public function getLastInsID()
- {
- $id = $this->builder->getLastInsID();
- if ($id instanceof ObjectID) {
- $id = $id->__toString();
- }
- return $id;
- }
- /**
- * 获取最近一次执行的指令
- * @access public
- * @return string
- */
- public function getLastSql()
- {
- return $this->connection->getQueryStr();
- }
- /**
- * 获取数据库的配置参数
- * @access public
- * @param string $name 参数名称
- * @return boolean
- */
- public function getConfig($name = '')
- {
- return $this->connection->getConfig($name);
- }
- /**
- * 得到某个字段的值
- * @access public
- * @param string $field 字段名
- * @param mixed $default 默认值
- * @return mixed
- */
- public function value($field, $default = null)
- {
- $result = null;
- if (!empty($this->options['cache'])) {
- // 判断查询缓存
- $cache = $this->options['cache'];
- if (empty($this->options['table'])) {
- $this->options['table'] = $this->getTable();
- }
- $key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
- $result = Cache::get($key);
- }
- if (!$result) {
- if (isset($this->options['field'])) {
- unset($this->options['field']);
- }
- $cursor = $this->field($field)->fetchCursor(true)->find();
- $cursor->setTypeMap(['root' => 'array']);
- $resultSet = $cursor->toArray();
- $data = isset($resultSet[0]) ? $resultSet[0] : null;
- $result = $data[$field];
- if (isset($cache)) {
- // 缓存数据
- $this->cacheData($key, $result, $cache);
- }
- } else {
- // 清空查询条件
- $this->options = [];
- }
- return !is_null($result) ? $result : $default;
- }
- /**
- * 得到某个列的数组
- * @access public
- * @param string $field 字段名 多个字段用逗号分隔
- * @param string $key 索引
- * @return array
- */
- public function column($field, $key = '')
- {
- $result = false;
- if (!empty($this->options['cache'])) {
- // 判断查询缓存
- $cache = $this->options['cache'];
- if (empty($this->options['table'])) {
- $this->options['table'] = $this->getTable();
- }
- $guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
- $result = Cache::get($guid);
- }
- if (!$result) {
- if (isset($this->options['projection'])) {
- unset($this->options['projection']);
- }
- if ($key && '*' != $field) {
- $field = $key . ',' . $field;
- }
- $cursor = $this->field($field)->fetchCursor(true)->select();
- $cursor->setTypeMap(['root' => 'array']);
- $resultSet = $cursor->toArray();
- if ($resultSet) {
- $fields = array_keys($resultSet[0]);
- $count = count($fields);
- $key1 = array_shift($fields);
- $key2 = $fields ? array_shift($fields) : '';
- $key = $key ?: $key1;
- foreach ($resultSet as $val) {
- $name = $val[$key];
- if ($name instanceof ObjectID) {
- $name = $name->__toString();
- }
- if (2 == $count) {
- $result[$name] = $val[$key2];
- } elseif (1 == $count) {
- $result[$name] = $val[$key1];
- } else {
- $result[$name] = $val;
- }
- }
- } else {
- $result = [];
- }
- if (isset($cache) && isset($guid)) {
- // 缓存数据
- $this->cacheData($guid, $result, $cache);
- }
- } else {
- // 清空查询条件
- $this->options = [];
- }
- return $result;
- }
- /**
- * 执行command
- * @access public
- * @param string|array|object $command 指令
- * @param mixed $extra 额外参数
- * @param string $db 数据库名
- * @return array
- */
- public function cmd($command, $extra = null, $db = null)
- {
- if (is_array($command) || is_object($command)) {
- if ($this->connection->getConfig('debug')) {
- $this->connection->log('cmd', 'cmd', $command);
- }
- // 直接创建Command对象
- $command = new Command($command);
- } else {
- // 调用Builder封装的Command对象
- $options = $this->parseExpress();
- $command = $this->builder->$command($options, $extra);
- }
- return $this->command($command, $db);
- }
- /**
- * 指定distinct查询
- * @access public
- * @param string $field 字段名
- * @return array
- */
- public function distinct($field)
- {
- $result = $this->cmd('distinct', $field);
- return $result[0]['values'];
- }
- /**
- * 获取数据库的所有collection
- * @access public
- * @param string $db 数据库名称 留空为当前数据库
- * @throws Exception
- */
- public function listCollections($db = '')
- {
- $cursor = $this->cmd('listCollections', null, $db);
- $result = [];
- foreach ($cursor as $collection) {
- $result[] = $collection['name'];
- }
- return $result;
- }
- /**
- * COUNT查询
- * @access public
- * @return integer
- */
- public function count()
- {
- $result = $this->cmd('count');
- return $result[0]['n'];
- }
- /**
- * 多聚合操作
- *
- * @param array $aggregate 聚合指令, 可以聚合多个参数, 如 ['sum' => 'field1', 'avg' => 'field2']
- * @param array $groupBy 类似mysql里面的group字段, 可以传入多个字段, 如 ['field_a', 'field_b', 'field_c']
- * @return array 查询结果
- */
- public function multiAggregate($aggregate, $groupBy)
- {
- $result = $this->cmd('multiAggregate', [$aggregate, $groupBy]);
- $result = isset($result[0]['result']) ? $result[0]['result'] : [];
- foreach ($result as &$row) {
- if (isset($row['_id']) && !empty($row['_id'])) {
- foreach ($row['_id'] as $k => $v) {
- $row[$k] = $v;
- }
- unset($row['_id']);
- }
- }
- return $result;
- }
- /**
- * 聚合查询
- * @access public
- * @param string $aggregate 聚合指令
- * @param string $field 字段名
- * @return mixed
- */
- public function aggregate($aggregate, $field)
- {
- $result = $this->cmd('aggregate', [$aggregate, $field]);
- return isset($result[0]['aggregate']) ? $result[0]['aggregate'] : 0;
- }
- /**
- * MAX查询
- * @access public
- * @param string $field 字段名
- * @return float
- */
- public function max($field)
- {
- return $this->aggregate('max', $field);
- }
- /**
- * MIN查询
- * @access public
- * @param string $field 字段名
- * @return mixed
- */
- public function min($field)
- {
- return $this->aggregate('min', $field);
- }
- /**
- * SUM查询
- * @access public
- * @param string $field 字段名
- * @return float
- */
- public function sum($field)
- {
- return $this->aggregate('sum', $field);
- }
- /**
- * AVG查询
- * @access public
- * @param string $field 字段名
- * @return float
- */
- public function avg($field)
- {
- return $this->aggregate('avg', $field);
- }
- /**
- * 设置记录的某个字段值
- * 支持使用数据库字段和方法
- * @access public
- * @param string|array $field 字段名
- * @param mixed $value 字段值
- * @return integer
- */
- public function setField($field, $value = '')
- {
- if (is_array($field)) {
- $data = $field;
- } else {
- $data[$field] = $value;
- }
- return $this->update($data);
- }
- /**
- * 字段值(延迟)增长
- * @access public
- * @param string $field 字段名
- * @param integer $step 增长值
- * @param integer $lazyTime 延时时间(s)
- * @return integer|true
- * @throws Exception
- */
- public function setInc($field, $step = 1, $lazyTime = 0)
- {
- $condition = !empty($this->options['where']) ? $this->options['where'] : [];
- if (empty($condition)) {
- // 没有条件不做任何更新
- throw new Exception('no data to update');
- }
- if ($lazyTime > 0) {
- // 延迟写入
- $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
- $step = $this->lazyWrite($guid, $step, $lazyTime);
- if (empty($step)) {
- return true; // 等待下次写入
- }
- }
- return $this->setField($field, ['$inc', $step]);
- }
- /**
- * 字段值(延迟)减少
- * @access public
- * @param string $field 字段名
- * @param integer $step 减少值
- * @param integer $lazyTime 延时时间(s)
- * @return integer|true
- * @throws Exception
- */
- public function setDec($field, $step = 1, $lazyTime = 0)
- {
- $condition = !empty($this->options['where']) ? $this->options['where'] : [];
- if (empty($condition)) {
- // 没有条件不做任何更新
- throw new Exception('no data to update');
- }
- if ($lazyTime > 0) {
- // 延迟写入
- $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
- $step = $this->lazyWrite($guid, -$step, $lazyTime);
- if (empty($step)) {
- return true; // 等待下次写入
- }
- }
- return $this->setField($field, ['$inc', -1 * $step]);
- }
- /**
- * 延时更新检查 返回false表示需要延时
- * 否则返回实际写入的数值
- * @access public
- * @param string $guid 写入标识
- * @param integer $step 写入步进值
- * @param integer $lazyTime 延时时间(s)
- * @return false|integer
- */
- protected function lazyWrite($guid, $step, $lazyTime)
- {
- if (false !== ($value = Cache::get($guid))) {
- // 存在缓存写入数据
- if ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) {
- // 延时更新时间到了,删除缓存数据 并实际写入数据库
- Cache::rm($guid);
- Cache::rm($guid . '_time');
- return $value + $step;
- } else {
- // 追加数据到缓存
- Cache::set($guid, $value + $step, 0);
- return false;
- }
- } else {
- // 没有缓存数据
- Cache::set($guid, $step, 0);
- // 计时开始
- Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0);
- return false;
- }
- }
- /**
- * 设置数据
- * @access public
- * @param mixed $field 字段名或者数据
- * @param mixed $value 字段值
- * @return $this
- */
- public function data($field, $value = null)
- {
- if (is_array($field)) {
- $this->options['data'] = isset($this->options['data']) ? array_merge($this->options['data'], $field) : $field;
- } else {
- $this->options['data'][$field] = $value;
- }
- return $this;
- }
- /**
- * 字段值增长
- * @access public
- * @param string|array $field 字段名
- * @param integer $step 增长值
- * @return $this
- */
- public function inc($field, $step = 1)
- {
- $fields = is_string($field) ? explode(',', $field) : $field;
- foreach ($fields as $field) {
- $this->data($field, ['$inc', $step]);
- }
- return $this;
- }
- /**
- * 字段值减少
- * @access public
- * @param string|array $field 字段名
- * @param integer $step 减少值
- * @return $this
- */
- public function dec($field, $step = 1)
- {
- $fields = is_string($field) ? explode(',', $field) : $field;
- foreach ($fields as $field) {
- $this->data($field, ['$inc', -1 * $step]);
- }
- return $this;
- }
- /**
- * 指定AND查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $op 查询表达式
- * @param mixed $condition 查询条件
- * @return $this
- */
- public function where($field, $op = null, $condition = null)
- {
- $param = func_get_args();
- array_shift($param);
- $this->parseWhereExp('and', $field, $op, $condition, $param);
- return $this;
- }
- /**
- * 指定OR查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $op 查询表达式
- * @param mixed $condition 查询条件
- * @return $this
- */
- public function whereOr($field, $op = null, $condition = null)
- {
- $param = func_get_args();
- array_shift($param);
- $this->parseWhereExp('or', $field, $op, $condition, $param);
- return $this;
- }
- /**
- * 指定NOR查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $op 查询表达式
- * @param mixed $condition 查询条件
- * @return $this
- */
- public function whereNor($field, $op = null, $condition = null)
- {
- $param = func_get_args();
- array_shift($param);
- $this->parseWhereExp('nor', $field, $op, $condition, $param);
- return $this;
- }
- /**
- * 指定Null查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereNull($field, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'null', null);
- return $this;
- }
- /**
- * 指定NotNull查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereNotNull($field, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'notnull', null);
- return $this;
- }
- /**
- * 指定In查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereIn($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'in', $condition);
- return $this;
- }
- /**
- * 指定NotIn查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereNotIn($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'not in', $condition);
- return $this;
- }
- /**
- * 指定Like查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereLike($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'like', $condition);
- return $this;
- }
- /**
- * 指定Between查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereBetween($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'between', $condition);
- return $this;
- }
- /**
- * 指定NotBetween查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereNotBetween($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'not between', $condition);
- return $this;
- }
- /**
- * 指定Exp查询条件
- * @access public
- * @param mixed $field 查询字段
- * @param mixed $condition 查询条件
- * @param string $logic 查询逻辑 and or xor
- * @return $this
- */
- public function whereExp($field, $condition, $logic = 'and')
- {
- $this->parseWhereExp($logic, $field, 'exp', $condition);
- return $this;
- }
- /**
- * 分析查询表达式
- * @access public
- * @param string $logic 查询逻辑 and or xor
- * @param string|array|\Closure $field 查询字段
- * @param mixed $op 查询表达式
- * @param mixed $condition 查询条件
- * @param array $param 查询参数
- * @return void
- */
- protected function parseWhereExp($logic, $field, $op, $condition, $param = [])
- {
- $logic = '$' . strtolower($logic);
- if ($field instanceof \Closure) {
- $this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field;
- return;
- }
- $where = [];
- if (is_null($op) && is_null($condition)) {
- if (is_array($field)) {
- // 数组批量查询
- $where = $field;
- } elseif ($field) {
- // 字符串查询
- $where[] = ['exp', $field];
- } else {
- $where = '';
- }
- } elseif (is_array($op)) {
- $where[$field] = $param;
- } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {
- // null查询
- $where[$field] = [$op, ''];
- } elseif (is_null($condition)) {
- // 字段相等查询
- $where[$field] = ['=', $op];
- } else {
- $where[$field] = [$op, $condition];
- }
- if (!empty($where)) {
- if (!isset($this->options['where'][$logic])) {
- $this->options['where'][$logic] = [];
- }
- $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where);
- }
- }
- /**
- * 查询日期或者时间
- * @access public
- * @param string $field 日期字段名
- * @param string $op 比较运算符或者表达式
- * @param string|array $range 比较范围
- * @return $this
- */
- public function whereTime($field, $op, $range = null)
- {
- if (is_null($range)) {
- // 使用日期表达式
- $date = getdate();
- switch (strtolower($op)) {
- case 'today':
- case 'd':
- $range = ['today', 'tomorrow'];
- break;
- case 'week':
- case 'w':
- $range = 'this week 00:00:00';
- break;
- case 'month':
- case 'm':
- $range = mktime(0, 0, 0, $date['mon'], 1, $date['year']);
- break;
- case 'year':
- case 'y':
- $range = mktime(0, 0, 0, 1, 1, $date['year']);
- break;
- case 'yesterday':
- $range = ['yesterday', 'today'];
- break;
- case 'last week':
- $range = ['last week 00:00:00', 'this week 00:00:00'];
- break;
- case 'last month':
- $range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])];
- break;
- case 'last year':
- $range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])];
- break;
- default:
- $range = $op;
- }
- $op = is_array($range) ? 'between' : '>';
- }
- $this->where($field, strtolower($op) . ' time', $range);
- return $this;
- }
- /**
- * 分页查询
- * @param int|null $listRows 每页数量
- * @param bool $simple 简洁模式
- * @param array $config 配置参数
- * page:当前页,
- * path:url路径,
- * query:url额外参数,
- * fragment:url锚点,
- * var_page:分页变量,
- * list_rows:每页数量
- * type:分页类名,
- * namespace:分页类命名空间
- * @return \think\Paginator
- * @throws DbException
- */
- public function paginate($listRows = null, $simple = false, $config = [])
- {
- $config = array_merge(Config::get('paginate'), $config);
- $listRows = $listRows ?: $config['list_rows'];
- $class = strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
- $page = isset($config['page']) ? (int) $config['page'] : call_user_func([
- $class,
- 'getCurrentPage',
- ], $config['var_page']);
- $page = $page < 1 ? 1 : $page;
- $config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);
- /** @var Paginator $paginator */
- if (!$simple) {
- $options = $this->getOptions();
- $total = $this->count();
- $results = $this->options($options)->page($page, $listRows)->select();
- } else {
- $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
- $total = null;
- }
- return $class::make($results, $listRows, $page, $total, $simple, $config);
- }
- /**
- * 指定当前操作的数据表
- * @access public
- * @param string $table 表名
- * @return $this
- */
- public function table($table)
- {
- $this->options['table'] = $table;
- return $this;
- }
- /**
- * 指定当前操作的collection
- * @access public
- * @param string $collection
- * @return $this
- */
- public function collection($collection)
- {
- return $this->table($collection);
- }
- /**
- * 查询缓存
- * @access public
- * @param mixed $key 缓存key
- * @param integer $expire 缓存有效期
- * @param string $tag 缓存标签
- * @return $this
- */
- public function cache($key = true, $expire = null, $tag = null)
- {
- // 增加快捷调用方式 cache(10) 等同于 cache(true, 10)
- if (is_numeric($key) && is_null($expire)) {
- $expire = $key;
- $key = true;
- }
- if (false !== $key) {
- $this->options['cache'] = ['key' => $key, 'expire' => $expire, 'tag' => $tag];
- }
- return $this;
- }
- /**
- * 设置软删除字段及条件(暂无支持)
- * @access public
- * @param false|string $field 查询字段
- * @param mixed $condition 查询条件
- * @return $this
- */
- public function useSoftDelete($field, $condition = null)
- {
- }
- /**
- * 不主动获取数据集
- * @access public
- * @param bool $cursor 是否返回 Cursor 对象
- * @return $this
- */
- public function fetchCursor($cursor = true)
- {
- $this->options['fetch_cursor'] = $cursor;
- return $this;
- }
- /**
- * 设置typeMap
- * @access public
- * @param string|array $typeMap
- * @return $this
- */
- public function typeMap($typeMap)
- {
- $this->options['typeMap'] = $typeMap;
- return $this;
- }
- /**
- * 设置从主服务器读取数据
- * @access public
- * @return $this
- */
- public function master()
- {
- $this->options['master'] = true;
- return $this;
- }
- /**
- * 设置查询数据不存在是否抛出异常
- * @access public
- * @param bool $fail 是否严格检查字段
- * @return $this
- */
- public function failException($fail = true)
- {
- $this->options['fail'] = $fail;
- return $this;
- }
- /**
- * awaitData
- * @access public
- * @param bool $awaitData
- * @return $this
- */
- public function awaitData($awaitData)
- {
- $this->options['awaitData'] = $awaitData;
- return $this;
- }
- /**
- * batchSize
- * @access public
- * @param integer $batchSize
- * @return $this
- */
- public function batchSize($batchSize)
- {
- $this->options['batchSize'] = $batchSize;
- return $this;
- }
- /**
- * exhaust
- * @access public
- * @param bool $exhaust
- * @return $this
- */
- public function exhaust($exhaust)
- {
- $this->options['exhaust'] = $exhaust;
- return $this;
- }
- /**
- * 设置modifiers
- * @access public
- * @param array $modifiers
- * @return $this
- */
- public function modifiers($modifiers)
- {
- $this->options['modifiers'] = $modifiers;
- return $this;
- }
- /**
- * 设置noCursorTimeout
- * @access public
- * @param bool $noCursorTimeout
- * @return $this
- */
- public function noCursorTimeout($noCursorTimeout)
- {
- $this->options['noCursorTimeout'] = $noCursorTimeout;
- return $this;
- }
- /**
- * 设置oplogReplay
- * @access public
- * @param bool $oplogReplay
- * @return $this
- */
- public function oplogReplay($oplogReplay)
- {
- $this->options['oplogReplay'] = $oplogReplay;
- return $this;
- }
- /**
- * 设置partial
- * @access public
- * @param bool $partial
- * @return $this
- */
- public function partial($partial)
- {
- $this->options['partial'] = $partial;
- return $this;
- }
- /**
- * 查询注释
- * @access public
- * @param string $comment 注释
- * @return $this
- */
- public function comment($comment)
- {
- $this->options['comment'] = $comment;
- return $this;
- }
- /**
- * maxTimeMS
- * @access public
- * @param string $maxTimeMS
- * @return $this
- */
- public function maxTimeMS($maxTimeMS)
- {
- $this->options['maxTimeMS'] = $maxTimeMS;
- return $this;
- }
- /**
- * collation
- * @access public
- * @param array $collation
- * @return $this
- */
- public function collation($collation)
- {
- $this->options['collation'] = $collation;
- return $this;
- }
- /**
- * 设置返回字段
- * @access public
- * @param array $field
- * @param boolean $except 是否排除
- * @return $this
- */
- public function field($field, $except = false)
- {
- if (is_string($field)) {
- $field = array_map('trim', explode(',', $field));
- }
- $projection = [];
- foreach ($field as $key => $val) {
- if (is_numeric($key)) {
- $projection[$val] = $except ? 0 : 1;
- } else {
- $projection[$key] = $val;
- }
- }
- $this->options['projection'] = $projection;
- return $this;
- }
- /**
- * 设置skip
- * @access public
- * @param integer $skip
- * @return $this
- */
- public function skip($skip)
- {
- $this->options['skip'] = $skip;
- return $this;
- }
- /**
- * 设置slaveOk
- * @access public
- * @param bool $slaveOk
- * @return $this
- */
- public function slaveOk($slaveOk)
- {
- $this->options['slaveOk'] = $slaveOk;
- return $this;
- }
- /**
- * 关联预载入查询
- * @access public
- * @param mixed $with
- * @return $this
- */
- public function with($with)
- {
- $this->options['with'] = $with;
- return $this;
- }
- /**
- * 关联统计
- * @access public
- * @param string|array $relation 关联方法名
- * @return $this
- */
- public function withCount($relation)
- {
- $this->options['with_count'] = $relation;
- return $this;
- }
- /**
- * 指定查询数量
- * @access public
- * @param mixed $offset 起始位置
- * @param mixed $length 查询数量
- * @return $this
- */
- public function limit($offset, $length = null)
- {
- if (is_null($length)) {
- if (is_numeric($offset)) {
- $length = $offset;
- $offset = 0;
- } else {
- list($offset, $length) = explode(',', $offset);
- }
- }
- $this->options['skip'] = intval($offset);
- $this->options['limit'] = intval($length);
- return $this;
- }
- /**
- * 指定分页
- * @access public
- * @param mixed $page 页数
- * @param mixed $listRows 每页数量
- * @return $this
- */
- public function page($page, $listRows = null)
- {
- if (is_null($listRows) && strpos($page, ',')) {
- list($page, $listRows) = explode(',', $page);
- }
- $this->options['page'] = [intval($page), intval($listRows)];
- return $this;
- }
- /**
- * 设置sort
- * @access public
- * @param array|string|object $field
- * @param string $order
- * @return $this
- */
- public function order($field, $order = '')
- {
- if (is_array($field)) {
- $this->options['sort'] = $field;
- } else {
- $this->options['sort'][$field] = 'asc' == strtolower($order) ? 1 : -1;
- }
- return $this;
- }
- /**
- * 设置tailable
- * @access public
- * @param bool $tailable
- * @return $this
- */
- public function tailable($tailable)
- {
- $this->options['tailable'] = $tailable;
- return $this;
- }
- /**
- * 设置writeConcern对象
- * @access public
- * @param WriteConcern $writeConcern
- * @return $this
- */
- public function writeConcern($writeConcern)
- {
- $this->options['writeConcern'] = $writeConcern;
- return $this;
- }
- /**
- * 获取当前数据表的主键
- * @access public
- * @return string|array
- */
- public function getPk()
- {
- return !empty($this->pk) ? $this->pk : $this->getConfig('pk');
- }
- /**
- * 查询参数赋值
- * @access protected
- * @param array $options 表达式参数
- * @return $this
- */
- protected function options(array $options)
- {
- $this->options = $options;
- return $this;
- }
- /**
- * 获取当前的查询参数
- * @access public
- * @param string $name 参数名
- * @return mixed
- */
- public function getOptions($name = '')
- {
- return isset($this->options[$name]) ? $this->options[$name] : $this->options;
- }
- /**
- * 设置关联查询
- * @access public
- * @param string $relation 关联名称
- * @return $this
- */
- public function relation($relation)
- {
- $this->options['relation'] = $relation;
- return $this;
- }
- /**
- * 把主键值转换为查询条件 支持复合主键
- * @access public
- * @param array|string $data 主键数据
- * @param mixed $options 表达式参数
- * @return void
- * @throws Exception
- */
- protected function parsePkWhere($data, &$options)
- {
- $pk = $this->getPk();
- if (is_string($pk)) {
- // 根据主键查询
- if (is_array($data)) {
- $where[$pk] = isset($data[$pk]) ? $data[$pk] : ['in', $data];
- } else {
- $where[$pk] = strpos($data, ',') ? ['in', $data] : $data;
- }
- }
- if (!empty($where)) {
- if (isset($options['where']['$and'])) {
- $options['where']['$and'] = array_merge($options['where']['$and'], $where);
- } else {
- $options['where']['$and'] = $where;
- }
- }
- return;
- }
- /**
- * 插入记录
- * @access public
- * @param mixed $data 数据
- * @param boolean $replace 是否replace(目前无效)
- * @param boolean $getLastInsID 返回自增主键
- * @return WriteResult
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function insert(array $data, $replace = null, $getLastInsID = false)
- {
- if (empty($data)) {
- throw new Exception('miss data to insert');
- }
- // 分析查询表达式
- $options = $this->parseExpress();
- $data = array_merge($options['data'], $data);
- // 生成bulk对象
- $bulk = $this->builder->insert($data, $options);
- $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
- $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
- $result = $writeResult->getInsertedCount();
- if ($result) {
- $lastInsId = $this->getLastInsID();
- if ($lastInsId) {
- $pk = $this->getPk();
- $data[$pk] = $lastInsId;
- }
- $options['data'] = $data;
- $this->trigger('after_insert', $options);
- if ($getLastInsID) {
- return $lastInsId;
- }
- }
- return $result;
- }
- /**
- * 插入记录并获取自增ID
- * @access public
- * @param mixed $data 数据
- * @return integer
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function insertGetId(array $data)
- {
- return $this->insert($data, null, true);
- }
- /**
- * 批量插入记录
- * @access public
- * @param mixed $dataSet 数据集
- * @return integer
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function insertAll(array $dataSet)
- {
- // 分析查询表达式
- $options = $this->parseExpress();
- if (!is_array(reset($dataSet))) {
- return false;
- }
- // 生成bulkWrite对象
- $bulk = $this->builder->insertAll($dataSet, $options);
- $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
- $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
- return $writeResult->getInsertedCount();
- }
- /**
- * 更新记录
- * @access public
- * @param mixed $data 数据
- * @return int
- * @throws Exception
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function update(array $data)
- {
- $options = $this->parseExpress();
- $data = array_merge($options['data'], $data);
- if (isset($options['cache']) && is_string($options['cache']['key'])) {
- $key = $options['cache']['key'];
- }
- $pk = $this->getPk();
- if (empty($options['where'])) {
- // 如果存在主键数据 则自动作为更新条件
- if (is_string($pk) && isset($data[$pk])) {
- $where[$pk] = $data[$pk];
- $key = 'mongo:' . $options['table'] . '|' . $data[$pk];
- unset($data[$pk]);
- } elseif (is_array($pk)) {
- // 增加复合主键支持
- foreach ($pk as $field) {
- if (isset($data[$field])) {
- $where[$field] = $data[$field];
- } else {
- // 如果缺少复合主键数据则不执行
- throw new Exception('miss complex primary data');
- }
- unset($data[$field]);
- }
- }
- if (!isset($where)) {
- // 如果没有任何更新条件则不执行
- throw new Exception('miss update condition');
- } else {
- $options['where']['$and'] = $where;
- }
- } elseif (!isset($key) && is_string($pk) && isset($options['where']['$and'][$pk])) {
- $key = $this->getCacheKey($options['where']['$and'][$pk], $options);
- }
- // 生成bulkWrite对象
- $bulk = $this->builder->update($data, $options);
- $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
- $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
- // 检测缓存
- if (isset($key) && Cache::get($key)) {
- // 删除缓存
- Cache::rm($key);
- }
- $result = $writeResult->getModifiedCount();
- if ($result) {
- if (isset($where[$pk])) {
- $data[$pk] = $where[$pk];
- } elseif (is_string($pk) && isset($key) && strpos($key, '|')) {
- list($a, $val) = explode('|', $key);
- $data[$pk] = $val;
- }
- $options['data'] = $data;
- $this->trigger('after_update', $options);
- }
- return $result;
- }
- /**
- * 删除记录
- * @access public
- * @param array $data 表达式 true 表示强制删除
- * @return int
- * @throws Exception
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- * @throws BulkWriteException
- */
- public function delete($data = null)
- {
- // 分析查询表达式
- $options = $this->parseExpress();
- $pk = $this->getPk();
- if (!is_null($data) && true !== $data) {
- if (!is_array($data)) {
- // 缓存标识
- $key = 'mongo:' . $options['table'] . '|' . $data;
- }
- // AR模式分析主键条件
- $this->parsePkWhere($data, $options);
- } elseif (!isset($key) && is_string($pk) && isset($options['where']['$and'][$pk])) {
- $key = $this->getCacheKey($options['where']['$and'][$pk], $options);
- }
- if (true !== $data && empty($options['where'])) {
- // 如果不是强制删除且条件为空 不进行删除操作
- throw new Exception('delete without condition');
- }
- // 生成bulkWrite对象
- $bulk = $this->builder->delete($options);
- $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
- // 执行操作
- $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
- // 检测缓存
- if (isset($key) && Cache::get($key)) {
- // 删除缓存
- Cache::rm($key);
- }
- $result = $writeResult->getDeletedCount();
- if ($result) {
- if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) {
- list($a, $val) = explode('|', $key);
- $item[$pk] = $val;
- $data = $item;
- }
- $options['data'] = $data;
- $this->trigger('after_delete', $options);
- }
- return $result;
- }
- /**
- * 执行查询但只返回Cursor对象
- * @access public
- * @return Cursor
- */
- public function getCursor()
- {
- // 分析查询表达式
- $options = $this->parseExpress();
- // 生成MongoQuery对象
- $query = $this->builder->select($options);
- // 执行查询操作
- $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null;
- return $this->query($options['table'], $query, $readPreference, true, $options['typeMap']);
- }
- /**
- * 查找记录
- * @access public
- * @param array|string|Query|\Closure $data
- * @return Collection|false|Cursor|string
- * @throws ModelNotFoundException
- * @throws DataNotFoundException
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function select($data = null)
- {
- if ($data instanceof Query) {
- return $data->select();
- } elseif ($data instanceof \Closure) {
- call_user_func_array($data, [ & $this]);
- $data = null;
- }
- // 分析查询表达式
- $options = $this->parseExpress();
- if (!is_null($data)) {
- // 主键条件分析
- $this->parsePkWhere($data, $options);
- }
- $resultSet = false;
- if (!empty($options['cache'])) {
- // 判断查询缓存
- $cache = $options['cache'];
- $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
- $resultSet = Cache::get($key);
- }
- if (!$resultSet) {
- // 生成MongoQuery对象
- $query = $this->builder->select($options);
- $options['data'] = $data;
- if ($resultSet = $this->trigger('before_select', $options)) {
- } else {
- // 执行查询操作
- $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null;
- $resultSet = $this->query($options['table'], $query, $readPreference, $options['fetch_cursor'], $options['typeMap']);
- if ($resultSet instanceof Cursor) {
- // 返回MongoDB\Driver\Cursor对象
- return $resultSet;
- }
- }
- if (isset($cache)) {
- // 缓存数据集
- $this->cacheData($key, $resultSet, $cache);
- }
- }
- // 数据列表读取后的处理
- if (!empty($this->model)) {
- // 生成模型对象
- $modelName = $this->model;
- if (count($resultSet) > 0) {
- foreach ($resultSet as $key => $result) {
- /** @var Model $result */
- $model = new $modelName($result);
- $model->isUpdate(true);
- // 关联查询
- if (!empty($options['relation'])) {
- $model->relationQuery($options['relation']);
- }
- // 关联统计
- if (!empty($options['with_count'])) {
- $model->relationCount($model, $options['with_count']);
- }
- $resultSet[$key] = $model;
- }
- if (!empty($options['with'])) {
- // 预载入
- $model->eagerlyResultSet($resultSet, $options['with']);
- }
- // 模型数据集转换
- $resultSet = $model->toCollection($resultSet);
- } else {
- $resultSet = (new $modelName)->toCollection($resultSet);
- }
- } elseif ('collection' == $this->connection->getConfig('resultset_type')) {
- // 返回Collection对象
- $resultSet = new Collection($resultSet);
- }
- if (!empty($options['fail']) && count($resultSet) == 0) {
- $this->throwNotFound($options);
- }
- return $resultSet;
- }
- /**
- * 缓存数据
- * @access public
- * @param string $key 缓存标识
- * @param mixed $data 缓存数据
- * @param array $config 缓存参数
- */
- protected function cacheData($key, $data, $config = [])
- {
- if (isset($config['tag'])) {
- Cache::tag($config['tag'])->set($key, $data, $config['expire']);
- } else {
- Cache::set($key, $data, $config['expire']);
- }
- }
- /**
- * 生成缓存标识
- * @access public
- * @param mixed $value 缓存数据
- * @param array $options 缓存参数
- */
- protected function getCacheKey($value, $options)
- {
- if (is_scalar($value)) {
- $data = $value;
- } elseif (is_array($value) && '=' == $value[0]) {
- $data = $value[1];
- }
- if (isset($data)) {
- return 'mongo:' . $options['table'] . '|' . $data;
- }
- }
- /**
- * 查找单条记录
- * @access public
- * @param array|string|Query|\Closure $data
- * @return array|null|Cursor|string|Model
- * @throws ModelNotFoundException
- * @throws DataNotFoundException
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function find($data = null)
- {
- if ($data instanceof Query) {
- return $data->find();
- } elseif ($data instanceof \Closure) {
- call_user_func_array($data, [ & $this]);
- $data = null;
- }
- // 分析查询表达式
- $options = $this->parseExpress();
- $pk = $this->getPk();
- if (!is_null($data)) {
- // AR模式分析主键条件
- $this->parsePkWhere($data, $options);
- } elseif (!empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['$and'][$pk])) {
- $key = $this->getCacheKey($options['where']['$and'][$pk], $options);
- }
- $options['limit'] = 1;
- $result = false;
- if (!empty($options['cache'])) {
- // 判断查询缓存
- $cache = $options['cache'];
- if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
- $key = 'mongo:' . $options['table'] . '|' . $data;
- } elseif (!isset($key)) {
- $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
- }
- $result = Cache::get($key);
- }
- if (false === $result) {
- // 生成查询SQL
- $query = $this->builder->select($options);
- if (is_string($pk)) {
- if (!is_array($data)) {
- if (isset($key) && strpos($key, '|')) {
- list($a, $val) = explode('|', $key);
- $item[$pk] = $val;
- } else {
- $item[$pk] = $data;
- }
- $data = $item;
- }
- }
- $options['data'] = $data;
- // 事件回调
- if ($result = $this->trigger('before_find', $options)) {
- } else {
- // 执行查询
- $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null;
- $resultSet = $this->query($options['table'], $query, $readPreference, $options['fetch_cursor'], $options['typeMap']);
- if ($resultSet instanceof Cursor) {
- // 返回MongoDB\Driver\Cursor对象
- return $resultSet;
- }
- $result = isset($resultSet[0]) ? $resultSet[0] : null;
- }
- if (isset($cache)) {
- // 缓存数据
- $this->cacheData($key, $result, $cache);
- }
- }
- // 数据处理
- if (!empty($result)) {
- if (!empty($this->model)) {
- // 返回模型对象
- $model = $this->model;
- $result = new $model($result);
- $result->isUpdate(true, isset($options['where']['$and']) ? $options['where']['$and'] : null);
- // 关联查询
- if (!empty($options['relation'])) {
- $result->relationQuery($options['relation']);
- }
- if (!empty($options['with'])) {
- // 预载入
- $result->eagerlyResult($result, $options['with']);
- }
- // 关联统计
- if (!empty($options['with_count'])) {
- $result->relationCount($result, $options['with_count']);
- }
- }
- } elseif (!empty($options['fail'])) {
- $this->throwNotFound($options);
- }
- return $result;
- }
- /**
- * 查询失败 抛出异常
- * @access public
- * @param array $options 查询参数
- * @throws ModelNotFoundException
- * @throws DataNotFoundException
- */
- protected function throwNotFound($options = [])
- {
- if (!empty($this->model)) {
- throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options);
- } else {
- throw new DataNotFoundException('table data not Found:' . $options['table'], $options['table'], $options);
- }
- }
- /**
- * 查找多条记录 如果不存在则抛出异常
- * @access public
- * @param array|string|Query|\Closure $data
- * @return array|\PDOStatement|string|Model
- * @throws ModelNotFoundException
- * @throws DataNotFoundException
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function selectOrFail($data = null)
- {
- return $this->failException(true)->select($data);
- }
- /**
- * 查找单条记录 如果不存在则抛出异常
- * @access public
- * @param array|string|Query|\Closure $data
- * @return array|\PDOStatement|string|Model
- * @throws ModelNotFoundException
- * @throws DataNotFoundException
- * @throws AuthenticationException
- * @throws InvalidArgumentException
- * @throws ConnectionException
- * @throws RuntimeException
- */
- public function findOrFail($data = null)
- {
- return $this->failException(true)->find($data);
- }
- /**
- * 分批数据返回处理
- * @access public
- * @param integer $count 每次处理的数据数量
- * @param callable $callback 处理回调方法
- * @param string $column 分批处理的字段名
- * @return boolean
- */
- public function chunk($count, $callback, $column = null)
- {
- $column = $column ?: $this->getPk();
- $options = $this->getOptions();
- $resultSet = $this->limit($count)->order($column, 'asc')->select();
- while (!empty($resultSet)) {
- if (false === call_user_func($callback, $resultSet)) {
- return false;
- }
- $end = end($resultSet);
- $lastId = is_array($end) ? $end[$column] : $end->$column;
- $resultSet = $this->options($options)
- ->limit($count)
- ->where($column, '>', $lastId)
- ->order($column, 'asc')
- ->select();
- }
- return true;
- }
- /**
- * 获取数据表信息
- * @access public
- * @param string $tableName 数据表名 留空自动获取
- * @param string $fetch 获取信息类型 包括 fields type pk
- * @return mixed
- */
- public function getTableInfo($tableName = '', $fetch = '')
- {
- if (!$tableName) {
- $tableName = $this->getTable();
- }
- if (is_array($tableName)) {
- $tableName = key($tableName) ?: current($tableName);
- }
- if (strpos($tableName, ',')) {
- // 多表不获取字段信息
- return false;
- } else {
- $tableName = $this->parseSqlTable($tableName);
- }
- $guid = md5($tableName);
- if (!isset(self::$info[$guid])) {
- $result = $this->table($tableName)->find();
- if ($result instanceof Model) {
- $result = $result->toArray();
- } elseif (!$result) {
- $result = [];
- }
- $fields = array_keys($result);
- $type = [];
- foreach ($result as $key => $val) {
- // 记录字段类型
- $type[$key] = getType($val);
- if ('_id' == $key) {
- $pk = $key;
- }
- }
- if (!isset($pk)) {
- // 设置主键
- $pk = null;
- }
- $result = ['fields' => $fields, 'type' => $type, 'pk' => $pk];
- self::$info[$guid] = $result;
- }
- return $fetch ? self::$info[$guid][$fetch] : self::$info[$guid];
- }
- /**
- * 分析表达式(可用于查询或者写入操作)
- * @access protected
- * @return array
- */
- protected function parseExpress()
- {
- $options = $this->options;
- // 获取数据表
- if (empty($options['table'])) {
- $options['table'] = $this->getTable();
- }
- foreach (['where', 'data'] as $name) {
- if (!isset($options[$name])) {
- $options[$name] = [];
- }
- }
- $modifiers = empty($options['modifiers']) ? [] : $options['modifiers'];
- if (isset($options['comment'])) {
- $modifiers['$comment'] = $options['comment'];
- }
- if (isset($options['maxTimeMS'])) {
- $modifiers['$maxTimeMS'] = $options['maxTimeMS'];
- }
- if (!empty($modifiers)) {
- $options['modifiers'] = $modifiers;
- }
- if (!isset($options['projection']) || '*' == $options['projection']) {
- $options['projection'] = [];
- }
- if (!isset($options['typeMap'])) {
- $options['typeMap'] = $this->getConfig('type_map');
- }
- if (!isset($options['limit'])) {
- $options['limit'] = 0;
- }
- foreach (['master', 'fetch_cursor'] as $name) {
- if (!isset($options[$name])) {
- $options[$name] = false;
- }
- }
- if (isset($options['page'])) {
- // 根据页数计算limit
- list($page, $listRows) = $options['page'];
- $page = $page > 0 ? $page : 1;
- $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
- $offset = $listRows * ($page - 1);
- $options['skip'] = intval($offset);
- $options['limit'] = intval($listRows);
- }
- $this->options = [];
- return $options;
- }
- /**
- * 注册回调方法
- * @access public
- * @param string $event 事件名
- * @param callable $callback 回调方法
- * @return void
- */
- public static function event($event, $callback)
- {
- self::$event[$event] = $callback;
- }
- /**
- * 触发事件
- * @access protected
- * @param string $event 事件名
- * @param mixed $params 额外参数
- * @return bool
- */
- protected function trigger($event, $params = [])
- {
- $result = false;
- if (isset(self::$event[$event])) {
- $callback = self::$event[$event];
- $result = call_user_func_array($callback, [$params, $this]);
- }
- return $result;
- }
- }
|