1<?php 2 3namespace Elastica; 4 5use Elastica\Exception\InvalidException; 6 7/** 8 * Elastica result set. 9 * 10 * List of all hits that are returned for a search on elasticsearch 11 * Result set implements iterator 12 * 13 * @author Nicolas Ruflin <spam@ruflin.com> 14 */ 15class ResultSet implements \Iterator, \Countable, \ArrayAccess 16{ 17 /** 18 * Current position. 19 * 20 * @var int Current position 21 */ 22 private $_position = 0; 23 24 /** 25 * Query. 26 * 27 * @var Query Query object 28 */ 29 private $_query; 30 31 /** 32 * Response. 33 * 34 * @var Response Response object 35 */ 36 private $_response; 37 38 /** 39 * Results. 40 * 41 * @var Result[] Results 42 */ 43 private $_results; 44 45 /** 46 * @param Result[] $results 47 */ 48 public function __construct(Response $response, Query $query, $results) 49 { 50 $this->_query = $query; 51 $this->_response = $response; 52 $this->_results = $results; 53 } 54 55 /** 56 * Returns all results. 57 * 58 * @return Result[] 59 */ 60 public function getResults() 61 { 62 return $this->_results; 63 } 64 65 /** 66 * Returns all Documents. 67 * 68 * @return Document[] 69 */ 70 public function getDocuments() 71 { 72 $documents = []; 73 foreach ($this->_results as $doc) { 74 $documents[] = $doc->getDocument(); 75 } 76 77 return $documents; 78 } 79 80 /** 81 * Returns true if the response contains suggestion results; false otherwise. 82 */ 83 public function hasSuggests(): bool 84 { 85 $data = $this->_response->getData(); 86 87 return isset($data['suggest']); 88 } 89 90 /** 91 * Return all suggests. 92 * 93 * @return array suggest results 94 */ 95 public function getSuggests(): array 96 { 97 $data = $this->_response->getData(); 98 99 return $data['suggest'] ?? []; 100 } 101 102 /** 103 * Returns whether aggregations exist. 104 */ 105 public function hasAggregations(): bool 106 { 107 $data = $this->_response->getData(); 108 109 return isset($data['aggregations']); 110 } 111 112 /** 113 * Returns all aggregation results. 114 */ 115 public function getAggregations(): array 116 { 117 $data = $this->_response->getData(); 118 119 return $data['aggregations'] ?? []; 120 } 121 122 /** 123 * Retrieve a specific aggregation from this result set. 124 * 125 * @param string $name the name of the desired aggregation 126 * 127 * @throws Exception\InvalidException if an aggregation by the given name cannot be found 128 */ 129 public function getAggregation(string $name): array 130 { 131 $data = $this->_response->getData(); 132 133 if (isset($data['aggregations'][$name])) { 134 return $data['aggregations'][$name]; 135 } 136 137 throw new InvalidException("This result set does not contain an aggregation named {$name}."); 138 } 139 140 /** 141 * Returns the total number of found hits. 142 */ 143 public function getTotalHits(): int 144 { 145 $data = $this->_response->getData(); 146 147 return (int) ($data['hits']['total']['value'] ?? 0); 148 } 149 150 /** 151 * Returns the total number relation of found hits. 152 */ 153 public function getTotalHitsRelation(): string 154 { 155 $data = $this->_response->getData(); 156 157 return $data['hits']['total']['relation'] ?? 'eq'; 158 } 159 160 /** 161 * Returns the max score of the results found. 162 */ 163 public function getMaxScore(): float 164 { 165 $data = $this->_response->getData(); 166 167 return (float) ($data['hits']['max_score'] ?? 0); 168 } 169 170 /** 171 * Returns the total number of ms for this search to complete. 172 */ 173 public function getTotalTime(): int 174 { 175 $data = $this->_response->getData(); 176 177 return $data['took'] ?? 0; 178 } 179 180 /** 181 * Returns the Point-In-Time ID, if available. 182 * 183 * @See: https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after 184 */ 185 public function getPointInTimeId(): ?string 186 { 187 $data = $this->_response->getData(); 188 189 return $data['pit_id'] ?? null; 190 } 191 192 /** 193 * Returns true if the query has timed out. 194 */ 195 public function hasTimedOut(): bool 196 { 197 $data = $this->_response->getData(); 198 199 return !empty($data['timed_out']); 200 } 201 202 /** 203 * Returns response object. 204 */ 205 public function getResponse(): Response 206 { 207 return $this->_response; 208 } 209 210 public function getQuery(): Query 211 { 212 return $this->_query; 213 } 214 215 /** 216 * Returns size of current set. 217 */ 218 public function count(): int 219 { 220 return \count($this->_results); 221 } 222 223 /** 224 * Returns size of current suggests. 225 */ 226 public function countSuggests(): int 227 { 228 return \count($this->getSuggests()); 229 } 230 231 /** 232 * Returns the current object of the set. 233 * 234 * @return Result Set object 235 */ 236 public function current(): Result 237 { 238 return $this->_results[$this->key()]; 239 } 240 241 /** 242 * Sets pointer (current) to the next item of the set. 243 */ 244 public function next(): void 245 { 246 ++$this->_position; 247 } 248 249 /** 250 * Returns the position of the current entry. 251 * 252 * @return int Current position 253 */ 254 public function key(): int 255 { 256 return $this->_position; 257 } 258 259 /** 260 * Check if an object exists at the current position. 261 * 262 * @return bool True if object exists 263 */ 264 public function valid(): bool 265 { 266 return isset($this->_results[$this->key()]); 267 } 268 269 /** 270 * Resets position to 0, restarts iterator. 271 */ 272 public function rewind(): void 273 { 274 $this->_position = 0; 275 } 276 277 /** 278 * Whether a offset exists. 279 * 280 * @see http://php.net/manual/en/arrayaccess.offsetexists.php 281 * 282 * @param int $offset 283 */ 284 public function offsetExists($offset): bool 285 { 286 return isset($this->_results[$offset]); 287 } 288 289 /** 290 * Offset to retrieve. 291 * 292 * @see http://php.net/manual/en/arrayaccess.offsetget.php 293 * 294 * @param int $offset 295 * 296 * @throws Exception\InvalidException If offset doesn't exist 297 */ 298 public function offsetGet($offset): Result 299 { 300 if ($this->offsetExists($offset)) { 301 return $this->_results[$offset]; 302 } 303 304 throw new InvalidException('Offset does not exist.'); 305 } 306 307 /** 308 * Offset to set. 309 * 310 * @see http://php.net/manual/en/arrayaccess.offsetset.php 311 * 312 * @param int $offset 313 * @param Result $value 314 * 315 * @throws Exception\InvalidException 316 */ 317 public function offsetSet($offset, $value): void 318 { 319 if (!($value instanceof Result)) { 320 throw new InvalidException('ResultSet is a collection of Result only.'); 321 } 322 323 if (!isset($this->_results[$offset])) { 324 throw new InvalidException('Offset does not exist.'); 325 } 326 327 $this->_results[$offset] = $value; 328 } 329 330 /** 331 * Offset to unset. 332 * 333 * @see http://php.net/manual/en/arrayaccess.offsetunset.php 334 * 335 * @param int $offset 336 */ 337 public function offsetUnset($offset): void 338 { 339 unset($this->_results[$offset]); 340 } 341} 342