1: <?php
2:
3: declare(strict_types=1);
4:
5: /**
6: * Copyright OpenSearch Contributors
7: * SPDX-License-Identifier: Apache-2.0
8: *
9: * OpenSearch PHP client
10: *
11: * @link https://github.com/opensearch-project/opensearch-php/
12: * @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
13: * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
14: * @license https://www.gnu.org/licenses/lgpl-2.1.html GNU Lesser General Public License, Version 2.1
15: *
16: * Licensed to Elasticsearch B.V under one or more agreements.
17: * Elasticsearch B.V licenses this file to you under the Apache 2.0 License or
18: * the GNU Lesser General Public License, Version 2.1, at your option.
19: * See the LICENSE file in the project root for more information.
20: */
21:
22: namespace OpenSearch\Helper\Iterators;
23:
24: use Iterator;
25:
26: class SearchHitIterator implements Iterator, \Countable
27: {
28: /**
29: * @var SearchResponseIterator
30: */
31: private $search_responses;
32:
33: /**
34: * @var int
35: */
36: protected $current_key;
37:
38: /**
39: * @var int
40: */
41: protected $current_hit_index;
42:
43: /**
44: * @var array|null
45: */
46: protected $current_hit_data;
47:
48: /**
49: * @var int
50: */
51: protected $count = 0;
52:
53: /**
54: * Constructor
55: *
56: * @param SearchResponseIterator $search_responses
57: */
58: public function __construct(SearchResponseIterator $search_responses)
59: {
60: $this->search_responses = $search_responses;
61: }
62:
63: /**
64: * Rewinds the internal SearchResponseIterator and itself
65: *
66: * @return void
67: * @see Iterator::rewind()
68: */
69: public function rewind(): void
70: {
71: $this->current_key = 0;
72: $this->search_responses->rewind();
73:
74: // The first page may be empty. In that case, the next page is fetched.
75: $current_page = $this->search_responses->current();
76: if ($this->search_responses->valid() && empty($current_page['hits']['hits'])) {
77: $this->search_responses->next();
78: }
79:
80: $this->count = 0;
81: if (isset($current_page['hits']) && isset($current_page['hits']['total'])) {
82: $this->count = $current_page['hits']['total'];
83: }
84:
85: $this->readPageData();
86: }
87:
88: /**
89: * Advances pointer of the current hit to the next one in the current page. If there
90: * isn't a next hit in the current page, then it advances the current page and moves the
91: * pointer to the first hit in the page.
92: *
93: * @return void
94: * @see Iterator::next()
95: */
96: public function next(): void
97: {
98: $this->current_key++;
99: $this->current_hit_index++;
100: $current_page = $this->search_responses->current();
101: if (isset($current_page['hits']['hits'][$this->current_hit_index])) {
102: $this->current_hit_data = $current_page['hits']['hits'][$this->current_hit_index];
103: } else {
104: $this->search_responses->next();
105: $this->readPageData();
106: }
107: }
108:
109: /**
110: * Returns a boolean indicating whether or not the current pointer has valid data
111: *
112: * @return bool
113: * @see Iterator::valid()
114: */
115: public function valid(): bool
116: {
117: return is_array($this->current_hit_data);
118: }
119:
120: /**
121: * Returns the current hit
122: *
123: * @return array
124: * @see Iterator::current()
125: */
126: public function current(): array
127: {
128: return $this->current_hit_data;
129: }
130:
131: /**
132: * Returns the current hit index. The hit index spans all pages.
133: *
134: * @return int
135: * @see Iterator::key()
136: */
137: public function key(): int
138: {
139: return $this->current_key;
140: }
141:
142: /**
143: * Advances the internal SearchResponseIterator and resets the current_hit_index to 0
144: *
145: * @internal
146: */
147: private function readPageData(): void
148: {
149: if ($this->search_responses->valid()) {
150: $current_page = $this->search_responses->current();
151: $this->current_hit_index = 0;
152: $this->current_hit_data = $current_page['hits']['hits'][$this->current_hit_index];
153: } else {
154: $this->current_hit_data = null;
155: }
156: }
157:
158: /**
159: * {@inheritDoc}
160: */
161: public function count(): int
162: {
163: return $this->count;
164: }
165: }
166: