Completed
Push — master ( c07009...b2c37d )
by Nicolas
02:48 queued 26s
created

Scroll::rewind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 1
eloc 8
nc 1
nop 0
1
<?php
2
namespace Elastica;
3
4
/**
5
 * Scroll Iterator.
6
 *
7
 * @author Manuel Andreo Garcia <[email protected]>
8
 *
9
 * @link https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html
10
 */
11
class Scroll implements \Iterator
12
{
13
    /**
14
     * @var string
15
     */
16
    public $expiryTime;
17
18
    /**
19
     * @var Search
20
     */
21
    protected $_search;
22
23
    /**
24
     * @var null|string
25
     */
26
    protected $_nextScrollId;
27
28
    /**
29
     * @var null|ResultSet
30
     */
31
    protected $_currentResultSet;
32
33
    /**
34
     * 0: scroll<br>
35
     * 1: scroll id.
36
     *
37
     * @var array
38
     */
39
    protected $_options = [null, null];
40
41
    private $totalPages = 0;
42
    private $currentPage = 0;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @param Search $search
48
     * @param string $expiryTime
49
     */
50
    public function __construct(Search $search, $expiryTime = '1m')
51
    {
52
        $this->_search = $search;
53
        $this->expiryTime = $expiryTime;
54
    }
55
56
    /**
57
     * Returns current result set.
58
     *
59
     * @link http://php.net/manual/en/iterator.current.php
60
     *
61
     * @return ResultSet
62
     */
63
    public function current()
64
    {
65
        return $this->_currentResultSet;
66
    }
67
68
    /**
69
     * Next scroll search.
70
     *
71
     * @link http://php.net/manual/en/iterator.next.php
72
     */
73
    public function next()
74
    {
75
        if ($this->currentPage < $this->totalPages) {
76
            $this->_saveOptions();
77
78
            $this->_search->setOption(Search::OPTION_SCROLL, $this->expiryTime);
79
            $this->_search->setOption(Search::OPTION_SCROLL_ID, $this->_nextScrollId);
80
81
            $this->_setScrollId($this->_search->search());
82
83
            $this->_revertOptions();
84
        } else {
85
            // If there are no pages left, we do not need to query ES.
86
            $this->clear();
87
        }
88
    }
89
90
    /**
91
     * Returns scroll id.
92
     *
93
     * @link http://php.net/manual/en/iterator.key.php
94
     *
95
     * @return string
96
     */
97
    public function key()
98
    {
99
        return $this->_nextScrollId;
100
    }
101
102
    /**
103
     * Returns true if current result set contains at least one hit.
104
     *
105
     * @link http://php.net/manual/en/iterator.valid.php
106
     *
107
     * @return bool
108
     */
109
    public function valid()
110
    {
111
        return $this->_nextScrollId !== null;
112
    }
113
114
    /**
115
     * Initial scroll search.
116
     *
117
     * @link http://php.net/manual/en/iterator.rewind.php
118
     */
119
    public function rewind()
120
    {
121
        // reset state
122
        $this->_options = [null, null];
123
        $this->currentPage = 0;
124
125
        // initial search
126
        $this->_saveOptions();
127
128
        $this->_search->setOption(Search::OPTION_SCROLL, $this->expiryTime);
129
        $this->_search->setOption(Search::OPTION_SCROLL_ID, null);
130
        $this->_setScrollId($this->_search->search());
131
132
        $this->_revertOptions();
133
    }
134
135
    /**
136
     * Cleares the search context on ES and marks this Scroll instance as finished.
137
     */
138
    public function clear()
139
    {
140
        if (null !== $this->_nextScrollId) {
141
            $this->_search->getClient()->request(
142
                '_search/scroll',
143
                Request::DELETE,
144
                [Search::OPTION_SCROLL_ID => [$this->_nextScrollId]]
145
            );
146
147
            // Reset scroll ID so valid() returns false.
148
            $this->_nextScrollId = null;
149
            $this->_currentResultSet = null;
150
        }
151
    }
152
153
    /**
154
     * Prepares Scroll for next request.
155
     *
156
     * @param ResultSet $resultSet
157
     */
158
    protected function _setScrollId(ResultSet $resultSet)
159
    {
160
        if ($this->currentPage === 0) {
161
            $this->totalPages = $resultSet->count() > 0 ? ceil($resultSet->getTotalHits() / $resultSet->count()) : 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like $resultSet->count() > 0 ...resultSet->count()) : 0 can also be of type double. However, the property $totalPages is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
162
        }
163
164
        $this->_currentResultSet = $resultSet;
165
        ++$this->currentPage;
166
        $this->_nextScrollId = $resultSet->getResponse()->isOk() && $resultSet->count() > 0 ? $resultSet->getResponse()->getScrollId() : null;
167
    }
168
169
    /**
170
     * Save all search options manipulated by Scroll.
171
     */
172
    protected function _saveOptions()
173
    {
174 View Code Duplication
        if ($this->_search->hasOption(Search::OPTION_SCROLL)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
            $this->_options[0] = $this->_search->getOption(Search::OPTION_SCROLL);
176
        }
177
178 View Code Duplication
        if ($this->_search->hasOption(Search::OPTION_SCROLL_ID)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
            $this->_options[1] = $this->_search->getOption(Search::OPTION_SCROLL_ID);
180
        }
181
    }
182
183
    /**
184
     * Revert search options to previously saved state.
185
     */
186
    protected function _revertOptions()
187
    {
188
        $this->_search->setOption(Search::OPTION_SCROLL, $this->_options[0]);
189
        $this->_search->setOption(Search::OPTION_SCROLL_ID, $this->_options[1]);
190
    }
191
}
192