Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

601 linhas
12 KiB

  1. <?php
  2. /**
  3. * 对MongoDB中集合的查询
  4. *
  5. * MongoDB中的数据类型是严格对应的,所以要注意整数和字符串的区别:
  6. * - attr("state", 0);
  7. * - attr("state", "0");
  8. * 两个查询是不一样的
  9. *
  10. */
  11. class RQuery {
  12. /**
  13. * Enter description here...
  14. *
  15. * @var MongoDB
  16. */
  17. private $_db;
  18. private $_dbName;
  19. /**
  20. * Enter description here...
  21. *
  22. * @var MongoCollection
  23. */
  24. private $_collection;
  25. private $_collectionName;
  26. private $_attrs = array();//field1 => array(..), field2 => array(...) ...
  27. private $_results = array();//field => [1|0]
  28. private $_sort = array();//field => [1|-1]
  29. private $_offset = -1;
  30. private $_limit = 0;
  31. private $_conds = array();//field1 => array( '$lt' => value1, .. )
  32. private $_noPk = false;
  33. private $_hints = array();
  34. /**
  35. * 构造查询
  36. *
  37. * @param Mongo $mongo MongoDB连接
  38. * @param string $db 数据库
  39. * @param string $collection 集合
  40. */
  41. function __construct(RMongo $mongo, $db, $collection) {
  42. $this->_dbName = $db;
  43. $this->_collectionName = $collection;
  44. $this->_db = $mongo->selectDB($this->_dbName);
  45. $this->_collection = $mongo->selectCollection($this->_dbName, $this->_collectionName);
  46. }
  47. /**
  48. * 指定属性值
  49. *
  50. * @param string|array $nameOrAttrs 属性名或一组属性值
  51. * @param string $value 属性值
  52. * @return RQuery
  53. */
  54. function attr($nameOrAttrs, $value = null) {
  55. if (!is_array($nameOrAttrs)) {
  56. $nameOrAttrs = array( $nameOrAttrs => $value );
  57. }
  58. foreach ($nameOrAttrs as $attr => $value) {
  59. if ($attr == "_id" && (!is_object($value) || !($value instanceof MongoId)) && strlen($attr) == 24) {
  60. $value = new MongoId($value);
  61. }
  62. if (!isset($this->_attrs[$attr])) {
  63. $this->_attrs[$attr] = array();
  64. }
  65. if (is_array($value)) {
  66. $this->_attrs[$attr] = array_merge($this->_attrs[$attr], array($value));
  67. }
  68. else {
  69. $this->_attrs[$attr][] = $value;
  70. }
  71. }
  72. return $this;
  73. }
  74. /**
  75. * 指定返回的结果属性。在当前的mongo版本中(<1.4.x),不能混合使用result()和exclude()
  76. *
  77. * @param string $attr1 第一个属性
  78. * @param ...
  79. * @return RQuery
  80. */
  81. function result($attr1 = null) {
  82. foreach (func_get_args() as $arg) {
  83. if ($arg) {
  84. if (is_array($arg)) {
  85. foreach ($arg as $v) {
  86. $this->_results[$v] = 1;
  87. }
  88. }
  89. else if (strstr($arg, ",")) {
  90. foreach (preg_split("/\s*,\s*/", $arg) as $attr) {
  91. $this->_results[$attr] = 1;
  92. }
  93. }
  94. else {
  95. $this->_results[$arg] = 1;
  96. }
  97. }
  98. }
  99. return $this;
  100. }
  101. /**
  102. * 指定返回的结果中要排除的属性
  103. *
  104. * @param string $attr1 第一个属性
  105. * @param ...
  106. * @return RQuery
  107. */
  108. function exclude($attr1 = null) {
  109. foreach (func_get_args() as $arg) {
  110. if ($arg) {
  111. if (strstr($arg, ",")) {
  112. foreach (preg_split("/\s*,\s*/", $arg) as $attr) {
  113. $this->_results[$attr] = 0;
  114. }
  115. }
  116. else {
  117. $this->_results[$arg] = 0;
  118. }
  119. }
  120. }
  121. return $this;
  122. }
  123. /**
  124. * 设置正排序条件
  125. *
  126. * @param string $attr 需要排序的属性
  127. * @return RQuery
  128. */
  129. function asc($attr = "_id") {
  130. $this->_sort[$attr] = 1;
  131. return $this;
  132. }
  133. /**
  134. * 设置倒排序条件
  135. *
  136. * @param string $attr 需要排序的属性
  137. * @return RQuery
  138. */
  139. function desc($attr = "_id") {
  140. $this->_sort[$attr] = -1;
  141. return $this;
  142. }
  143. /**
  144. * 设置记录开始的位置
  145. *
  146. * @param integer $offset 开始位置
  147. * @return RQuery
  148. */
  149. function offset($offset) {
  150. $this->_offset = intval($offset);
  151. return $this;
  152. }
  153. /**
  154. * 设置需要查询的记录行数
  155. *
  156. * @param integer $size 行数
  157. * @return RQuery
  158. */
  159. function limit($size) {
  160. $this->_limit = intval($size);
  161. return $this;
  162. }
  163. /**
  164. * 增加查询条件
  165. *
  166. * @param array $cond 查询条件
  167. * @return RQuery
  168. */
  169. function cond(array $cond) {
  170. foreach ($cond as $key => $value) {
  171. $this->_conds[$key] = $value;
  172. }
  173. return $this;
  174. }
  175. /**
  176. * 添加操作符
  177. *
  178. * @param string $attr 属性名
  179. * @param string $operator 操作符,比如$gt, $lt ...
  180. * @param mixed $value 操作符对应的值
  181. * @return RQuery
  182. */
  183. function operator($attr, $operator, $value) {
  184. if (!isset($this->_conds[$attr])) {
  185. $this->_conds[$attr] = array();
  186. }
  187. $this->_conds[$attr][$operator] = $value;
  188. return $this;
  189. }
  190. function gt($attr, $value) {
  191. return $this->operator($attr, '$gt', $value);
  192. }
  193. function lt($attr, $value) {
  194. return $this->operator($attr, '$lt', $value);
  195. }
  196. function gte($attr, $value) {
  197. return $this->operator($attr, '$gte', $value);
  198. }
  199. function lte($attr, $value) {
  200. return $this->operator($attr, '$lte', $value);
  201. }
  202. /**
  203. * 设置不等于(!=)条件
  204. *
  205. * @param string $attr 属性名
  206. * @param mixed $value 和属性比较的值
  207. * @return RQuery
  208. */
  209. function ne($attr, $value) {
  210. return $this->operator($attr, '$ne', $value);
  211. }
  212. function in($attr, array $values) {
  213. return $this->operator($attr, '$in', $values);
  214. }
  215. function nin($attr, array $values) {
  216. return $this->operator($attr, '$nin', $values);
  217. }
  218. function mod($attr, $value) {
  219. return $this->operator($attr, '$mod', $value);
  220. }
  221. function all($attr, $value) {
  222. return $this->operator($attr, '$all', $value);
  223. }
  224. function contains($attr, $value) {
  225. return $this->all($attr, '$all', $value);
  226. }
  227. /**
  228. * 限定集合属性的尺寸
  229. *
  230. * @param string $attr 属性名
  231. * @param integer $size 限制的尺寸
  232. * @return RQuery
  233. */
  234. function size($attr, $size) {
  235. return $this->operator($attr, '$size', intval($size));
  236. }
  237. function exists($attr, $bool = true) {
  238. return $this->operator($attr, '$exists', $bool);
  239. }
  240. function type($attr, $type) {
  241. return $this->operator($attr, '$type', intval($type));
  242. }
  243. function match($attr, $regexp) {
  244. return $this->attr($attr, new MongoRegex($regexp));
  245. }
  246. /**
  247. * 分割一个是集合(相当于PHP中的索引数组)的属性值
  248. *
  249. * @param string $attr 属性名
  250. * @param integer $subOffset 开始位置
  251. * @param integer $subLimit 要取出的条数
  252. * @return RQuery
  253. */
  254. function slice($attr, $subOffset, $subLimit) {
  255. $this->_results[$attr]['$slice'] = array( intval($subOffset), intval($subLimit) );
  256. return $this;
  257. }
  258. /**
  259. * 使用函数。如果数据较多话,将会非常慢。
  260. *
  261. * @param string $func Javascript函数
  262. * @return RQuery
  263. */
  264. function func($func) {
  265. $this->_conds['$where'] = $func;
  266. return $this;
  267. }
  268. /**
  269. * 设置否是不返回主键(_id)
  270. *
  271. * @param unknown_type $returnPk
  272. * @return RQuery
  273. */
  274. function noPk($noPk = true) {
  275. $this->_noPk = $noPk;
  276. return $this;
  277. }
  278. /**
  279. * 设置查询的主键值
  280. *
  281. * @param string $pk1 主键1
  282. * @param string ...
  283. * @return RQuery
  284. */
  285. function id($pk1) {
  286. foreach (func_get_args() as $arg) {
  287. $this->attr("_id", $arg);
  288. }
  289. return $this;
  290. }
  291. function copy() {
  292. exit(__METHOD__ . "() to be implemented");
  293. }
  294. /**
  295. * 现有查询条件的组合
  296. *
  297. * @return array
  298. */
  299. function criteria() {
  300. $attrs = $this->_attrs;
  301. foreach ($this->_attrs as $attr => $values) {
  302. if (!empty($values)) {
  303. if (count($values) == 1) {
  304. $attrs[$attr] = $values[0];
  305. }
  306. else {
  307. $attrs[$attr]['$in'] = $values;
  308. }
  309. }
  310. }
  311. foreach ($this->_conds as $key => $value) {
  312. $attrs[$key] = $value;
  313. }
  314. return $attrs;
  315. }
  316. /**
  317. * add hints for query
  318. *
  319. * @param unknown_type $hint
  320. * @return RQuery
  321. */
  322. function hint($hint) {
  323. $this->_hints[] = $hint;
  324. return $this;
  325. }
  326. /**
  327. * 取得当前查询的游标
  328. *
  329. * @return MongoCursor
  330. */
  331. function cursor() {
  332. $cursor = $this->_collection->find($this->criteria(), $this->_results);
  333. if ($this->_offset >= 0) {
  334. $cursor->skip($this->_offset);
  335. }
  336. if ($this->_limit > 0) {
  337. $cursor->limit($this->_limit);
  338. }
  339. if ($this->_sort) {
  340. $cursor->sort($this->_sort);
  341. }
  342. if (!empty($this->_hints)) {
  343. foreach ($this->_hints as $hint) {
  344. $cursor->hint($hint);
  345. }
  346. }
  347. return $cursor;
  348. }
  349. /**
  350. * 查找一行数据,并以一个对象的形式返回
  351. *
  352. * @param string $id 主键_id值
  353. * @return RObject
  354. */
  355. function find($id = null) {
  356. if (!is_null($id)) {
  357. $this->id($id);
  358. }
  359. $row = $this->findOne();
  360. if (empty($row)) {
  361. return null;
  362. }
  363. import("@.RObject");
  364. $obj = new RObject();
  365. $obj->setSource($row);
  366. $obj->setCollection($this->_collection);
  367. $obj->setId($row["_id"]);
  368. return $obj;
  369. }
  370. /**
  371. * 查找一行数据,以数组的形式返回
  372. *
  373. * @param string $id 主键_id值
  374. * @return array
  375. */
  376. function findOne($id = null) {
  377. if (!is_null($id)) {
  378. $this->id($id);
  379. }
  380. $this->limit(1);
  381. $all = $this->findAll();
  382. return empty($all) ? array() : $all[0];
  383. }
  384. /**
  385. * 查找一行数据,但只返回ID数据
  386. *
  387. * @param string $id 主键_id值
  388. * @return RObjct
  389. */
  390. function findId($id = null) {
  391. if (!is_null($id)) {
  392. $this->id($id);
  393. }
  394. $this->_results = array();
  395. $this->_results["_id"] = 1;
  396. return $this->find();
  397. }
  398. /**
  399. * 根据请求的参数查询数据
  400. *
  401. * @param string $requestPkName 值为主键值的请求参数
  402. * @return RObject
  403. */
  404. function findx($requestPkName = "id") {
  405. return $this->find(x($requestPkName));
  406. }
  407. /**
  408. * 取出所有记录
  409. *
  410. * @param boolean $keepId 是否保留ID的原始状态
  411. * @return array
  412. */
  413. function findAll($keepId = true) {
  414. $rets = array();
  415. foreach ($this->cursor() as $value) {
  416. if ($this->_noPk) {
  417. unset($value["_id"]);
  418. }
  419. else {
  420. if (!$keepId && isset($value["_id"]) && ($value["_id"] instanceof MongoId)) {
  421. $value["_id"] = $value["_id"]->__toString();
  422. }
  423. }
  424. $rets[] = $value;
  425. }
  426. return $rets;
  427. }
  428. /**
  429. * 分页
  430. *
  431. * @param array $params 分页参数
  432. * @param string $style 分页样式
  433. * @return RPage
  434. */
  435. function page(array $params = array(), $style = null) {
  436. import("RPage");
  437. if (!class_exists("RPage")) {
  438. exit("You must use the method " . __METHOD__ . "() with page plugin.");
  439. }
  440. $page = RPage::pageWithStyle($style, $params);
  441. $this->offset($page->offset());
  442. $this->limit($page->size());
  443. $page->setRows($this->findAll());
  444. return $page;
  445. }
  446. /**
  447. * 计算符合条件的行数
  448. *
  449. * @param boolean $withLimit limit()/offset()方法是否有效
  450. * @return integer
  451. */
  452. function count($withLimit = false) {
  453. return $this->cursor()->count($withLimit);
  454. }
  455. /**
  456. * Insert new record
  457. *
  458. * @param array $attrs attributes of new record
  459. * @param boolean $safe check result
  460. * @return boolean
  461. */
  462. function insert(array $attrs, $safe = false) {
  463. $bool = $this->_collection->insert($attrs, array( "safe" => $safe ));
  464. if ($bool) {
  465. import("@.RMongo");
  466. if ($attrs["_id"] instanceof MongoId) {
  467. RMongo::setLastInsertId($attrs["_id"]->__toString());
  468. }
  469. else {
  470. RMongo::setLastInsertId($attrs["_id"]);
  471. }
  472. }
  473. return $bool;
  474. }
  475. /**
  476. * 插入新的行,_id是上一行的ID加1
  477. *
  478. * @param array $attrs 新行的属性集
  479. * @return boolean
  480. */
  481. function insertNext(array $attrs) {
  482. $response = $this->_db->execute('function insertObject(o, myCollection) {
  483. var x = db.getCollection(myCollection);
  484. while( 1 ) {
  485. // determine next _id value to try
  486. var c = x.find({},{_id:1}).sort({_id:-1}).limit(1);
  487. var i = c.hasNext() ? c.next()._id + 1 : 1;
  488. o._id = i;
  489. x.insert(o);
  490. var err = db.getLastErrorObj();
  491. if( err && err.code ) {
  492. if( err.code == 11000 /* dup key */ )
  493. continue;
  494. else
  495. print("unexpected error inserting data: " + tojson(err));
  496. }
  497. break;
  498. }
  499. return o._id;
  500. }', array( $attrs, $this->_collectionName ));
  501. if ($response["ok"]) {
  502. import("@.RMongo");
  503. RMongo::setLastInsertId($response["retval"]);
  504. }
  505. return $response["ok"];
  506. }
  507. /**
  508. * 删除符合条件的记录
  509. *
  510. * @return boolean
  511. */
  512. function delete() {
  513. return $this->_collection->remove($this->criteria());
  514. }
  515. /**
  516. * 更改或插入新的对象
  517. *
  518. * 在当前驱动下不能正常工作
  519. *
  520. * @param array $obj 新的对象
  521. * @return boolean
  522. */
  523. function upsert(array $obj) {
  524. return $this->_collection->update($this->criteria(), $obj, array( "upsert" => true, "multiple" => true));
  525. }
  526. /**
  527. * 批量插入一组新的数据
  528. *
  529. * @param array $array 每一个元素包含一个要插入的行
  530. * @return boolean
  531. */
  532. function batchInsert(array $array) {
  533. return $this->_collection->batchInsert($array);
  534. }
  535. /**
  536. * 当前操作的集合
  537. *
  538. * @return MongoCollection
  539. */
  540. function collection() {
  541. return $this->_collection;
  542. }
  543. /**
  544. * 当前操作的数据库
  545. *
  546. * @return MongoDB
  547. */
  548. function db() {
  549. return $this->_db;
  550. }
  551. }
  552. ?>