1<?php
2
3declare(strict_types = 1);
4
5namespace Elasticsearch\Helper\Iterators;
6
7use Elasticsearch\Client;
8use Iterator;
9
10/**
11 * Class SearchResponseIterator
12 *
13 * @category Elasticsearch
14 * @package  Elasticsearch\Helper\Iterators
15 * @author   Arturo Mejia <arturo.mejia@kreatetechnology.com>
16 * @license  http://www.apache.org/licenses/LICENSE-2.0 Apache2
17 * @link     http://elastic.co
18 * @see      Iterator
19 */
20class SearchResponseIterator implements Iterator
21{
22
23    /**
24     * @var Client
25     */
26    private $client;
27
28    /**
29     * @var array
30     */
31    private $params;
32
33    /**
34     * @var int
35     */
36    private $current_key = 0;
37
38    /**
39     * @var array
40     */
41    private $current_scrolled_response;
42
43    /**
44     * @var string
45     */
46    private $scroll_id;
47
48    /**
49     * @var string duration
50     */
51    private $scroll_ttl;
52
53    /**
54     * Constructor
55     *
56     * @param Client $client
57     * @param array  $search_params Associative array of parameters
58     * @see   Client::search()
59     */
60    public function __construct(Client $client, array $search_params)
61    {
62        $this->client = $client;
63        $this->params = $search_params;
64
65        if (isset($search_params['scroll'])) {
66            $this->scroll_ttl = $search_params['scroll'];
67        }
68    }
69
70    /**
71     * Destructor
72     */
73    public function __destruct()
74    {
75        $this->clearScroll();
76    }
77
78    /**
79     * Sets the time to live duration of a scroll window
80     *
81     * @param  string $time_to_live
82     * @return $this
83     */
84    public function setScrollTimeout(string $time_to_live): SearchResponseIterator
85    {
86        $this->scroll_ttl = $time_to_live;
87        return $this;
88    }
89
90    /**
91     * Clears the current scroll window if there is a scroll_id stored
92     *
93     * @return void
94     */
95    private function clearScroll(): void
96    {
97        if (!empty($this->scroll_id)) {
98            $this->client->clearScroll(
99                array(
100                    'scroll_id' => $this->scroll_id,
101                    'client' => array(
102                        'ignore' => 404
103                    )
104                )
105            );
106            $this->scroll_id = null;
107        }
108    }
109
110    /**
111     * Rewinds the iterator by performing the initial search.
112     *
113     * @return void
114     * @see    Iterator::rewind()
115     */
116    public function rewind(): void
117    {
118        $this->clearScroll();
119        $this->current_key = 0;
120        $this->current_scrolled_response = $this->client->search($this->params);
121        $this->scroll_id = $this->current_scrolled_response['_scroll_id'];
122    }
123
124    /**
125     * Fetches every "page" after the first one using the lastest "scroll_id"
126     *
127     * @return void
128     * @see    Iterator::next()
129     */
130    public function next(): void
131    {
132        $this->current_scrolled_response = $this->client->scroll(
133            [
134            'scroll_id' => $this->scroll_id,
135            'scroll'    => $this->scroll_ttl
136            ]
137        );
138        $this->scroll_id = $this->current_scrolled_response['_scroll_id'];
139        $this->current_key++;
140    }
141
142    /**
143     * Returns a boolean value indicating if the current page is valid or not
144     *
145     * @return bool
146     * @see    Iterator::valid()
147     */
148    public function valid(): bool
149    {
150        return isset($this->current_scrolled_response['hits']['hits'][0]);
151    }
152
153    /**
154     * Returns the current "page"
155     *
156     * @return array
157     * @see    Iterator::current()
158     */
159    public function current(): array
160    {
161        return $this->current_scrolled_response;
162    }
163
164    /**
165     * Returns the current "page number" of the current "page"
166     *
167     * @return int
168     * @see    Iterator::key()
169     */
170    public function key(): int
171    {
172        return $this->current_key;
173    }
174}
175