Skip to content

Commit f536eab

Browse files
committed
iterator helpers revised for es 8.x
1 parent 3058023 commit f536eab

File tree

4 files changed

+688
-0
lines changed

4 files changed

+688
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Iterators;
16+
17+
use Iterator;
18+
19+
class SearchHitIterator implements Iterator, \Countable
20+
{
21+
22+
/**
23+
* @var SearchResponseIterator
24+
*/
25+
private SearchResponseIterator $searchResponses;
26+
27+
/**
28+
* @var int
29+
*/
30+
protected int $currentKey;
31+
32+
/**
33+
* @var int
34+
*/
35+
protected int $currentHitIndex;
36+
37+
/**
38+
* @var array|null
39+
*/
40+
protected ?array $currentHitData;
41+
42+
/**
43+
* @var int
44+
*/
45+
protected int $count = 0;
46+
47+
/**
48+
* Constructor
49+
*
50+
* @param SearchResponseIterator $searchResponses
51+
*/
52+
public function __construct(SearchResponseIterator $searchResponses)
53+
{
54+
$this->searchResponses = $searchResponses;
55+
}
56+
57+
/**
58+
* Rewinds the internal SearchResponseIterator and itself
59+
*
60+
* @return void
61+
* @see Iterator::rewind()
62+
*/
63+
public function rewind(): void
64+
{
65+
$this->currentKey = 0;
66+
$this->searchResponses->rewind();
67+
68+
// The first page may be empty. In that case, the next page is fetched.
69+
$current_page = $this->searchResponses->current();
70+
if ($this->searchResponses->valid() && empty($current_page['hits']['hits'])) {
71+
$this->searchResponses->next();
72+
}
73+
74+
$this->count = 0;
75+
if (isset($current_page['hits']) && isset($current_page['hits']['total'])) {
76+
$this->count = $current_page['hits']['total']['value'] ?? $current_page['hits']['total'];
77+
}
78+
79+
$this->readPageData();
80+
}
81+
82+
/**
83+
* Advances pointer of the current hit to the next one in the current page. If there
84+
* isn't a next hit in the current page, then it advances the current page and moves the
85+
* pointer to the first hit in the page.
86+
*
87+
* @return void
88+
* @see Iterator::next()
89+
*/
90+
public function next(): void
91+
{
92+
$this->currentKey++;
93+
$this->currentHitIndex++;
94+
$current_page = $this->searchResponses->current();
95+
if (isset($current_page['hits']['hits'][$this->currentHitIndex])) {
96+
$this->currentHitData = $current_page['hits']['hits'][$this->currentHitIndex];
97+
} else {
98+
$this->searchResponses->next();
99+
$this->readPageData();
100+
}
101+
}
102+
103+
/**
104+
* Returns a boolean indicating whether or not the current pointer has valid data
105+
*
106+
* @return bool
107+
* @see Iterator::valid()
108+
*/
109+
public function valid(): bool
110+
{
111+
return is_array($this->currentHitData);
112+
}
113+
114+
/**
115+
* Returns the current hit
116+
*
117+
* @return array
118+
* @see Iterator::current()
119+
*/
120+
public function current(): array
121+
{
122+
return $this->currentHitData;
123+
}
124+
125+
/**
126+
* Returns the current hit index. The hit index spans all pages.
127+
*
128+
* @return int
129+
* @see Iterator::key()
130+
*/
131+
public function key(): int
132+
{
133+
return $this->currentKey;
134+
}
135+
136+
/**
137+
* Advances the internal SearchResponseIterator and resets the currentHitIndex to 0
138+
*
139+
* @internal
140+
*/
141+
private function readPageData(): void
142+
{
143+
if ($this->searchResponses->valid()) {
144+
$current_page = $this->searchResponses->current();
145+
$this->currentHitIndex = 0;
146+
$this->currentHitData = $current_page['hits']['hits'][$this->currentHitIndex];
147+
} else {
148+
$this->currentHitData = null;
149+
}
150+
}
151+
152+
/**
153+
* {@inheritDoc}
154+
*/
155+
public function count(): int
156+
{
157+
return $this->count;
158+
}
159+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Iterators;
16+
17+
use Elastic\Elasticsearch\ClientInterface;
18+
use Elastic\Elasticsearch\Exception\ClientResponseException;
19+
use Elastic\Elasticsearch\Exception\ServerResponseException;
20+
use Iterator;
21+
22+
class SearchResponseIterator implements Iterator
23+
{
24+
25+
/**
26+
* @var ClientInterface
27+
*/
28+
private ClientInterface $client;
29+
30+
/**
31+
* @var array
32+
*/
33+
private array $params;
34+
35+
/**
36+
* @var int
37+
*/
38+
private int $currentKey = 0;
39+
40+
/**
41+
* @var array
42+
*/
43+
private array $currentScrolledResponse;
44+
45+
/**
46+
* @var string|null
47+
*/
48+
private ?string $scrollId;
49+
50+
/**
51+
* @var string duration
52+
*/
53+
private $scroll_ttl;
54+
55+
/**
56+
* Constructor
57+
*
58+
* @param ClientInterface $client
59+
* @param array $search_params Associative array of parameters
60+
* @see ClientInterface::search()
61+
*/
62+
public function __construct(ClientInterface $client, array $search_params)
63+
{
64+
$this->client = $client;
65+
$this->params = $search_params;
66+
67+
if (isset($search_params['scroll'])) {
68+
$this->scroll_ttl = $search_params['scroll'];
69+
}
70+
}
71+
72+
/**
73+
* Destructor
74+
*
75+
* @throws ClientResponseException
76+
* @throws ServerResponseException
77+
*/
78+
public function __destruct()
79+
{
80+
$this->clearScroll();
81+
}
82+
83+
/**
84+
* Sets the time to live duration of a scroll window
85+
*
86+
* @param string $time_to_live
87+
* @return $this
88+
*/
89+
public function setScrollTimeout(string $time_to_live): SearchResponseIterator
90+
{
91+
$this->scroll_ttl = $time_to_live;
92+
return $this;
93+
}
94+
95+
/**
96+
* Clears the current scroll window if there is a scroll_id stored
97+
*
98+
* @return void
99+
* @throws ClientResponseException
100+
* @throws ServerResponseException
101+
*/
102+
private function clearScroll(): void
103+
{
104+
if (!empty($this->scrollId)) {
105+
$this->client->clearScroll(
106+
[
107+
'body' => [
108+
'scroll_id' => $this->scrollId
109+
],
110+
'client' => [
111+
'ignore' => 404
112+
]
113+
]
114+
);
115+
$this->scrollId = null;
116+
}
117+
}
118+
119+
/**
120+
* Rewinds the iterator by performing the initial search.
121+
*
122+
* @return void
123+
* @throws ClientResponseException
124+
* @throws ServerResponseException
125+
* @see Iterator::rewind()
126+
*/
127+
public function rewind(): void
128+
{
129+
$this->clearScroll();
130+
$this->currentKey = 0;
131+
$this->currentScrolledResponse = $this->client->search($this->params);
132+
$this->scrollId = $this->currentScrolledResponse['_scroll_id'];
133+
}
134+
135+
/**
136+
* Fetches every "page" after the first one using the lastest "scroll_id"
137+
*
138+
* @return void
139+
* @see Iterator::next()
140+
*/
141+
public function next(): void
142+
{
143+
$this->currentScrolledResponse = $this->client->scroll(
144+
[
145+
'body' => [
146+
'scroll_id' => $this->scrollId,
147+
'scroll' => $this->scroll_ttl
148+
]
149+
]
150+
);
151+
$this->scrollId = $this->currentScrolledResponse['_scroll_id'];
152+
$this->currentKey++;
153+
}
154+
155+
/**
156+
* Returns a boolean value indicating if the current page is valid or not
157+
*
158+
* @return bool
159+
* @see Iterator::valid()
160+
*/
161+
public function valid(): bool
162+
{
163+
return isset($this->currentScrolledResponse['hits']['hits'][0]);
164+
}
165+
166+
/**
167+
* Returns the current "page"
168+
*
169+
* @return array
170+
* @see Iterator::current()
171+
*/
172+
public function current(): array
173+
{
174+
return $this->currentScrolledResponse;
175+
}
176+
177+
/**
178+
* Returns the current "page number" of the current "page"
179+
*
180+
* @return int
181+
* @see Iterator::key()
182+
*/
183+
public function key(): int
184+
{
185+
return $this->currentKey;
186+
}
187+
}

0 commit comments

Comments
 (0)