Query.php 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace think\mongo;
  12. use MongoDB\BSON\ObjectID;
  13. use MongoDB\Driver\BulkWrite;
  14. use MongoDB\Driver\Command;
  15. use MongoDB\Driver\Cursor;
  16. use MongoDB\Driver\Exception\AuthenticationException;
  17. use MongoDB\Driver\Exception\BulkWriteException;
  18. use MongoDB\Driver\Exception\ConnectionException;
  19. use MongoDB\Driver\Exception\InvalidArgumentException;
  20. use MongoDB\Driver\Exception\RuntimeException;
  21. use MongoDB\Driver\Query as MongoQuery;
  22. use MongoDB\Driver\ReadPreference;
  23. use MongoDB\Driver\WriteConcern;
  24. use think\Cache;
  25. use think\Collection;
  26. use think\Config;
  27. use think\Db;
  28. use think\db\exception\DataNotFoundException;
  29. use think\db\exception\ModelNotFoundException;
  30. use think\Exception;
  31. use think\exception\DbException;
  32. use think\Loader;
  33. use think\Model;
  34. use think\mongo\Builder;
  35. use think\mongo\Connection;
  36. use think\Paginator;
  37. class Query
  38. {
  39. // 数据库Connection对象实例
  40. protected $connection;
  41. // 数据库驱动类型
  42. protected $driver;
  43. // 当前模型类名称
  44. protected $model;
  45. // 当前数据表名称(含前缀)
  46. protected $table = '';
  47. // 当前数据表名称(不含前缀)
  48. protected $name = '';
  49. // 当前数据表主键
  50. protected $pk;
  51. // 当前数据表前缀
  52. protected $prefix = '';
  53. // 查询参数
  54. protected $options = [];
  55. // 数据表信息
  56. protected static $info = [];
  57. /**
  58. * 架构函数
  59. * @access public
  60. * @param Connection $connection 数据库对象实例
  61. * @param string $model 模型名
  62. */
  63. public function __construct(Connection $connection = null, $model = '')
  64. {
  65. $this->connection = $connection ?: Db::connect([], true);
  66. $this->prefix = $this->connection->getConfig('prefix');
  67. $this->model = $model;
  68. $this->builder = new Builder($this->connection, $this);
  69. }
  70. /**
  71. * 利用__call方法实现一些特殊的Model方法
  72. * @access public
  73. * @param string $method 方法名称
  74. * @param array $args 调用参数
  75. * @return mixed
  76. * @throws DbException
  77. * @throws Exception
  78. */
  79. public function __call($method, $args)
  80. {
  81. if (strtolower(substr($method, 0, 5)) == 'getby') {
  82. // 根据某个字段获取记录
  83. $field = Loader::parseName(substr($method, 5));
  84. $where[$field] = $args[0];
  85. return $this->where($where)->find();
  86. } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
  87. // 根据某个字段获取记录的某个值
  88. $name = Loader::parseName(substr($method, 10));
  89. $where[$name] = $args[0];
  90. return $this->where($where)->value($args[1]);
  91. } else {
  92. throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
  93. }
  94. }
  95. /**
  96. * 获取当前的数据库Connection对象
  97. * @access public
  98. * @return Connection
  99. */
  100. public function getConnection()
  101. {
  102. return $this->connection;
  103. }
  104. /**
  105. * 切换当前的数据库连接
  106. * @access public
  107. * @param mixed $config
  108. * @return $this
  109. */
  110. public function connect($config)
  111. {
  112. $this->connection = Db::connect($config);
  113. return $this;
  114. }
  115. /**
  116. * 指定默认的数据表名(不含前缀)
  117. * @access public
  118. * @param string $name
  119. * @return $this
  120. */
  121. public function name($name)
  122. {
  123. $this->name = $name;
  124. return $this;
  125. }
  126. /**
  127. * 指定默认数据表名(含前缀)
  128. * @access public
  129. * @param string $table 表名
  130. * @return $this
  131. */
  132. public function setTable($table)
  133. {
  134. $this->table = $table;
  135. return $this;
  136. }
  137. /**
  138. * 得到当前或者指定名称的数据表
  139. * @access public
  140. * @param string $name
  141. * @return string
  142. */
  143. public function getTable($name = '')
  144. {
  145. if ($name || empty($this->table)) {
  146. $name = $name ?: $this->name;
  147. $tableName = $this->prefix;
  148. if ($name) {
  149. $tableName .= Loader::parseName($name);
  150. }
  151. } else {
  152. $tableName = $this->table;
  153. }
  154. return $tableName;
  155. }
  156. /**
  157. * 指定数据表主键
  158. * @access public
  159. * @param string $pk 主键
  160. * @return $this
  161. */
  162. public function pk($pk)
  163. {
  164. $this->pk = $pk;
  165. return $this;
  166. }
  167. /**
  168. * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写)
  169. * @access public
  170. * @param string $sql sql语句
  171. * @return string
  172. */
  173. public function parseSqlTable($sql)
  174. {
  175. if (false !== strpos($sql, '__')) {
  176. $prefix = $this->prefix;
  177. $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) {
  178. return $prefix . strtolower($match[1]);
  179. }, $sql);
  180. }
  181. return $sql;
  182. }
  183. /**
  184. * 执行查询 返回数据集
  185. * @access public
  186. * @param string $namespace
  187. * @param MongoQuery $query 查询对象
  188. * @param ReadPreference $readPreference readPreference
  189. * @param bool|string $class 指定返回的数据集对象
  190. * @param string|array $typeMap 指定返回的typeMap
  191. * @return mixed
  192. * @throws AuthenticationException
  193. * @throws InvalidArgumentException
  194. * @throws ConnectionException
  195. * @throws RuntimeException
  196. */
  197. public function query($namespace, MongoQuery $query, ReadPreference $readPreference = null, $class = false, $typeMap = null)
  198. {
  199. return $this->connection->query($namespace, $query, $readPreference, $class, $typeMap);
  200. }
  201. /**
  202. * 执行指令 返回数据集
  203. * @access public
  204. * @param Command $command 指令
  205. * @param string $dbName
  206. * @param ReadPreference $readPreference readPreference
  207. * @param bool|string $class 指定返回的数据集对象
  208. * @param string|array $typeMap 指定返回的typeMap
  209. * @return mixed
  210. * @throws AuthenticationException
  211. * @throws InvalidArgumentException
  212. * @throws ConnectionException
  213. * @throws RuntimeException
  214. */
  215. public function command(Command $command, $dbName = '', ReadPreference $readPreference = null, $class = false, $typeMap = null)
  216. {
  217. return $this->connection->command($command, $dbName, $readPreference, $class, $typeMap);
  218. }
  219. /**
  220. * 执行语句
  221. * @access public
  222. * @param string $namespace
  223. * @param BulkWrite $bulk
  224. * @param WriteConcern $writeConcern
  225. * @return int
  226. * @throws AuthenticationException
  227. * @throws InvalidArgumentException
  228. * @throws ConnectionException
  229. * @throws RuntimeException
  230. * @throws BulkWriteException
  231. */
  232. public function execute($namespace, BulkWrite $bulk, WriteConcern $writeConcern = null)
  233. {
  234. return $this->connection->execute($namespace, $bulk, $writeConcern);
  235. }
  236. /**
  237. * 获取最近插入的ID
  238. * @access public
  239. * @return string
  240. */
  241. public function getLastInsID()
  242. {
  243. $id = $this->builder->getLastInsID();
  244. if ($id instanceof ObjectID) {
  245. $id = $id->__toString();
  246. }
  247. return $id;
  248. }
  249. /**
  250. * 获取最近一次执行的指令
  251. * @access public
  252. * @return string
  253. */
  254. public function getLastSql()
  255. {
  256. return $this->connection->getQueryStr();
  257. }
  258. /**
  259. * 获取数据库的配置参数
  260. * @access public
  261. * @param string $name 参数名称
  262. * @return boolean
  263. */
  264. public function getConfig($name = '')
  265. {
  266. return $this->connection->getConfig($name);
  267. }
  268. /**
  269. * 得到某个字段的值
  270. * @access public
  271. * @param string $field 字段名
  272. * @param mixed $default 默认值
  273. * @return mixed
  274. */
  275. public function value($field, $default = null)
  276. {
  277. $result = null;
  278. if (!empty($this->options['cache'])) {
  279. // 判断查询缓存
  280. $cache = $this->options['cache'];
  281. if (empty($this->options['table'])) {
  282. $this->options['table'] = $this->getTable();
  283. }
  284. $key = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
  285. $result = Cache::get($key);
  286. }
  287. if (!$result) {
  288. if (isset($this->options['field'])) {
  289. unset($this->options['field']);
  290. }
  291. $cursor = $this->field($field)->fetchCursor(true)->find();
  292. $cursor->setTypeMap(['root' => 'array']);
  293. $resultSet = $cursor->toArray();
  294. $data = isset($resultSet[0]) ? $resultSet[0] : null;
  295. $result = $data[$field];
  296. if (isset($cache)) {
  297. // 缓存数据
  298. Cache::set($key, $result, $cache['expire']);
  299. }
  300. } else {
  301. // 清空查询条件
  302. $this->options = [];
  303. }
  304. return !is_null($result) ? $result : $default;
  305. }
  306. /**
  307. * 得到某个列的数组
  308. * @access public
  309. * @param string $field 字段名 多个字段用逗号分隔
  310. * @param string $key 索引
  311. * @return array
  312. */
  313. public function column($field, $key = '')
  314. {
  315. $result = false;
  316. if (!empty($this->options['cache'])) {
  317. // 判断查询缓存
  318. $cache = $this->options['cache'];
  319. if (empty($this->options['table'])) {
  320. $this->options['table'] = $this->getTable();
  321. }
  322. $guid = is_string($cache['key']) ? $cache['key'] : md5($field . serialize($this->options));
  323. $result = Cache::get($guid);
  324. }
  325. if (!$result) {
  326. if (isset($this->options['field'])) {
  327. unset($this->options['field']);
  328. }
  329. if ($key && '*' != $field) {
  330. $field = $key . ',' . $field;
  331. }
  332. $cursor = $this->field($field)->fetchCursor(true)->select();
  333. $cursor->setTypeMap(['root' => 'array']);
  334. $resultSet = $cursor->toArray();
  335. if ($resultSet) {
  336. $fields = array_keys($resultSet[0]);
  337. $count = count($fields);
  338. $key1 = array_shift($fields);
  339. $key2 = $fields ? array_shift($fields) : '';
  340. $key = $key ?: $key1;
  341. foreach ($resultSet as $val) {
  342. $name = $val[$key];
  343. if ($name instanceof ObjectID) {
  344. $name = $name->__toString();
  345. }
  346. if (2 == $count) {
  347. $result[$name] = $val[$key2];
  348. } elseif (1 == $count) {
  349. $result[$name] = $val[$key1];
  350. } else {
  351. $result[$name] = $val;
  352. }
  353. }
  354. } else {
  355. $result = [];
  356. }
  357. if (isset($cache) && isset($guid)) {
  358. // 缓存数据
  359. Cache::set($guid, $result, $cache['expire']);
  360. }
  361. } else {
  362. // 清空查询条件
  363. $this->options = [];
  364. }
  365. return $result;
  366. }
  367. /**
  368. * 执行command
  369. * @access public
  370. * @param string|array|object $command 指令
  371. * @param mixed $extra 额外参数
  372. * @param string $db 数据库名
  373. * @return array
  374. */
  375. public function cmd($command, $extra = null, $db = null)
  376. {
  377. if (is_array($command) || is_object($command)) {
  378. if ($this->connection->getConfig('debug')) {
  379. $this->connection->log('cmd', 'cmd', $command);
  380. }
  381. // 直接创建Command对象
  382. $command = new Command($command);
  383. } else {
  384. // 调用Builder封装的Command对象
  385. $options = $this->parseExpress();
  386. $command = $this->builder->$command($options, $extra);
  387. }
  388. return $this->command($command, $db);
  389. }
  390. /**
  391. * 指定distinct查询
  392. * @access public
  393. * @param string $field 字段名
  394. * @return array
  395. */
  396. public function distinct($field)
  397. {
  398. $result = $this->cmd('distinct', $field);
  399. return $result[0]['values'];
  400. }
  401. /**
  402. * 获取数据库的所有collection
  403. * @access public
  404. * @param string $db 数据库名称 留空为当前数据库
  405. * @throws Exception
  406. */
  407. public function listCollections($db = '')
  408. {
  409. $cursor = $this->cmd('listCollections', null, $db);
  410. $result = [];
  411. foreach ($cursor as $collection) {
  412. $result[] = $collection['name'];
  413. }
  414. return $result;
  415. }
  416. /**
  417. * COUNT查询
  418. * @access public
  419. * @return integer
  420. */
  421. public function count()
  422. {
  423. $result = $this->cmd('count');
  424. return $result[0]['n'];
  425. }
  426. /**
  427. * 设置记录的某个字段值
  428. * 支持使用数据库字段和方法
  429. * @access public
  430. * @param string|array $field 字段名
  431. * @param mixed $value 字段值
  432. * @return integer
  433. */
  434. public function setField($field, $value = '')
  435. {
  436. if (is_array($field)) {
  437. $data = $field;
  438. } else {
  439. $data[$field] = $value;
  440. }
  441. return $this->update($data);
  442. }
  443. /**
  444. * 字段值(延迟)增长
  445. * @access public
  446. * @param string $field 字段名
  447. * @param integer $step 增长值
  448. * @param integer $lazyTime 延时时间(s)
  449. * @return integer|true
  450. * @throws Exception
  451. */
  452. public function setInc($field, $step = 1, $lazyTime = 0)
  453. {
  454. $condition = !empty($this->options['where']) ? $this->options['where'] : [];
  455. if (empty($condition)) {
  456. // 没有条件不做任何更新
  457. throw new Exception('no data to update');
  458. }
  459. if ($lazyTime > 0) {
  460. // 延迟写入
  461. $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
  462. $step = $this->lazyWrite($guid, $step, $lazyTime);
  463. if (empty($step)) {
  464. return true; // 等待下次写入
  465. }
  466. }
  467. return $this->setField($field, ['$inc', $step]);
  468. }
  469. /**
  470. * 字段值(延迟)减少
  471. * @access public
  472. * @param string $field 字段名
  473. * @param integer $step 减少值
  474. * @param integer $lazyTime 延时时间(s)
  475. * @return integer|true
  476. * @throws Exception
  477. */
  478. public function setDec($field, $step = 1, $lazyTime = 0)
  479. {
  480. $condition = !empty($this->options['where']) ? $this->options['where'] : [];
  481. if (empty($condition)) {
  482. // 没有条件不做任何更新
  483. throw new Exception('no data to update');
  484. }
  485. if ($lazyTime > 0) {
  486. // 延迟写入
  487. $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition));
  488. $step = $this->lazyWrite($guid, -$step, $lazyTime);
  489. if (empty($step)) {
  490. return true; // 等待下次写入
  491. }
  492. }
  493. return $this->setField($field, ['$inc', -1 * $step]);
  494. }
  495. /**
  496. * 延时更新检查 返回false表示需要延时
  497. * 否则返回实际写入的数值
  498. * @access public
  499. * @param string $guid 写入标识
  500. * @param integer $step 写入步进值
  501. * @param integer $lazyTime 延时时间(s)
  502. * @return false|integer
  503. */
  504. protected function lazyWrite($guid, $step, $lazyTime)
  505. {
  506. if (false !== ($value = Cache::get($guid))) {
  507. // 存在缓存写入数据
  508. if ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) {
  509. // 延时更新时间到了,删除缓存数据 并实际写入数据库
  510. Cache::rm($guid);
  511. Cache::rm($guid . '_time');
  512. return $value + $step;
  513. } else {
  514. // 追加数据到缓存
  515. Cache::set($guid, $value + $step, 0);
  516. return false;
  517. }
  518. } else {
  519. // 没有缓存数据
  520. Cache::set($guid, $step, 0);
  521. // 计时开始
  522. Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0);
  523. return false;
  524. }
  525. }
  526. /**
  527. * 指定AND查询条件
  528. * @access public
  529. * @param mixed $field 查询字段
  530. * @param mixed $op 查询表达式
  531. * @param mixed $condition 查询条件
  532. * @return $this
  533. */
  534. public function where($field, $op = null, $condition = null)
  535. {
  536. $param = func_get_args();
  537. array_shift($param);
  538. $this->parseWhereExp('$and', $field, $op, $condition, $param);
  539. return $this;
  540. }
  541. /**
  542. * 指定OR查询条件
  543. * @access public
  544. * @param mixed $field 查询字段
  545. * @param mixed $op 查询表达式
  546. * @param mixed $condition 查询条件
  547. * @return $this
  548. */
  549. public function whereOr($field, $op = null, $condition = null)
  550. {
  551. $param = func_get_args();
  552. array_shift($param);
  553. $this->parseWhereExp('$or', $field, $op, $condition, $param);
  554. return $this;
  555. }
  556. /**
  557. * 指定NOR查询条件
  558. * @access public
  559. * @param mixed $field 查询字段
  560. * @param mixed $op 查询表达式
  561. * @param mixed $condition 查询条件
  562. * @return $this
  563. */
  564. public function whereNor($field, $op = null, $condition = null)
  565. {
  566. $param = func_get_args();
  567. array_shift($param);
  568. $this->parseWhereExp('$nor', $field, $op, $condition, $param);
  569. return $this;
  570. }
  571. /**
  572. * 分析查询表达式
  573. * @access public
  574. * @param string $logic 查询逻辑 and or xor
  575. * @param string|array|\Closure $field 查询字段
  576. * @param mixed $op 查询表达式
  577. * @param mixed $condition 查询条件
  578. * @param array $param 查询参数
  579. * @return void
  580. */
  581. protected function parseWhereExp($logic, $field, $op, $condition, $param = [])
  582. {
  583. if ($field instanceof \Closure) {
  584. $this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field;
  585. return;
  586. }
  587. $where = [];
  588. if (is_null($op) && is_null($condition)) {
  589. if (is_array($field)) {
  590. // 数组批量查询
  591. $where = $field;
  592. } elseif ($field) {
  593. // 字符串查询
  594. $where[] = ['exp', $field];
  595. } else {
  596. $where = '';
  597. }
  598. } elseif (is_array($op)) {
  599. $where[$field] = $param;
  600. } elseif (is_null($condition)) {
  601. // 字段相等查询
  602. $where[$field] = ['=', $op];
  603. } else {
  604. $where[$field] = [$op, $condition];
  605. }
  606. if (!empty($where)) {
  607. if (!isset($this->options['where'][$logic])) {
  608. $this->options['where'][$logic] = [];
  609. }
  610. $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where);
  611. }
  612. }
  613. /**
  614. * 查询日期或者时间
  615. * @access public
  616. * @param string $field 日期字段名
  617. * @param string $op 比较运算符或者表达式
  618. * @param string|array $range 比较范围
  619. * @return $this
  620. */
  621. public function whereTime($field, $op, $range = null)
  622. {
  623. if (is_null($range)) {
  624. // 使用日期表达式
  625. $date = getdate();
  626. switch (strtolower($op)) {
  627. case 'today':
  628. case 'd':
  629. $range = 'today';
  630. break;
  631. case 'week':
  632. case 'w':
  633. $range = 'this week 00:00:00';
  634. break;
  635. case 'month':
  636. case 'm':
  637. $range = mktime(0, 0, 0, $date['mon'], 1, $date['year']);
  638. break;
  639. case 'year':
  640. case 'y':
  641. $range = mktime(0, 0, 0, 1, 1, $date['year']);
  642. break;
  643. case 'yesterday':
  644. $range = ['yesterday', 'today'];
  645. break;
  646. case 'last week':
  647. $range = ['last week 00:00:00', 'this week 00:00:00'];
  648. break;
  649. case 'last month':
  650. $range = [date('y-m-01', strtotime('-1 month')), mktime(0, 0, 0, $date['mon'], 1, $date['year'])];
  651. break;
  652. case 'last year':
  653. $range = [mktime(0, 0, 0, 1, 1, $date['year'] - 1), mktime(0, 0, 0, 1, 1, $date['year'])];
  654. break;
  655. }
  656. $op = is_array($range) ? 'between' : '>';
  657. }
  658. $this->where($field, strtolower($op) . ' time', $range);
  659. return $this;
  660. }
  661. /**
  662. * 分页查询
  663. * @param int|null $listRows 每页数量
  664. * @param bool $simple 简洁模式
  665. * @param array $config 配置参数
  666. * page:当前页,
  667. * path:url路径,
  668. * query:url额外参数,
  669. * fragment:url锚点,
  670. * var_page:分页变量,
  671. * list_rows:每页数量
  672. * type:分页类名,
  673. * namespace:分页类命名空间
  674. * @return \think\paginator\Collection
  675. * @throws DbException
  676. */
  677. public function paginate($listRows = null, $simple = false, $config = [])
  678. {
  679. $config = array_merge(Config::get('paginate'), $config);
  680. $listRows = $listRows ?: $config['list_rows'];
  681. $class = strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
  682. $page = isset($config['page']) ? (int) $config['page'] : call_user_func([
  683. $class,
  684. 'getCurrentPage',
  685. ], $config['var_page']);
  686. $page = $page < 1 ? 1 : $page;
  687. $config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);
  688. /** @var Paginator $paginator */
  689. if (!$simple) {
  690. $options = $this->getOptions();
  691. $total = $this->count();
  692. $results = $this->options($options)->page($page, $listRows)->select();
  693. } else {
  694. $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
  695. $total = null;
  696. }
  697. return $class::make($results, $listRows, $page, $total, $simple, $config);
  698. }
  699. /**
  700. * 指定当前操作的数据表
  701. * @access public
  702. * @param string $table 表名
  703. * @return $this
  704. */
  705. public function table($table)
  706. {
  707. $this->options['table'] = $table;
  708. return $this;
  709. }
  710. /**
  711. * 查询缓存
  712. * @access public
  713. * @param mixed $key
  714. * @param integer $expire
  715. * @return $this
  716. */
  717. public function cache($key = true, $expire = null)
  718. {
  719. // 增加快捷调用方式 cache(10) 等同于 cache(true, 10)
  720. if (is_numeric($key) && is_null($expire)) {
  721. $expire = $key;
  722. $key = true;
  723. }
  724. if (false !== $key) {
  725. $this->options['cache'] = ['key' => $key, 'expire' => $expire];
  726. }
  727. return $this;
  728. }
  729. /**
  730. * 不主动获取数据集
  731. * @access public
  732. * @param bool $cursor 是否返回 Cursor 对象
  733. * @return $this
  734. */
  735. public function fetchCursor($cursor = true)
  736. {
  737. $this->options['fetch_class'] = $cursor;
  738. return $this;
  739. }
  740. /**
  741. * 指定数据集返回对象
  742. * @access public
  743. * @param string $class 指定返回的数据集对象类名
  744. * @return $this
  745. */
  746. public function fetchClass($class)
  747. {
  748. $this->options['fetch_class'] = $class;
  749. return $this;
  750. }
  751. /**
  752. * 设置typeMap
  753. * @access public
  754. * @param string|array $typeMap
  755. * @return $this
  756. */
  757. public function typeMap($typeMap)
  758. {
  759. $this->options['typeMap'] = $typeMap;
  760. return $this;
  761. }
  762. /**
  763. * 设置从主服务器读取数据
  764. * @access public
  765. * @return $this
  766. */
  767. public function master()
  768. {
  769. $this->options['master'] = true;
  770. return $this;
  771. }
  772. /**
  773. * 设置查询数据不存在是否抛出异常
  774. * @access public
  775. * @param bool $fail 是否严格检查字段
  776. * @return $this
  777. */
  778. public function failException($fail = true)
  779. {
  780. $this->options['fail'] = $fail;
  781. return $this;
  782. }
  783. /**
  784. * 设置查询数据不存在是否抛出异常
  785. * @access public
  786. * @param bool $awaitData
  787. * @return $this
  788. */
  789. public function awaitData($awaitData)
  790. {
  791. $this->options['awaitData'] = $awaitData;
  792. return $this;
  793. }
  794. /**
  795. * batchSize
  796. * @access public
  797. * @param integer $batchSize
  798. * @return $this
  799. */
  800. public function batchSize($batchSize)
  801. {
  802. $this->options['batchSize'] = $batchSize;
  803. return $this;
  804. }
  805. /**
  806. * exhaust
  807. * @access public
  808. * @param bool $exhaust
  809. * @return $this
  810. */
  811. public function exhaust($exhaust)
  812. {
  813. $this->options['exhaust'] = $exhaust;
  814. return $this;
  815. }
  816. /**
  817. * 设置modifiers
  818. * @access public
  819. * @param array $modifiers
  820. * @return $this
  821. */
  822. public function modifiers($modifiers)
  823. {
  824. $this->options['modifiers'] = $modifiers;
  825. return $this;
  826. }
  827. /**
  828. * 设置noCursorTimeout
  829. * @access public
  830. * @param bool $noCursorTimeout
  831. * @return $this
  832. */
  833. public function noCursorTimeout($noCursorTimeout)
  834. {
  835. $this->options['noCursorTimeout'] = $noCursorTimeout;
  836. return $this;
  837. }
  838. /**
  839. * 设置oplogReplay
  840. * @access public
  841. * @param bool $oplogReplay
  842. * @return $this
  843. */
  844. public function oplogReplay($oplogReplay)
  845. {
  846. $this->options['oplogReplay'] = $oplogReplay;
  847. return $this;
  848. }
  849. /**
  850. * 设置partial
  851. * @access public
  852. * @param bool $partial
  853. * @return $this
  854. */
  855. public function partial($partial)
  856. {
  857. $this->options['partial'] = $partial;
  858. return $this;
  859. }
  860. /**
  861. * 查询注释
  862. * @access public
  863. * @param string $comment 注释
  864. * @return $this
  865. */
  866. public function comment($comment)
  867. {
  868. $this->options['comment'] = $comment;
  869. return $this;
  870. }
  871. /**
  872. * maxTimeMS
  873. * @access public
  874. * @param string $maxTimeMS
  875. * @return $this
  876. */
  877. public function maxTimeMS($maxTimeMS)
  878. {
  879. $this->options['maxTimeMS'] = $maxTimeMS;
  880. return $this;
  881. }
  882. /**
  883. * 设置返回字段
  884. * @access public
  885. * @param array $field
  886. * @param boolean $except 是否排除
  887. * @return $this
  888. */
  889. public function field($field, $except = false)
  890. {
  891. if (is_string($field)) {
  892. $field = array_map('trim', explode(',', $field));
  893. }
  894. $projection = [];
  895. foreach ($field as $key => $val) {
  896. if (is_numeric($key)) {
  897. $projection[$val] = $except ? 0 : 1;
  898. } else {
  899. $projection[$key] = $val;
  900. }
  901. }
  902. $this->options['projection'] = $projection;
  903. return $this;
  904. }
  905. /**
  906. * 设置skip
  907. * @access public
  908. * @param integer $skip
  909. * @return $this
  910. */
  911. public function skip($skip)
  912. {
  913. $this->options['skip'] = $skip;
  914. return $this;
  915. }
  916. /**
  917. * 设置slaveOk
  918. * @access public
  919. * @param bool $slaveOk
  920. * @return $this
  921. */
  922. public function slaveOk($slaveOk)
  923. {
  924. $this->options['slaveOk'] = $slaveOk;
  925. return $this;
  926. }
  927. /**
  928. * 关联预载入查询
  929. * @access public
  930. * @param mixed $with
  931. * @return $this
  932. */
  933. public function with($with)
  934. {
  935. return $this;
  936. }
  937. /**
  938. * 指定查询数量
  939. * @access public
  940. * @param mixed $offset 起始位置
  941. * @param mixed $length 查询数量
  942. * @return $this
  943. */
  944. public function limit($offset, $length = null)
  945. {
  946. if (is_null($length)) {
  947. if (is_numeric($offset)) {
  948. $length = $offset;
  949. $offset = 0;
  950. } else {
  951. list($offset, $length) = explode(',', $offset);
  952. }
  953. }
  954. $this->options['skip'] = intval($offset);
  955. $this->options['limit'] = intval($length);
  956. return $this;
  957. }
  958. /**
  959. * 指定分页
  960. * @access public
  961. * @param mixed $page 页数
  962. * @param mixed $listRows 每页数量
  963. * @return $this
  964. */
  965. public function page($page, $listRows = null)
  966. {
  967. if (is_null($listRows) && strpos($page, ',')) {
  968. list($page, $listRows) = explode(',', $page);
  969. }
  970. $this->options['page'] = [intval($page), intval($listRows)];
  971. return $this;
  972. }
  973. /**
  974. * 设置sort
  975. * @access public
  976. * @param array|string|object $field
  977. * @param string $order
  978. * @return $this
  979. */
  980. public function order($field, $order = '')
  981. {
  982. if (is_array($field)) {
  983. $this->options['sort'] = $field;
  984. } else {
  985. $this->options['sort'][$field] = 'asc' == strtolower($order) ? 1 : -1;
  986. }
  987. return $this;
  988. }
  989. /**
  990. * 设置tailable
  991. * @access public
  992. * @param bool $tailable
  993. * @return $this
  994. */
  995. public function tailable($tailable)
  996. {
  997. $this->options['tailable'] = $tailable;
  998. return $this;
  999. }
  1000. /**
  1001. * 设置writeConcern对象
  1002. * @access public
  1003. * @param WriteConcern $writeConcern
  1004. * @return $this
  1005. */
  1006. public function writeConcern($writeConcern)
  1007. {
  1008. $this->options['writeConcern'] = $writeConcern;
  1009. return $this;
  1010. }
  1011. /**
  1012. * 获取当前数据表的主键
  1013. * @access public
  1014. * @return string|array
  1015. */
  1016. public function getPk()
  1017. {
  1018. return !empty($this->pk) ? $this->pk : $this->getConfig('pk');
  1019. }
  1020. /**
  1021. * 查询参数赋值
  1022. * @access protected
  1023. * @param array $options 表达式参数
  1024. * @return $this
  1025. */
  1026. protected function options(array $options)
  1027. {
  1028. $this->options = $options;
  1029. return $this;
  1030. }
  1031. /**
  1032. * 获取当前的查询参数
  1033. * @access public
  1034. * @param string $name 参数名
  1035. * @return mixed
  1036. */
  1037. public function getOptions($name = '')
  1038. {
  1039. return isset($this->options[$name]) ? $this->options[$name] : $this->options;
  1040. }
  1041. /**
  1042. * 设置关联查询
  1043. * @access public
  1044. * @param string $relation 关联名称
  1045. * @return $this
  1046. */
  1047. public function relation($relation)
  1048. {
  1049. $this->options['relation'] = $relation;
  1050. return $this;
  1051. }
  1052. /**
  1053. * 把主键值转换为查询条件 支持复合主键
  1054. * @access public
  1055. * @param array|string $data 主键数据
  1056. * @param mixed $options 表达式参数
  1057. * @return void
  1058. * @throws Exception
  1059. */
  1060. protected function parsePkWhere($data, &$options)
  1061. {
  1062. $pk = $this->getPk();
  1063. if (is_string($pk)) {
  1064. // 根据主键查询
  1065. if (is_array($data)) {
  1066. $where[$pk] = isset($data[$pk]) ? $data[$pk] : ['in', $data];
  1067. } else {
  1068. $where[$pk] = strpos($data, ',') ? ['in', $data] : $data;
  1069. }
  1070. }
  1071. if (!empty($where)) {
  1072. if (isset($options['where']['$and'])) {
  1073. $options['where']['$and'] = array_merge($options['where']['$and'], $where);
  1074. } else {
  1075. $options['where']['$and'] = $where;
  1076. }
  1077. }
  1078. return;
  1079. }
  1080. /**
  1081. * 插入记录
  1082. * @access public
  1083. * @param mixed $data 数据
  1084. * @return WriteResult
  1085. * @throws AuthenticationException
  1086. * @throws InvalidArgumentException
  1087. * @throws ConnectionException
  1088. * @throws RuntimeException
  1089. * @throws BulkWriteException
  1090. */
  1091. public function insert(array $data)
  1092. {
  1093. if (empty($data)) {
  1094. throw new Exception('miss data to insert');
  1095. }
  1096. // 分析查询表达式
  1097. $options = $this->parseExpress();
  1098. // 生成bulk对象
  1099. $bulk = $this->builder->insert($data, $options);
  1100. $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
  1101. $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
  1102. return $writeResult->getInsertedCount();
  1103. }
  1104. /**
  1105. * 插入记录并获取自增ID
  1106. * @access public
  1107. * @param mixed $data 数据
  1108. * @return integer
  1109. * @throws AuthenticationException
  1110. * @throws InvalidArgumentException
  1111. * @throws ConnectionException
  1112. * @throws RuntimeException
  1113. * @throws BulkWriteException
  1114. */
  1115. public function insertGetId(array $data)
  1116. {
  1117. $this->insert($data);
  1118. return $this->getLastInsID();
  1119. }
  1120. /**
  1121. * 批量插入记录
  1122. * @access public
  1123. * @param mixed $dataSet 数据集
  1124. * @return integer
  1125. * @throws AuthenticationException
  1126. * @throws InvalidArgumentException
  1127. * @throws ConnectionException
  1128. * @throws RuntimeException
  1129. * @throws BulkWriteException
  1130. */
  1131. public function insertAll(array $dataSet)
  1132. {
  1133. // 分析查询表达式
  1134. $options = $this->parseExpress();
  1135. if (!is_array(reset($dataSet))) {
  1136. return false;
  1137. }
  1138. // 生成bulkWrite对象
  1139. $bulk = $this->builder->insertAll($dataSet, $options);
  1140. $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
  1141. $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
  1142. return $writeResult->getInsertedCount();
  1143. }
  1144. /**
  1145. * 更新记录
  1146. * @access public
  1147. * @param mixed $data 数据
  1148. * @return int
  1149. * @throws Exception
  1150. * @throws AuthenticationException
  1151. * @throws InvalidArgumentException
  1152. * @throws ConnectionException
  1153. * @throws RuntimeException
  1154. * @throws BulkWriteException
  1155. */
  1156. public function update(array $data)
  1157. {
  1158. $options = $this->parseExpress();
  1159. if (empty($options['where'])) {
  1160. $pk = $this->getPk();
  1161. // 如果存在主键数据 则自动作为更新条件
  1162. if (is_string($pk) && isset($data[$pk])) {
  1163. $where[$pk] = $data[$pk];
  1164. $key = 'mongo:' . $options['table'] . '|' . $data[$pk];
  1165. unset($data[$pk]);
  1166. } elseif (is_array($pk)) {
  1167. // 增加复合主键支持
  1168. foreach ($pk as $field) {
  1169. if (isset($data[$field])) {
  1170. $where[$field] = $data[$field];
  1171. } else {
  1172. // 如果缺少复合主键数据则不执行
  1173. throw new Exception('miss complex primary data');
  1174. }
  1175. unset($data[$field]);
  1176. }
  1177. }
  1178. if (!isset($where)) {
  1179. // 如果没有任何更新条件则不执行
  1180. throw new Exception('miss update condition');
  1181. } else {
  1182. $options['where']['$and'] = $where;
  1183. }
  1184. }
  1185. // 生成bulkWrite对象
  1186. $bulk = $this->builder->update($data, $options);
  1187. $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
  1188. $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
  1189. // 检测缓存
  1190. if (isset($key) && Cache::get($key)) {
  1191. // 删除缓存
  1192. Cache::rm($key);
  1193. }
  1194. return $writeResult->getModifiedCount();
  1195. }
  1196. /**
  1197. * 删除记录
  1198. * @access public
  1199. * @param array $data 表达式 true 表示强制删除
  1200. * @return int
  1201. * @throws Exception
  1202. * @throws AuthenticationException
  1203. * @throws InvalidArgumentException
  1204. * @throws ConnectionException
  1205. * @throws RuntimeException
  1206. * @throws BulkWriteException
  1207. */
  1208. public function delete($data = null)
  1209. {
  1210. // 分析查询表达式
  1211. $options = $this->parseExpress();
  1212. if (!is_null($data) && true !== $data) {
  1213. if (!is_array($data)) {
  1214. // 缓存标识
  1215. $key = 'mongo:' . $options['table'] . '|' . $data;
  1216. }
  1217. // AR模式分析主键条件
  1218. $this->parsePkWhere($data, $options);
  1219. }
  1220. if (true !== $data && empty($options['where'])) {
  1221. // 如果不是强制删除且条件为空 不进行删除操作
  1222. throw new Exception('delete without condition');
  1223. }
  1224. // 生成bulkWrite对象
  1225. $bulk = $this->builder->delete($options);
  1226. $writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : null;
  1227. // 执行操作
  1228. $writeResult = $this->execute($options['table'], $bulk, $writeConcern);
  1229. // 检测缓存
  1230. if (isset($key) && Cache::get($key)) {
  1231. // 删除缓存
  1232. Cache::rm($key);
  1233. }
  1234. return $writeResult->getDeletedCount();
  1235. }
  1236. /**
  1237. * 查找记录
  1238. * @access public
  1239. * @param array|string|Query|\Closure $data
  1240. * @return Collection|false|Cursor|string
  1241. * @throws ModelNotFoundException
  1242. * @throws DataNotFoundException
  1243. * @throws AuthenticationException
  1244. * @throws InvalidArgumentException
  1245. * @throws ConnectionException
  1246. * @throws RuntimeException
  1247. */
  1248. public function select($data = null)
  1249. {
  1250. if ($data instanceof Query) {
  1251. return $data->select();
  1252. } elseif ($data instanceof \Closure) {
  1253. call_user_func_array($data, [ & $this]);
  1254. $data = null;
  1255. }
  1256. // 分析查询表达式
  1257. $options = $this->parseExpress();
  1258. if (!is_null($data)) {
  1259. // 主键条件分析
  1260. $this->parsePkWhere($data, $options);
  1261. }
  1262. $resultSet = false;
  1263. if (!empty($options['cache'])) {
  1264. // 判断查询缓存
  1265. $cache = $options['cache'];
  1266. $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
  1267. $resultSet = Cache::get($key);
  1268. }
  1269. if (!$resultSet) {
  1270. // 生成MongoQuery对象
  1271. $query = $this->builder->select($options);
  1272. // 执行查询操作
  1273. $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null;
  1274. $resultSet = $this->query($options['table'], $query, $readPreference, $options['fetch_class'], $options['typeMap']);
  1275. if ($resultSet instanceof Cursor) {
  1276. // 返回MongoDB\Driver\Cursor对象
  1277. return $resultSet;
  1278. }
  1279. if (isset($cache)) {
  1280. // 缓存数据集
  1281. Cache::set($key, $resultSet, $cache['expire']);
  1282. }
  1283. }
  1284. // 返回结果处理
  1285. if ($resultSet) {
  1286. // 数据列表读取后的处理
  1287. if (!empty($this->model)) {
  1288. // 生成模型对象
  1289. $model = $this->model;
  1290. foreach ($resultSet as $key => $result) {
  1291. /** @var Model $result */
  1292. $result = new $model($result);
  1293. $result->isUpdate(true);
  1294. // 关联查询
  1295. if (!empty($options['relation'])) {
  1296. $result->relationQuery($options['relation']);
  1297. }
  1298. $resultSet[$key] = $result;
  1299. }
  1300. if (!empty($options['with'])) {
  1301. // 预载入
  1302. $resultSet = $result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : '');
  1303. }
  1304. }
  1305. } elseif (!empty($options['fail'])) {
  1306. $this->throwNotFound($options);
  1307. }
  1308. return $resultSet;
  1309. }
  1310. /**
  1311. * 查找单条记录
  1312. * @access public
  1313. * @param array|string|Query|\Closure $data
  1314. * @return array|false|Cursor|string|Model
  1315. * @throws ModelNotFoundException
  1316. * @throws DataNotFoundException
  1317. * @throws AuthenticationException
  1318. * @throws InvalidArgumentException
  1319. * @throws ConnectionException
  1320. * @throws RuntimeException
  1321. */
  1322. public function find($data = null)
  1323. {
  1324. if ($data instanceof Query) {
  1325. return $data->find();
  1326. } elseif ($data instanceof \Closure) {
  1327. call_user_func_array($data, [ & $this]);
  1328. $data = null;
  1329. }
  1330. // 分析查询表达式
  1331. $options = $this->parseExpress();
  1332. if (!is_null($data)) {
  1333. // AR模式分析主键条件
  1334. $this->parsePkWhere($data, $options);
  1335. }
  1336. $options['limit'] = 1;
  1337. $result = false;
  1338. if (!empty($options['cache'])) {
  1339. // 判断查询缓存
  1340. $cache = $options['cache'];
  1341. if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
  1342. $key = 'mongo:' . $options['table'] . '|' . $data;
  1343. } else {
  1344. $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
  1345. }
  1346. $result = Cache::get($key);
  1347. }
  1348. if (!$result) {
  1349. // 生成查询SQL
  1350. $query = $this->builder->select($options);
  1351. // 执行查询
  1352. $readPreference = isset($options['readPreference']) ? $options['readPreference'] : null;
  1353. $result = $this->query($options['table'], $query, $readPreference, $options['fetch_class'], $options['typeMap']);
  1354. if ($result instanceof Cursor) {
  1355. // 返回MongoDB\Driver\Cursor对象
  1356. return $result;
  1357. }
  1358. if (isset($cache)) {
  1359. // 缓存数据
  1360. Cache::set($key, $result, $cache['expire']);
  1361. }
  1362. }
  1363. // 数据处理
  1364. if (!empty($result[0])) {
  1365. $data = $result[0];
  1366. if (!empty($this->model)) {
  1367. // 返回模型对象
  1368. $model = $this->model;
  1369. $data = new $model($data);
  1370. $data->isUpdate(true, isset($options['where']['$and']) ? $options['where']['$and'] : null);
  1371. // 关联查询
  1372. if (!empty($options['relation'])) {
  1373. $data->relationQuery($options['relation']);
  1374. }
  1375. if (!empty($options['with'])) {
  1376. // 预载入
  1377. $data->eagerlyResult($data, $options['with'], is_object($result) ? get_class($result) : '');
  1378. }
  1379. }
  1380. } elseif (!empty($options['fail'])) {
  1381. $this->throwNotFound($options);
  1382. } else {
  1383. $data = null;
  1384. }
  1385. return $data;
  1386. }
  1387. /**
  1388. * 查询失败 抛出异常
  1389. * @access public
  1390. * @param array $options 查询参数
  1391. * @throws ModelNotFoundException
  1392. * @throws DataNotFoundException
  1393. */
  1394. protected function throwNotFound($options = [])
  1395. {
  1396. if (!empty($this->model)) {
  1397. throw new ModelNotFoundException('model data Not Found:' . $this->model, $this->model, $options);
  1398. } else {
  1399. throw new DataNotFoundException('table data not Found:' . $options['table'], $options['table'], $options);
  1400. }
  1401. }
  1402. /**
  1403. * 查找多条记录 如果不存在则抛出异常
  1404. * @access public
  1405. * @param array|string|Query|\Closure $data
  1406. * @return array|\PDOStatement|string|Model
  1407. * @throws ModelNotFoundException
  1408. * @throws DataNotFoundException
  1409. * @throws AuthenticationException
  1410. * @throws InvalidArgumentException
  1411. * @throws ConnectionException
  1412. * @throws RuntimeException
  1413. */
  1414. public function selectOrFail($data = null)
  1415. {
  1416. return $this->failException(true)->select($data);
  1417. }
  1418. /**
  1419. * 查找单条记录 如果不存在则抛出异常
  1420. * @access public
  1421. * @param array|string|Query|\Closure $data
  1422. * @return array|\PDOStatement|string|Model
  1423. * @throws ModelNotFoundException
  1424. * @throws DataNotFoundException
  1425. * @throws AuthenticationException
  1426. * @throws InvalidArgumentException
  1427. * @throws ConnectionException
  1428. * @throws RuntimeException
  1429. */
  1430. public function findOrFail($data = null)
  1431. {
  1432. return $this->failException(true)->find($data);
  1433. }
  1434. /**
  1435. * 分批数据返回处理
  1436. * @access public
  1437. * @param integer $count 每次处理的数据数量
  1438. * @param callable $callback 处理回调方法
  1439. * @param string $column 分批处理的字段名
  1440. * @return boolean
  1441. */
  1442. public function chunk($count, $callback, $column = null)
  1443. {
  1444. $column = $column ?: $this->getPk();
  1445. $options = $this->getOptions();
  1446. $resultSet = $this->limit($count)->order($column, 'asc')->select();
  1447. while (!empty($resultSet)) {
  1448. if (false === call_user_func($callback, $resultSet)) {
  1449. return false;
  1450. }
  1451. $end = end($resultSet);
  1452. $lastId = is_array($end) ? $end[$column] : $end->$column;
  1453. $resultSet = $this->options($options)
  1454. ->limit($count)
  1455. ->where($column, '>', $lastId)
  1456. ->order($column, 'asc')
  1457. ->select();
  1458. }
  1459. return true;
  1460. }
  1461. /**
  1462. * 获取数据表信息
  1463. * @access public
  1464. * @param string $tableName 数据表名 留空自动获取
  1465. * @param string $fetch 获取信息类型 包括 fields type pk
  1466. * @return mixed
  1467. */
  1468. public function getTableInfo($tableName = '', $fetch = '')
  1469. {
  1470. if (!$tableName) {
  1471. $tableName = $this->getTable();
  1472. }
  1473. if (is_array($tableName)) {
  1474. $tableName = key($tableName) ?: current($tableName);
  1475. }
  1476. if (strpos($tableName, ',')) {
  1477. // 多表不获取字段信息
  1478. return false;
  1479. } else {
  1480. $tableName = $this->parseSqlTable($tableName);
  1481. }
  1482. $guid = md5($tableName);
  1483. if (!isset(self::$info[$guid])) {
  1484. $result = $this->table($tableName)->find();
  1485. $fields = array_keys($result);
  1486. $type = [];
  1487. foreach ($result as $key => $val) {
  1488. // 记录字段类型
  1489. $type[$key] = getType($val);
  1490. if ('_id' == $key) {
  1491. $pk = $key;
  1492. }
  1493. }
  1494. if (!isset($pk)) {
  1495. // 设置主键
  1496. $pk = null;
  1497. }
  1498. $result = ['fields' => $fields, 'type' => $type, 'pk' => $pk];
  1499. self::$info[$guid] = $result;
  1500. }
  1501. return $fetch ? self::$info[$guid][$fetch] : self::$info[$guid];
  1502. }
  1503. /**
  1504. * 分析表达式(可用于查询或者写入操作)
  1505. * @access protected
  1506. * @return array
  1507. */
  1508. protected function parseExpress()
  1509. {
  1510. $options = $this->options;
  1511. // 获取数据表
  1512. if (empty($options['table'])) {
  1513. $options['table'] = $this->getTable();
  1514. }
  1515. if (!isset($options['where'])) {
  1516. $options['where'] = [];
  1517. }
  1518. $modifiers = empty($options['modifiers']) ? [] : $options['modifiers'];
  1519. if (isset($options['comment'])) {
  1520. $modifiers['$comment'] = $options['comment'];
  1521. }
  1522. if (isset($options['maxTimeMS'])) {
  1523. $modifiers['$maxTimeMS'] = $options['maxTimeMS'];
  1524. }
  1525. if (!empty($modifiers)) {
  1526. $options['modifiers'] = $modifiers;
  1527. }
  1528. if (!isset($options['projection']) || '*' == $options['projection']) {
  1529. $options['projection'] = [];
  1530. }
  1531. if (!isset($options['typeMap'])) {
  1532. $options['typeMap'] = $this->getConfig('type_map');
  1533. }
  1534. if (!isset($options['limit'])) {
  1535. $options['limit'] = 0;
  1536. }
  1537. foreach (['master', 'fetch_class'] as $name) {
  1538. if (!isset($options[$name])) {
  1539. $options[$name] = false;
  1540. }
  1541. }
  1542. if (isset($options['page'])) {
  1543. // 根据页数计算limit
  1544. list($page, $listRows) = $options['page'];
  1545. $page = $page > 0 ? $page : 1;
  1546. $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20);
  1547. $offset = $listRows * ($page - 1);
  1548. $options['skip'] = intval($offset);
  1549. $options['limit'] = intval($listRows);
  1550. }
  1551. $this->options = [];
  1552. return $options;
  1553. }
  1554. }