BatchQueryResult.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\mongodb;
  8. use yii\base\BaseObject;
  9. use Yii;
  10. /**
  11. * BatchQueryResult represents a batch query from which you can retrieve data in batches.
  12. *
  13. * You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by
  14. * calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the `Iterator` interface,
  15. * you can iterate it to obtain a batch of data in each iteration. For example,
  16. *
  17. * ```php
  18. * $query = (new Query())->from('user');
  19. * foreach ($query->batch() as $i => $users) {
  20. * // $users represents the rows in the $i-th batch
  21. * }
  22. * foreach ($query->each() as $user) {
  23. * }
  24. * ```
  25. *
  26. * @author Paul Klimov <klimov.paul@gmail.com>
  27. * @since 2.1
  28. */
  29. class BatchQueryResult extends BaseObject implements \Iterator
  30. {
  31. /**
  32. * @var Connection the MongoDB connection to be used when performing batch query.
  33. * If null, the "mongodb" application component will be used.
  34. */
  35. public $db;
  36. /**
  37. * @var Query the query object associated with this batch query.
  38. * Do not modify this property directly unless after [[reset()]] is called explicitly.
  39. */
  40. public $query;
  41. /**
  42. * @var int the number of rows to be returned in each batch.
  43. */
  44. public $batchSize = 100;
  45. /**
  46. * @var bool whether to return a single row during each iteration.
  47. * If false, a whole batch of rows will be returned in each iteration.
  48. */
  49. public $each = false;
  50. /**
  51. * @var array the data retrieved in the current batch
  52. */
  53. private $_batch;
  54. /**
  55. * @var mixed the value for the current iteration
  56. */
  57. private $_value;
  58. /**
  59. * @var string|int the key for the current iteration
  60. */
  61. private $_key;
  62. /**
  63. * @var \Iterator
  64. */
  65. private $_iterator;
  66. /**
  67. * Resets the batch query.
  68. * This method will clean up the existing batch query so that a new batch query can be performed.
  69. */
  70. public function reset()
  71. {
  72. $this->_iterator = null;
  73. $this->_batch = null;
  74. $this->_value = null;
  75. $this->_key = null;
  76. }
  77. /**
  78. * Resets the iterator to the initial state.
  79. * This method is required by the interface Iterator.
  80. */
  81. public function rewind()
  82. {
  83. $this->reset();
  84. $this->next();
  85. }
  86. /**
  87. * Moves the internal pointer to the next dataset.
  88. * This method is required by the interface Iterator.
  89. */
  90. public function next()
  91. {
  92. if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
  93. $this->_batch = $this->fetchData();
  94. reset($this->_batch);
  95. }
  96. if ($this->each) {
  97. $this->_value = current($this->_batch);
  98. if ($this->query->indexBy !== null) {
  99. $this->_key = key($this->_batch);
  100. } elseif (key($this->_batch) !== null) {
  101. $this->_key++;
  102. } else {
  103. $this->_key = null;
  104. }
  105. } else {
  106. $this->_value = $this->_batch;
  107. $this->_key = $this->_key === null ? 0 : $this->_key + 1;
  108. }
  109. }
  110. /**
  111. * Fetches the next batch of data.
  112. * @return array the data fetched
  113. */
  114. protected function fetchData()
  115. {
  116. if ($this->_iterator === null) {
  117. if (empty($this->query->orderBy)) {
  118. // setting cursor batch size may setup implicit limit on the query with 'sort'
  119. // @see https://jira.mongodb.org/browse/PHP-457
  120. $this->query->addOptions(['batchSize' => $this->batchSize]);
  121. }
  122. $cursor = $this->query->buildCursor($this->db);
  123. $token = 'fetch cursor id = ' . $cursor->getId();
  124. Yii::info($token, __METHOD__);
  125. if ($cursor instanceof \Iterator) {
  126. $this->_iterator = $cursor;
  127. } else {
  128. $this->_iterator = new \IteratorIterator($cursor);
  129. }
  130. $this->_iterator->rewind();
  131. }
  132. $rows = [];
  133. $count = 0;
  134. while ($count++ < $this->batchSize) {
  135. $row = $this->_iterator->current();
  136. if ($row === null) {
  137. break;
  138. }
  139. $this->_iterator->next();
  140. //var_dump($row);
  141. $rows[] = $row;
  142. }
  143. return $this->query->populate($rows);
  144. }
  145. /**
  146. * Returns the index of the current dataset.
  147. * This method is required by the interface Iterator.
  148. * @return int the index of the current row.
  149. */
  150. public function key()
  151. {
  152. return $this->_key;
  153. }
  154. /**
  155. * Returns the current dataset.
  156. * This method is required by the interface Iterator.
  157. * @return mixed the current dataset.
  158. */
  159. public function current()
  160. {
  161. return $this->_value;
  162. }
  163. /**
  164. * Returns whether there is a valid dataset at the current position.
  165. * This method is required by the interface Iterator.
  166. * @return bool whether there is a valid dataset at the current position.
  167. */
  168. public function valid()
  169. {
  170. return !empty($this->_batch);
  171. }
  172. }