Mongo.class.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 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\Db\Driver;
  12. use Think\Db\Driver;
  13. /**
  14. * Mongo数据库驱动
  15. */
  16. class Mongo extends Driver
  17. {
  18. protected $_mongo = null; // MongoDb Object
  19. protected $_collection = null; // MongoCollection Object
  20. protected $_dbName = ''; // dbName
  21. protected $_collectionName = ''; // collectionName
  22. protected $_cursor = null; // MongoCursor Object
  23. protected $comparison = array('neq' => 'ne', 'ne' => 'ne', 'gt' => 'gt', 'egt' => 'gte', 'gte' => 'gte', 'lt' => 'lt', 'elt' => 'lte', 'lte' => 'lte', 'in' => 'in', 'not in' => 'nin', 'nin' => 'nin');
  24. /**
  25. * 架构函数 读取数据库配置信息
  26. * @access public
  27. * @param array $config 数据库配置数组
  28. */
  29. public function __construct($config = '')
  30. {
  31. if (!class_exists('mongoClient')) {
  32. E(L('_NOT_SUPPORT_') . ':Mongo');
  33. }
  34. if (!empty($config)) {
  35. $this->config = array_merge($this->config, $config);
  36. if (empty($this->config['params'])) {
  37. $this->config['params'] = array();
  38. }
  39. }
  40. }
  41. /**
  42. * 连接数据库方法
  43. * @access public
  44. */
  45. public function connect($config = '', $linkNum = 0)
  46. {
  47. if (!isset($this->linkID[$linkNum])) {
  48. if (empty($config)) {
  49. $config = $this->config;
  50. }
  51. $host = 'mongodb://' . ($config['username'] ? "{$config['username']}" : '') . ($config['password'] ? ":{$config['password']}@" : '') . $config['hostname'] . ($config['hostport'] ? ":{$config['hostport']}" : '') . '/' . ($config['database'] ? "{$config['database']}" : '');
  52. try {
  53. $this->linkID[$linkNum] = new \mongoClient($host, $this->config['params']);
  54. } catch (\MongoConnectionException $e) {
  55. E($e->getmessage());
  56. }
  57. }
  58. return $this->linkID[$linkNum];
  59. }
  60. /**
  61. * 切换当前操作的Db和Collection
  62. * @access public
  63. * @param string $collection collection
  64. * @param string $db db
  65. * @param boolean $master 是否主服务器
  66. * @return void
  67. */
  68. public function switchCollection($collection, $db = '', $master = true)
  69. {
  70. // 当前没有连接 则首先进行数据库连接
  71. if (!$this->_linkID) {
  72. $this->initConnect($master);
  73. }
  74. try {
  75. if (!empty($db)) {
  76. // 传人Db则切换数据库
  77. // 当前MongoDb对象
  78. $this->_dbName = $db;
  79. $this->_mongo = $this->_linkID->selectDb($db);
  80. }
  81. // 当前MongoCollection对象
  82. if ($this->config['debug']) {
  83. $this->queryStr = $this->_dbName . '.getCollection(' . $collection . ')';
  84. }
  85. if ($this->_collectionName != $collection) {
  86. $this->queryTimes++;
  87. N('db_query', 1); // 兼容代码
  88. $this->debug(true);
  89. $this->_collection = $this->_mongo->selectCollection($collection);
  90. $this->debug(false);
  91. $this->_collectionName = $collection; // 记录当前Collection名称
  92. }
  93. } catch (MongoException $e) {
  94. E($e->getMessage());
  95. }
  96. }
  97. /**
  98. * 释放查询结果
  99. * @access public
  100. */
  101. public function free()
  102. {
  103. $this->_cursor = null;
  104. }
  105. /**
  106. * 执行命令
  107. * @access public
  108. * @param array $command 指令
  109. * @return array
  110. */
  111. public function command($command = array(), $options = array())
  112. {
  113. $cache = isset($options['cache']) ? $options['cache'] : false;
  114. if ($cache) {
  115. // 查询缓存检测
  116. $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($command));
  117. $value = S($key, '', '', $cache['type']);
  118. if (false !== $value) {
  119. return $value;
  120. }
  121. }
  122. N('db_write', 1); // 兼容代码
  123. $this->executeTimes++;
  124. try {
  125. if ($this->config['debug']) {
  126. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.runCommand(';
  127. $this->queryStr .= json_encode($command);
  128. $this->queryStr .= ')';
  129. }
  130. $this->debug(true);
  131. $result = $this->_mongo->command($command);
  132. $this->debug(false);
  133. if ($cache && $result['ok']) {
  134. // 查询缓存写入
  135. S($key, $result, $cache['expire'], $cache['type']);
  136. }
  137. return $result;
  138. } catch (\MongoCursorException $e) {
  139. E($e->getMessage());
  140. }
  141. }
  142. /**
  143. * 执行语句
  144. * @access public
  145. * @param string $code sql指令
  146. * @param array $args 参数
  147. * @return mixed
  148. */
  149. public function execute($code, $args = array())
  150. {
  151. $this->executeTimes++;
  152. N('db_write', 1); // 兼容代码
  153. $this->debug(true);
  154. $this->queryStr = 'execute:' . $code;
  155. $result = $this->_mongo->execute($code, $args);
  156. $this->debug(false);
  157. if ($result['ok']) {
  158. return $result['retval'];
  159. } else {
  160. E($result['errmsg']);
  161. }
  162. }
  163. /**
  164. * 关闭数据库
  165. * @access public
  166. */
  167. public function close()
  168. {
  169. if ($this->_linkID) {
  170. $this->_linkID->close();
  171. $this->_linkID = null;
  172. $this->_mongo = null;
  173. $this->_collection = null;
  174. $this->_cursor = null;
  175. }
  176. }
  177. /**
  178. * 数据库错误信息
  179. * @access public
  180. * @return string
  181. */
  182. public function error()
  183. {
  184. $this->error = $this->_mongo->lastError();
  185. trace($this->error, '', 'ERR');
  186. return $this->error;
  187. }
  188. /**
  189. * 插入记录
  190. * @access public
  191. * @param mixed $data 数据
  192. * @param array $options 参数表达式
  193. * @param boolean $replace 是否replace
  194. * @return false | integer
  195. */
  196. public function insert($data, $options = array(), $replace = false)
  197. {
  198. if (isset($options['table'])) {
  199. $this->switchCollection($options['table']);
  200. }
  201. $this->model = $options['model'];
  202. $this->executeTimes++;
  203. N('db_write', 1); // 兼容代码
  204. if ($this->config['debug']) {
  205. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.insert(';
  206. $this->queryStr .= $data ? json_encode($data) : '{}';
  207. $this->queryStr .= ')';
  208. }
  209. try {
  210. $this->debug(true);
  211. $result = $replace ? $this->_collection->save($data) : $this->_collection->insert($data);
  212. $this->debug(false);
  213. if ($result) {
  214. $_id = $data['_id'];
  215. if (is_object($_id)) {
  216. $_id = $_id->__toString();
  217. }
  218. $this->lastInsID = $_id;
  219. }
  220. return $result;
  221. } catch (\MongoCursorException $e) {
  222. E($e->getMessage());
  223. }
  224. }
  225. /**
  226. * 插入多条记录
  227. * @access public
  228. * @param array $dataList 数据
  229. * @param array $options 参数表达式
  230. * @return bool
  231. */
  232. public function insertAll($dataList, $options = array())
  233. {
  234. if (isset($options['table'])) {
  235. $this->switchCollection($options['table']);
  236. }
  237. $this->model = $options['model'];
  238. $this->executeTimes++;
  239. N('db_write', 1); // 兼容代码
  240. try {
  241. $this->debug(true);
  242. $result = $this->_collection->batchInsert($dataList);
  243. $this->debug(false);
  244. return $result;
  245. } catch (\MongoCursorException $e) {
  246. E($e->getMessage());
  247. }
  248. }
  249. /**
  250. * 生成下一条记录ID 用于自增非MongoId主键
  251. * @access public
  252. * @param string $pk 主键名
  253. * @return integer
  254. */
  255. public function getMongoNextId($pk, $options = array())
  256. {
  257. if (isset($options['table'])) {
  258. $this->switchCollection($options['table']);
  259. }
  260. if ($this->config['debug']) {
  261. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.find({},{' . $pk . ':1}).sort({' . $pk . ':-1}).limit(1)';
  262. }
  263. try {
  264. $this->debug(true);
  265. $result = $this->_collection->find(array(), array($pk => 1))->sort(array($pk => -1))->limit(1);
  266. $this->debug(false);
  267. } catch (\MongoCursorException $e) {
  268. E($e->getMessage());
  269. }
  270. $data = $result->getNext();
  271. return isset($data[$pk]) ? $data[$pk] + 1 : 1;
  272. }
  273. /**
  274. * 更新记录
  275. * @access public
  276. * @param mixed $data 数据
  277. * @param array $options 表达式
  278. * @return bool
  279. */
  280. public function update($data, $options)
  281. {
  282. if (isset($options['table'])) {
  283. $this->switchCollection($options['table']);
  284. }
  285. $this->executeTimes++;
  286. N('db_write', 1); // 兼容代码
  287. $this->model = $options['model'];
  288. $query = $this->parseWhere(isset($options['where']) ? $options['where'] : array());
  289. $set = $this->parseSet($data);
  290. if ($this->config['debug']) {
  291. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.update(';
  292. $this->queryStr .= $query ? json_encode($query) : '{}';
  293. $this->queryStr .= ',' . json_encode($set) . ')';
  294. }
  295. try {
  296. $this->debug(true);
  297. if (isset($options['limit']) && 1 == $options['limit']) {
  298. $multiple = array("multiple" => false);
  299. } else {
  300. $multiple = array("multiple" => true);
  301. }
  302. $result = $this->_collection->update($query, $set, $multiple);
  303. $this->debug(false);
  304. return $result;
  305. } catch (\MongoCursorException $e) {
  306. E($e->getMessage());
  307. }
  308. }
  309. /**
  310. * 删除记录
  311. * @access public
  312. * @param array $options 表达式
  313. * @return false | integer
  314. */
  315. public function delete($options = array())
  316. {
  317. if (isset($options['table'])) {
  318. $this->switchCollection($options['table']);
  319. }
  320. $query = $this->parseWhere(isset($options['where']) ? $options['where'] : array());
  321. $this->model = $options['model'];
  322. $this->executeTimes++;
  323. N('db_write', 1); // 兼容代码
  324. if ($this->config['debug']) {
  325. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.remove(' . json_encode($query) . ')';
  326. }
  327. try {
  328. $this->debug(true);
  329. $result = $this->_collection->remove($query);
  330. $this->debug(false);
  331. return $result;
  332. } catch (\MongoCursorException $e) {
  333. E($e->getMessage());
  334. }
  335. }
  336. /**
  337. * 清空记录
  338. * @access public
  339. * @param array $options 表达式
  340. * @return false | integer
  341. */
  342. public function clear($options = array())
  343. {
  344. if (isset($options['table'])) {
  345. $this->switchCollection($options['table']);
  346. }
  347. $this->model = $options['model'];
  348. $this->executeTimes++;
  349. N('db_write', 1); // 兼容代码
  350. if ($this->config['debug']) {
  351. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.remove({})';
  352. }
  353. try {
  354. $this->debug(true);
  355. $result = $this->_collection->drop();
  356. $this->debug(false);
  357. return $result;
  358. } catch (\MongoCursorException $e) {
  359. E($e->getMessage());
  360. }
  361. }
  362. /**
  363. * 查找记录
  364. * @access public
  365. * @param array $options 表达式
  366. * @return iterator
  367. */
  368. public function select($options = array())
  369. {
  370. if (isset($options['table'])) {
  371. $this->switchCollection($options['table'], '', false);
  372. }
  373. $this->model = $options['model'];
  374. $this->queryTimes++;
  375. N('db_query', 1); // 兼容代码
  376. $query = $this->parseWhere(isset($options['where']) ? $options['where'] : array());
  377. $field = $this->parseField(isset($options['field']) ? $options['field'] : array());
  378. try {
  379. if ($this->config['debug']) {
  380. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.find(';
  381. $this->queryStr .= $query ? json_encode($query) : '{}';
  382. if (is_array($field) && count($field)) {
  383. foreach ($field as $f => $v) {
  384. $_field_array[$f] = $v ? 1 : 0;
  385. }
  386. $this->queryStr .= $field ? ', ' . json_encode($_field_array) : ', {}';
  387. }
  388. $this->queryStr .= ')';
  389. }
  390. $this->debug(true);
  391. $_cursor = $this->_collection->find($query, $field);
  392. if (!empty($options['order'])) {
  393. $order = $this->parseOrder($options['order']);
  394. if ($this->config['debug']) {
  395. $this->queryStr .= '.sort(' . json_encode($order) . ')';
  396. }
  397. $_cursor = $_cursor->sort($order);
  398. }
  399. if (isset($options['page'])) {
  400. // 根据页数计算limit
  401. list($page, $length) = $options['page'];
  402. $page = $page > 0 ? $page : 1;
  403. $length = $length > 0 ? $length : (is_numeric($options['limit']) ? $options['limit'] : 20);
  404. $offset = $length * ((int) $page - 1);
  405. $options['limit'] = $offset . ',' . $length;
  406. }
  407. if (isset($options['limit'])) {
  408. list($offset, $length) = $this->parseLimit($options['limit']);
  409. if (!empty($offset)) {
  410. if ($this->config['debug']) {
  411. $this->queryStr .= '.skip(' . intval($offset) . ')';
  412. }
  413. $_cursor = $_cursor->skip(intval($offset));
  414. }
  415. if ($this->config['debug']) {
  416. $this->queryStr .= '.limit(' . intval($length) . ')';
  417. }
  418. $_cursor = $_cursor->limit(intval($length));
  419. }
  420. $this->debug(false);
  421. $this->_cursor = $_cursor;
  422. $resultSet = iterator_to_array($_cursor);
  423. return $resultSet;
  424. } catch (\MongoCursorException $e) {
  425. E($e->getMessage());
  426. }
  427. }
  428. /**
  429. * 查找某个记录
  430. * @access public
  431. * @param array $options 表达式
  432. * @return array
  433. */
  434. public function find($options = array())
  435. {
  436. $options['limit'] = 1;
  437. $find = $this->select($options);
  438. return array_shift($find);
  439. }
  440. /**
  441. * 统计记录数
  442. * @access public
  443. * @param array $options 表达式
  444. * @return iterator
  445. */
  446. public function count($options = array())
  447. {
  448. if (isset($options['table'])) {
  449. $this->switchCollection($options['table'], '', false);
  450. }
  451. $this->model = $options['model'];
  452. $this->queryTimes++;
  453. N('db_query', 1); // 兼容代码
  454. $query = $this->parseWhere(isset($options['where']) ? $options['where'] : array());
  455. if ($this->config['debug']) {
  456. $this->queryStr = $this->_dbName . '.' . $this->_collectionName;
  457. $this->queryStr .= $query ? '.find(' . json_encode($query) . ')' : '';
  458. $this->queryStr .= '.count()';
  459. }
  460. try {
  461. $this->debug(true);
  462. $count = $this->_collection->count($query);
  463. $this->debug(false);
  464. return $count;
  465. } catch (\MongoCursorException $e) {
  466. E($e->getMessage());
  467. }
  468. }
  469. public function group($keys, $initial, $reduce, $options = array())
  470. {
  471. if (isset($options['table']) && $this->_collectionName != $options['table']) {
  472. $this->switchCollection($options['table'], '', false);
  473. }
  474. $cache = isset($options['cache']) ? $options['cache'] : false;
  475. if ($cache) {
  476. $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
  477. $value = S($key, '', '', $cache['type']);
  478. if (false !== $value) {
  479. return $value;
  480. }
  481. }
  482. $this->model = $options['model'];
  483. $this->queryTimes++;
  484. N('db_query', 1); // 兼容代码
  485. $query = $this->parseWhere(isset($options['where']) ? $options['where'] : array());
  486. if ($this->config['debug']) {
  487. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.group({key:' . json_encode($keys) . ',cond:' .
  488. json_encode($options['condition']) . ',reduce:' .
  489. json_encode($reduce) . ',initial:' .
  490. json_encode($initial) . '})';
  491. }
  492. try {
  493. $this->debug(true);
  494. $option = array('condition' => $options['condition'], 'finalize' => $options['finalize'], 'maxTimeMS' => $options['maxTimeMS']);
  495. $group = $this->_collection->group($keys, $initial, $reduce, $options);
  496. $this->debug(false);
  497. if ($cache && $group['ok']) {
  498. S($key, $group, $cache['expire'], $cache['type']);
  499. }
  500. return $group;
  501. } catch (\MongoCursorException $e) {
  502. E($e->getMessage());
  503. }
  504. }
  505. /**
  506. * 取得数据表的字段信息
  507. * @access public
  508. * @return array
  509. */
  510. public function getFields($collection = '')
  511. {
  512. if (!empty($collection) && $collection != $this->_collectionName) {
  513. $this->switchCollection($collection, '', false);
  514. }
  515. $this->queryTimes++;
  516. N('db_query', 1); // 兼容代码
  517. if ($this->config['debug']) {
  518. $this->queryStr = $this->_dbName . '.' . $this->_collectionName . '.findOne()';
  519. }
  520. try {
  521. $this->debug(true);
  522. $result = $this->_collection->findOne();
  523. $this->debug(false);
  524. } catch (\MongoCursorException $e) {
  525. E($e->getMessage());
  526. }
  527. if ($result) {
  528. // 存在数据则分析字段
  529. $info = array();
  530. foreach ($result as $key => $val) {
  531. $info[$key] = array(
  532. 'name' => $key,
  533. 'type' => getType($val),
  534. );
  535. }
  536. return $info;
  537. }
  538. // 暂时没有数据 返回false
  539. return false;
  540. }
  541. /**
  542. * 取得当前数据库的collection信息
  543. * @access public
  544. */
  545. public function getTables()
  546. {
  547. if ($this->config['debug']) {
  548. $this->queryStr = $this->_dbName . '.getCollenctionNames()';
  549. }
  550. $this->queryTimes++;
  551. N('db_query', 1); // 兼容代码
  552. $this->debug(true);
  553. $list = $this->_mongo->listCollections();
  554. $this->debug(false);
  555. $info = array();
  556. foreach ($list as $collection) {
  557. $info[] = $collection->getName();
  558. }
  559. return $info;
  560. }
  561. /**
  562. * 取得当前数据库的对象
  563. * @access public
  564. * @return object mongoClient
  565. */
  566. public function getDB()
  567. {
  568. return $this->_mongo;
  569. }
  570. /**
  571. * 取得当前集合的对象
  572. * @access public
  573. * @return object MongoCollection
  574. */
  575. public function getCollection()
  576. {
  577. return $this->_collection;
  578. }
  579. /**
  580. * set分析
  581. * @access protected
  582. * @param array $data
  583. * @return string
  584. */
  585. protected function parseSet($data)
  586. {
  587. $result = array();
  588. foreach ($data as $key => $val) {
  589. if (is_array($val)) {
  590. switch ($val[0]) {
  591. case 'inc':
  592. $result['$inc'][$key] = (float) $val[1];
  593. break;
  594. case 'set':
  595. case 'unset':
  596. case 'push':
  597. case 'pushall':
  598. case 'addtoset':
  599. case 'pop':
  600. case 'pull':
  601. case 'pullall':
  602. $result['$' . $val[0]][$key] = $val[1];
  603. break;
  604. default:
  605. $result['$set'][$key] = $val;
  606. }
  607. } else {
  608. $result['$set'][$key] = $val;
  609. }
  610. }
  611. return $result;
  612. }
  613. /**
  614. * order分析
  615. * @access protected
  616. * @param mixed $order
  617. * @return array
  618. */
  619. protected function parseOrder($order)
  620. {
  621. if (is_string($order)) {
  622. $array = explode(',', $order);
  623. $order = array();
  624. foreach ($array as $key => $val) {
  625. $arr = explode(' ', trim($val));
  626. if (isset($arr[1])) {
  627. $arr[1] = 'asc' == $arr[1] ? 1 : -1;
  628. } else {
  629. $arr[1] = 1;
  630. }
  631. $order[$arr[0]] = $arr[1];
  632. }
  633. }
  634. return $order;
  635. }
  636. /**
  637. * limit分析
  638. * @access protected
  639. * @param mixed $limit
  640. * @return array
  641. */
  642. protected function parseLimit($limit)
  643. {
  644. if (strpos($limit, ',')) {
  645. $array = explode(',', $limit);
  646. } else {
  647. $array = array(0, $limit);
  648. }
  649. return $array;
  650. }
  651. /**
  652. * field分析
  653. * @access protected
  654. * @param mixed $fields
  655. * @return array
  656. */
  657. public function parseField($fields)
  658. {
  659. if (empty($fields)) {
  660. $fields = array();
  661. }
  662. if (is_string($fields)) {
  663. $_fields = explode(',', $fields);
  664. $fields = array();
  665. foreach ($_fields as $f) {
  666. $fields[$f] = true;
  667. }
  668. } elseif (is_array($fields)) {
  669. $_fields = $fields;
  670. $fields = array();
  671. foreach ($_fields as $f => $v) {
  672. if (is_numeric($f)) {
  673. $fields[$v] = true;
  674. } else {
  675. $fields[$f] = $v ? true : false;
  676. }
  677. }
  678. }
  679. return $fields;
  680. }
  681. /**
  682. * where分析
  683. * @access protected
  684. * @param mixed $where
  685. * @return array
  686. */
  687. public function parseWhere($where)
  688. {
  689. $query = array();
  690. $return = array();
  691. $_logic = '$and';
  692. if (isset($where['_logic'])) {
  693. $where['_logic'] = strtolower($where['_logic']);
  694. $_logic = in_array($where['_logic'], array('or', 'xor', 'nor', 'and')) ? '$' . $where['_logic'] : $_logic;
  695. unset($where['_logic']);
  696. }
  697. foreach ($where as $key => $val) {
  698. if ('_id' != $key && 0 === strpos($key, '_')) {
  699. // 解析特殊条件表达式
  700. $parse = $this->parseThinkWhere($key, $val);
  701. $query = array_merge($query, $parse);
  702. } else {
  703. // 查询字段的安全过滤
  704. if (!preg_match('/^[A-Z_\|\&\-.a-z0-9]+$/', trim($key))) {
  705. E(L('_ERROR_QUERY_') . ':' . $key);
  706. }
  707. $key = trim($key);
  708. if (strpos($key, '|')) {
  709. $array = explode('|', $key);
  710. $str = array();
  711. foreach ($array as $k) {
  712. $str[] = $this->parseWhereItem($k, $val);
  713. }
  714. $query['$or'] = $str;
  715. } elseif (strpos($key, '&')) {
  716. $array = explode('&', $key);
  717. $str = array();
  718. foreach ($array as $k) {
  719. $str[] = $this->parseWhereItem($k, $val);
  720. }
  721. $query = array_merge($query, $str);
  722. } else {
  723. $str = $this->parseWhereItem($key, $val);
  724. $query = array_merge($query, $str);
  725. }
  726. }
  727. }
  728. if ('$and' == $_logic) {
  729. return $query;
  730. }
  731. foreach ($query as $key => $val) {
  732. $return[$_logic][] = array($key => $val);
  733. }
  734. return $return;
  735. }
  736. /**
  737. * 特殊条件分析
  738. * @access protected
  739. * @param string $key
  740. * @param mixed $val
  741. * @return string
  742. */
  743. protected function parseThinkWhere($key, $val)
  744. {
  745. $query = array();
  746. $_logic = array('or', 'xor', 'nor', 'and');
  747. switch ($key) {
  748. case '_query': // 字符串模式查询条件
  749. parse_str($val, $query);
  750. if (isset($query['_logic']) && strtolower($query['_logic']) == 'or') {
  751. unset($query['_logic']);
  752. $query['$or'] = $query;
  753. }
  754. break;
  755. case '_complex': // 子查询模式查询条件
  756. $__logic = strtolower($val['_logic']);
  757. if (isset($val['_logic']) && in_array($__logic, $_logic)) {
  758. unset($val['_logic']);
  759. $query['$' . $__logic] = $val;
  760. }
  761. break;
  762. case '_string': // MongoCode查询
  763. $query['$where'] = new \MongoCode($val);
  764. break;
  765. }
  766. //兼容 MongoClient OR条件查询方法
  767. if (isset($query['$or']) && !is_array(current($query['$or']))) {
  768. $val = array();
  769. foreach ($query['$or'] as $k => $v) {
  770. $val[] = array($k => $v);
  771. }
  772. $query['$or'] = $val;
  773. }
  774. return $query;
  775. }
  776. /**
  777. * where子单元分析
  778. * @access protected
  779. * @param string $key
  780. * @param mixed $val
  781. * @return array
  782. */
  783. protected function parseWhereItem($key, $val)
  784. {
  785. $query = array();
  786. if (is_array($val)) {
  787. if (is_string($val[0])) {
  788. $con = strtolower($val[0]);
  789. if (in_array($con, array('neq', 'ne', 'gt', 'egt', 'gte', 'lt', 'lte', 'elt'))) {
  790. // 比较运算
  791. $k = '$' . $this->comparison[$con];
  792. $query[$key] = array($k => $val[1]);
  793. } elseif ('like' == $con) {
  794. // 模糊查询 采用正则方式
  795. $query[$key] = new \MongoRegex("/" . $val[1] . "/");
  796. } elseif ('mod' == $con) {
  797. // mod 查询
  798. $query[$key] = array('$mod' => $val[1]);
  799. } elseif ('regex' == $con) {
  800. // 正则查询
  801. $query[$key] = new \MongoRegex($val[1]);
  802. } elseif (in_array($con, array('in', 'nin', 'not in'))) {
  803. // IN NIN 运算
  804. $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
  805. $k = '$' . $this->comparison[$con];
  806. $query[$key] = array($k => $data);
  807. } elseif ('all' == $con) {
  808. // 满足所有指定条件
  809. $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
  810. $query[$key] = array('$all' => $data);
  811. } elseif ('between' == $con) {
  812. // BETWEEN运算
  813. $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
  814. $query[$key] = array('$gte' => $data[0], '$lte' => $data[1]);
  815. } elseif ('not between' == $con) {
  816. $data = is_string($val[1]) ? explode(',', $val[1]) : $val[1];
  817. $query[$key] = array('$lt' => $data[0], '$gt' => $data[1]);
  818. } elseif ('exp' == $con) {
  819. // 表达式查询
  820. $query['$where'] = new \MongoCode($val[1]);
  821. } elseif ('exists' == $con) {
  822. // 字段是否存在
  823. $query[$key] = array('$exists' => (bool) $val[1]);
  824. } elseif ('size' == $con) {
  825. // 限制属性大小
  826. $query[$key] = array('$size' => intval($val[1]));
  827. } elseif ('type' == $con) {
  828. // 限制字段类型 1 浮点型 2 字符型 3 对象或者MongoDBRef 5 MongoBinData 7 MongoId 8 布尔型 9 MongoDate 10 NULL 15 MongoCode 16 32位整型 17 MongoTimestamp 18 MongoInt64 如果是数组的话判断元素的类型
  829. $query[$key] = array('$type' => intval($val[1]));
  830. } else {
  831. $query[$key] = $val;
  832. }
  833. return $query;
  834. }
  835. }
  836. $query[$key] = $val;
  837. return $query;
  838. }
  839. }