Builder.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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\Javascript;
  13. use MongoDB\BSON\ObjectID;
  14. use MongoDB\BSON\Regex;
  15. use MongoDB\Driver\BulkWrite;
  16. use MongoDB\Driver\Command;
  17. use MongoDB\Driver\Query as MongoQuery;
  18. use think\Exception;
  19. use think\mongo\Connection;
  20. use think\mongo\Query;
  21. class Builder
  22. {
  23. // connection对象实例
  24. protected $connection;
  25. // 查询对象实例
  26. protected $query;
  27. // 查询参数
  28. protected $options = [];
  29. // 最后插入ID
  30. protected $insertId = [];
  31. // 查询表达式
  32. protected $exp = ['<>' => 'ne', '=' => '=', '>' => 'gt', '>=' => 'gte', '<' => 'lt', '<=' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin', 'mod' => 'mod', 'exists' => 'exists', 'regex' => 'regex', 'type' => 'type', 'all' => 'all', '> time' => '> time', '< time' => '< time', 'between time' => 'between time', 'not between time' => 'not between time', 'notbetween time' => 'not between time', 'like' => 'like'];
  33. /**
  34. * 架构函数
  35. * @access public
  36. * @param Connection $connection 数据库连接对象实例
  37. * @param Query $query 数据库查询对象实例
  38. */
  39. public function __construct(Connection $connection, Query $query)
  40. {
  41. $this->connection = $connection;
  42. $this->query = $query;
  43. }
  44. /**
  45. * key分析
  46. * @access protected
  47. * @param string $key
  48. * @return string
  49. */
  50. protected function parseKey($key)
  51. {
  52. if ('id' == $key && $this->connection->getConfig('pk_convert_id')) {
  53. $key = '_id';
  54. }
  55. return trim($key);
  56. }
  57. /**
  58. * value分析
  59. * @access protected
  60. * @param mixed $value
  61. * @param string $field
  62. * @return string
  63. */
  64. protected function parseValue($value, $field = '')
  65. {
  66. if ('_id' == $field && !($value instanceof ObjectID)) {
  67. return new ObjectID($value);
  68. }
  69. return $value;
  70. }
  71. /**
  72. * insert数据分析
  73. * @access protected
  74. * @param array $data 数据
  75. * @param array $options 查询参数
  76. * @return array
  77. */
  78. protected function parseData($data, $options)
  79. {
  80. if (empty($data)) {
  81. return [];
  82. }
  83. $result = [];
  84. foreach ($data as $key => $val) {
  85. $item = $this->parseKey($key);
  86. if (isset($val[0]) && 'exp' == $val[0]) {
  87. $result[$item] = $val[1];
  88. } elseif (is_null($val)) {
  89. $result[$item] = 'NULL';
  90. } else {
  91. $result[$item] = $this->parseValue($val, $key);
  92. }
  93. }
  94. return $result;
  95. }
  96. /**
  97. * Set数据分析
  98. * @access protected
  99. * @param array $data 数据
  100. * @param array $options 查询参数
  101. * @return array
  102. */
  103. protected function parseSet($data, $options)
  104. {
  105. if (empty($data)) {
  106. return [];
  107. }
  108. $result = [];
  109. foreach ($data as $key => $val) {
  110. $item = $this->parseKey($key);
  111. if (is_array($val) && isset($val[0]) && in_array($val[0], ['$inc', '$set', '$unset', '$push', '$pushall', '$addtoset', '$pop', '$pull', '$pullall'])) {
  112. $result[$val[0]][$item] = $this->parseValue($val[1], $key);
  113. } else {
  114. $result['$set'][$item] = $this->parseValue($val, $key);
  115. }
  116. }
  117. return $result;
  118. }
  119. /**
  120. * 生成查询过滤条件
  121. * @access public
  122. * @param mixed $where
  123. * @return array
  124. */
  125. public function parseWhere($where)
  126. {
  127. if (empty($where)) {
  128. $where = [];
  129. }
  130. $filter = [];
  131. foreach ($where as $logic => $val) {
  132. foreach ($val as $field => $value) {
  133. if ($value instanceof \Closure) {
  134. // 使用闭包查询
  135. $query = new Query($this->connection);
  136. call_user_func_array($value, [ & $query]);
  137. $filter[$logic][] = $this->parseWhere($query->getOptions('where')[$logic]);
  138. } else {
  139. if (strpos($field, '|')) {
  140. // 不同字段使用相同查询条件(OR)
  141. $array = explode('|', $field);
  142. foreach ($array as $k) {
  143. $filter['$or'][] = $this->parseWhereItem($k, $value);
  144. }
  145. } elseif (strpos($field, '&')) {
  146. // 不同字段使用相同查询条件(AND)
  147. $array = explode('&', $field);
  148. foreach ($array as $k) {
  149. $filter['$and'][] = $this->parseWhereItem($k, $value);
  150. }
  151. } else {
  152. // 对字段使用表达式查询
  153. $field = is_string($field) ? $field : '';
  154. $filter[$logic][] = $this->parseWhereItem($field, $value);
  155. }
  156. }
  157. }
  158. }
  159. return $filter;
  160. }
  161. // where子单元分析
  162. protected function parseWhereItem($field, $val)
  163. {
  164. $key = $field ? $this->parseKey($field) : '';
  165. // 查询规则和条件
  166. if (!is_array($val)) {
  167. $val = ['=', $val];
  168. }
  169. list($exp, $value) = $val;
  170. // 对一个字段使用多个查询条件
  171. if (is_array($exp)) {
  172. foreach ($val as $item) {
  173. $str[] = $this->parseWhereItem($key, $item);
  174. }
  175. return $str;
  176. }
  177. // 检测操作符
  178. if (!in_array($exp, $this->exp)) {
  179. $exp = strtolower($exp);
  180. if (isset($this->exp[$exp])) {
  181. $exp = $this->exp[$exp];
  182. } else {
  183. throw new Exception('where express error:' . $exp);
  184. }
  185. }
  186. $query = [];
  187. if ('=' == $exp) {
  188. // 普通查询
  189. $query[$key] = $this->parseValue($value, $key);
  190. } elseif (in_array($exp, ['neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt', 'mod'])) {
  191. // 比较运算
  192. $k = '$' . $exp;
  193. $query[$key] = [$k => $this->parseValue($value, $key)];
  194. } elseif ('all' == $exp) {
  195. // 满足所有指定条件
  196. $query[$key] = ['$all', $this->parseValue($value, $key)];
  197. } elseif ('between' == $exp) {
  198. // 区间查询
  199. $value = is_array($value) ? $value : explode(',', $value);
  200. $query[$key] = ['$gte' => $this->parseValue($value[0], $key), '$lte' => $this->parseValue($value[1], $key)];
  201. } elseif ('not between' == $exp) {
  202. // 范围查询
  203. $value = is_array($value) ? $value : explode(',', $value);
  204. $query[$key] = ['$lt' => $this->parseValue($value[0], $key), '$gt' => $this->parseValue($value[1], $key)];
  205. } elseif ('exists' == $exp) {
  206. // 字段是否存在
  207. $query[$key] = ['$exists' => (bool) $value];
  208. } elseif ('type' == $exp) {
  209. // 类型查询
  210. $query[$key] = ['$type' => intval($value)];
  211. } elseif ('exp' == $exp) {
  212. // 表达式查询
  213. $query['$where'] = $value instanceof Javascript ? $value : new Javascript($value);
  214. } elseif ('like' == $exp) {
  215. // 模糊查询 采用正则方式
  216. $query[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
  217. } elseif (in_array($exp, ['nin', 'in'])) {
  218. // IN 查询
  219. $value = is_array($value) ? $value : explode(',', $value);
  220. foreach ($value as $k => $val) {
  221. $value[$k] = $this->parseValue($val, $key);
  222. }
  223. $query[$key] = ['$' . $exp => $value];
  224. } elseif ('regex' == $exp) {
  225. $query[$key] = $value instanceof Regex ? $value : new Regex($value, 'i');
  226. } elseif ('< time' == $exp) {
  227. $query[$key] = ['$lt' => $this->parseDateTime($value, $field)];
  228. } elseif ('> time' == $exp) {
  229. $query[$key] = ['$gt' => $this->parseDateTime($value, $field)];
  230. } elseif ('between time' == $exp) {
  231. // 区间查询
  232. $value = is_array($value) ? $value : explode(',', $value);
  233. $query[$key] = ['$gte' => $this->parseDateTime($value[0], $field), '$lte' => $this->parseDateTime($value[1], $field)];
  234. } elseif ('not between time' == $exp) {
  235. // 范围查询
  236. $value = is_array($value) ? $value : explode(',', $value);
  237. $query[$key] = ['$lt' => $this->parseDateTime($value[0], $field), '$gt' => $this->parseDateTime($value[1], $field)];
  238. } else {
  239. // 普通查询
  240. $query[$key] = $this->parseValue($value, $key);
  241. }
  242. return $query;
  243. }
  244. /**
  245. * 日期时间条件解析
  246. * @access protected
  247. * @param string $value
  248. * @param string $key
  249. * @return string
  250. */
  251. protected function parseDateTime($value, $key)
  252. {
  253. // 获取时间字段类型
  254. $type = $this->query->getTableInfo('', 'type');
  255. if (isset($type[$key])) {
  256. $value = strtotime($value) ?: $value;
  257. if (preg_match('/(datetime|timestamp)/is', $type[$key])) {
  258. // 日期及时间戳类型
  259. $value = date('Y-m-d H:i:s', $value);
  260. } elseif (preg_match('/(date)/is', $type[$key])) {
  261. // 日期及时间戳类型
  262. $value = date('Y-m-d', $value);
  263. }
  264. }
  265. return $value;
  266. }
  267. /**
  268. * 获取最后写入的ID 如果是insertAll方法的话 返回所有写入的ID
  269. * @access public
  270. * @return mixed
  271. */
  272. public function getLastInsID()
  273. {
  274. return $this->insertId;
  275. }
  276. /**
  277. * 生成insert BulkWrite对象
  278. * @access public
  279. * @param array $data 数据
  280. * @param array $options 表达式
  281. * @return BulkWrite
  282. */
  283. public function insert(array $data, $options = [])
  284. {
  285. // 分析并处理数据
  286. $data = $this->parseData($data, $options);
  287. $bulk = new BulkWrite;
  288. if ($insertId = $bulk->insert($data)) {
  289. $this->insertId = $insertId;
  290. }
  291. $this->log('insert', $data, $options);
  292. return $bulk;
  293. }
  294. /**
  295. * 生成insertall BulkWrite对象
  296. * @access public
  297. * @param array $dataSet 数据集
  298. * @param array $options 参数
  299. * @return BulkWrite
  300. */
  301. public function insertAll($dataSet, $options = [])
  302. {
  303. $bulk = new BulkWrite;
  304. foreach ($dataSet as $data) {
  305. // 分析并处理数据
  306. $data = $this->parseData($data, $options);
  307. if ($insertId = $bulk->insert($data)) {
  308. $this->insertId[] = $insertId;
  309. }
  310. }
  311. $this->log('insert', $dataSet, $options);
  312. return $bulk;
  313. }
  314. /**
  315. * 生成update BulkWrite对象
  316. * @access public
  317. * @param array $data 数据
  318. * @param array $options 参数
  319. * @return BulkWrite
  320. */
  321. public function update($data, $options = [])
  322. {
  323. $data = $this->parseSet($data, $options);
  324. $where = $this->parseWhere($options['where']);
  325. if (1 == $options['limit']) {
  326. $updateOptions = ['multi' => false];
  327. } else {
  328. $updateOptions = ['multi' => true];
  329. }
  330. $bulk = new BulkWrite;
  331. $bulk->update($where, $data, $updateOptions);
  332. $this->log('update', $data, $where);
  333. return $bulk;
  334. }
  335. /**
  336. * 生成delete BulkWrite对象
  337. * @access public
  338. * @param array $options 参数
  339. * @return BulkWrite
  340. */
  341. public function delete($options)
  342. {
  343. $where = $this->parseWhere($options['where']);
  344. $bulk = new BulkWrite;
  345. if (1 == $options['limit']) {
  346. $deleteOptions = ['limit' => 1];
  347. } else {
  348. $deleteOptions = ['limit' => 0];
  349. }
  350. $bulk->delete($where, $deleteOptions);
  351. $this->log('remove', $where, $deleteOptions);
  352. return $bulk;
  353. }
  354. /**
  355. * 生成Mongo查询对象
  356. * @access public
  357. * @param array $options 参数
  358. * @return MongoQuery
  359. */
  360. public function select($options)
  361. {
  362. $where = $this->parseWhere($options['where']);
  363. $query = new MongoQuery($where, $options);
  364. $this->log('find', $where, $options);
  365. return $query;
  366. }
  367. /**
  368. * 生成Count命令
  369. * @access public
  370. * @param array $options 参数
  371. * @return Command
  372. */
  373. public function count($options)
  374. {
  375. $cmd['count'] = $options['table'];
  376. $cmd['query'] = $this->parseWhere($options['where']);
  377. foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
  378. if (isset($options[$option])) {
  379. $cmd[$option] = $options[$option];
  380. }
  381. }
  382. $command = new Command($cmd);
  383. $this->log('cmd', 'count', $cmd);
  384. return $command;
  385. }
  386. /**
  387. * 生成distinct命令
  388. * @access public
  389. * @param array $options 参数
  390. * @param string $field 字段名
  391. * @return Command
  392. */
  393. public function distinct($options, $field)
  394. {
  395. $cmd = [
  396. 'distinct' => $options['table'],
  397. 'key' => $field,
  398. ];
  399. if (!empty($options['where'])) {
  400. $cmd['query'] = $this->parseWhere($options['where']);
  401. }
  402. if (isset($options['maxTimeMS'])) {
  403. $cmd['maxTimeMS'] = $options['maxTimeMS'];
  404. }
  405. $command = new Command($cmd);
  406. $this->log('cmd', 'distinct', $cmd);
  407. return $command;
  408. }
  409. /**
  410. * 查询所有的collection
  411. * @access public
  412. * @return Command
  413. */
  414. public function listcollections()
  415. {
  416. $cmd = ['listCollections' => 1];
  417. $command = new Command($cmd);
  418. $this->log('cmd', 'listCollections', $cmd);
  419. return $command;
  420. }
  421. /**
  422. * 查询数据表的状态信息
  423. * @access public
  424. * @return Command
  425. */
  426. public function collStats($options)
  427. {
  428. $cmd = ['collStats' => $options['table']];
  429. $command = new Command($cmd);
  430. $this->log('cmd', 'collStats', $cmd);
  431. return $command;
  432. }
  433. protected function log($type, $data, $options = [])
  434. {
  435. if ($this->connection->getConfig('debug')) {
  436. $this->connection->log($type, $data, $options);
  437. }
  438. }
  439. }