-
Notifications
You must be signed in to change notification settings - Fork 730
#1645: add support for "search after" pagination #2298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9eee4e5
c079d8d
703733c
369d823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -539,6 +539,82 @@ public function count($query = '', string $method = Request::POST): int | |
| return $search->count('', false, $method); | ||
| } | ||
|
|
||
| /** | ||
| * Iterates over all documents, matching given query, using "search after" based pagination. | ||
| * | ||
| * @see \Elastica\Query::setSearchAfter() | ||
| * | ||
| * @param mixed $query search query with sort by unique document field. | ||
| * @param int $batchSize the number of rows to be returned in each batch (e.g. each query size). | ||
| * @param array|null $options | ||
| * @param string $method request method. | ||
| * @return \Generator|\Elastica\Document[] list of all documents matched the given query as iterator. | ||
| */ | ||
| public function each($query = '', int $batchSize = 100, ?array $options = null, string $method = Request::POST): \Generator | ||
| { | ||
| $query = Query::create($query); | ||
|
|
||
| if (!$query->hasParam('sort')) { | ||
| throw new \LogicException('Query must have "sort" parameter in order to use "search after" based iteration.'); | ||
| } | ||
|
|
||
| $query->setSize($batchSize); | ||
|
|
||
| while (true) { | ||
| $resultSet = $this->search($query, $options, $method); | ||
| foreach ($resultSet->getDocuments() as $document) { | ||
| yield $document; | ||
| } | ||
|
|
||
| if (count($resultSet->getDocuments()) < $batchSize) { | ||
| break; | ||
| } | ||
|
|
||
| $query->setSearchAfter($document->getSort()); | ||
| } | ||
| } | ||
|
Comment on lines
+553
to
+575
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid mutating the caller's
♻️ Proposed fix- $query = Query::create($query);
+ $query = clone Query::create($query);
@@
- $query = Query::create($query);
+ $query = clone Query::create($query);Also applies to: 588-615 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * Iterates over all documents in batches, matching given query, using "search after" based pagination. | ||
| * | ||
| * @see \Elastica\Query::setSearchAfter() | ||
| * | ||
| * @param mixed $query search query with sort by unique document field. | ||
| * @param int $batchSize the number of rows to be returned in each batch (e.g. each query size). | ||
| * @param array|null $options | ||
| * @param string $method request method. | ||
| * @return \Generator|\Elastica\Document[][] list of document batches matched the given query as iterator. | ||
| */ | ||
| public function batch($query = '', int $batchSize = 100, ?array $options = null, string $method = Request::POST): \Generator | ||
| { | ||
| $query = Query::create($query); | ||
|
|
||
| if (!$query->hasParam('sort')) { | ||
| throw new \LogicException('Query must have "sort" parameter in order to use "search after" based iteration.'); | ||
| } | ||
|
|
||
| $query->setSize($batchSize); | ||
|
|
||
| while (true) { | ||
| $resultSet = $this->search($query, $options, $method); | ||
|
|
||
| $documents = $resultSet->getDocuments(); | ||
| if (empty($documents)) { | ||
| break; | ||
| } | ||
|
|
||
| yield $documents; | ||
|
|
||
| if (count($documents) < $batchSize) { | ||
| break; | ||
| } | ||
|
|
||
| $lastDocument = array_pop($documents); | ||
|
|
||
| $query->setSearchAfter($lastDocument->getSort()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Opens an index. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -476,4 +476,22 @@ public function setTrackTotalHits($trackTotalHits = true): self | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return $this->setParam('track_total_hits', $trackTotalHits); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after | ||||||||||||||||||||||||
| * | ||||||||||||||||||||||||
| * Allows retrieval of the next page of hits using sort values from previous page. | ||||||||||||||||||||||||
| * The value of {@see \Elastica\Document::getSort()} should be passed as argument here. | ||||||||||||||||||||||||
| * | ||||||||||||||||||||||||
| * @param array $searchAfter the sort of the last document from previous search result set. | ||||||||||||||||||||||||
| * @return static self reference. | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
| public function setSearchAfter(array $searchAfter): self | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| foreach ($searchAfter as $value) { | ||||||||||||||||||||||||
| $this->addParam('search_after', $value); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return $this; | ||||||||||||||||||||||||
|
Comment on lines
+489
to
+495
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace Line 492 uses 🐛 Proposed fix public function setSearchAfter(array $searchAfter): self
{
- foreach ($searchAfter as $value) {
- $this->addParam('search_after', $value);
- }
-
- return $this;
+ return $this->setParam('search_after', \array_values($searchAfter));
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate
$batchSizebefore entering the loop.With
0,each()reaches Line 573 without ever binding$document; with negative values, both methods forward an invalidsizedownstream. This should fail fast.🛡️ Proposed fix
public function each($query = '', int $batchSize = 100, ?array $options = null, string $method = Request::POST): \Generator { + if ($batchSize < 1) { + throw new InvalidException('Batch size must be greater than 0.'); + } + $query = Query::create($query); @@ public function batch($query = '', int $batchSize = 100, ?array $options = null, string $method = Request::POST): \Generator { + if ($batchSize < 1) { + throw new InvalidException('Batch size must be greater than 0.'); + } + $query = Query::create($query);Also applies to: 588-589
🤖 Prompt for AI Agents