From 9153e319306220b066007a3fbd68a6639a4e5fa5 Mon Sep 17 00:00:00 2001 From: ykan821 Date: Fri, 10 Jul 2026 23:43:35 +0800 Subject: [PATCH 1/3] docs(readme): drop AI-assisted development notice Co-Authored-By: Claude --- README.md | 4 ---- README.zh.md | 4 ---- 2 files changed, 8 deletions(-) diff --git a/README.md b/README.md index 1b8ddef..46afdaa 100644 --- a/README.md +++ b/README.md @@ -313,10 +313,6 @@ EventDispatcher::listen('search.*', function (Event $e) { - [Changelog](CHANGELOG.md) - [Elasticsearch official docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html) — query types and parameter reference -## AI-assisted development - -This project is developed with AI assistance; core paths and tests are human-reviewed. - ## License MIT diff --git a/README.zh.md b/README.zh.md index d8f367f..c3420f3 100644 --- a/README.zh.md +++ b/README.zh.md @@ -312,10 +312,6 @@ EventDispatcher::listen('search.*', function (Event $e) { - [更新日志](CHANGELOG.md) - [Elasticsearch 官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html)——查询类型和参数参考 -## AI 辅助开发 - -本项目使用 AI 辅助开发,核心路径和测试经人工审查。 - ## License MIT From c3f9479366d67d55d3ab547e5b5f25aa0edbe884 Mon Sep 17 00:00:00 2001 From: ykan821 Date: Sat, 11 Jul 2026 00:51:30 +0800 Subject: [PATCH 2/3] fix(index): make bulk options instance-level state, reach auto-flush flush() took options as a parameter, so the batchSize auto-flush path silently dropped refresh/timeout/etc. options() now holds them as instance state applied to every flush; flush($options) overrides per-call. The onError retry Bulk inherits target/options/retry_on_conflict (not batchSize/errorHandler, to avoid auto-flush disrupting re-enqueue and handler recursion). Adds two contract tests and docs. Co-Authored-By: Claude --- docs/index.md | 6 ++++ docs/index.zh.md | 6 ++++ src/Index/Bulk.php | 34 ++++++++++++++++++-- tests/Integration/Index/BulkContractTest.php | 27 ++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index c67185c..6e7b7d0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -198,6 +198,12 @@ foreach ($docs as $id => $doc) { $bulk->flush(); // the tail (< 500) ``` +`options()` sets top-level bulk API params (`refresh`, `timeout`, `pipeline`, ...) applied to **every** flush — including the `batchSize()` auto-flush. It persists across flushes; arguments passed to `flush($options)` override them for a single call. + +```php +$bulk->options(['refresh' => true, 'timeout' => '30s']); +``` + ### Error handling `flush()` throws a `RuntimeException` by default when the response contains errors. Use `onError()` to customize — the callback receives three raw materials and decides what to do: diff --git a/docs/index.zh.md b/docs/index.zh.md index 6d4f64e..3fa39b6 100644 --- a/docs/index.zh.md +++ b/docs/index.zh.md @@ -198,6 +198,12 @@ foreach ($docs as $id => $doc) { $bulk->flush(); // 尾部(< 500 那批) ``` +`options()` 设置顶层 bulk API 参数(`refresh`、`timeout`、`pipeline` 等),对**每次** flush 生效——包括 `batchSize()` 的自动 flush。跨 flush 持久;传给 `flush($options)` 的参数仅在单次调用时覆盖。 + +```php +$bulk->options(['refresh' => true, 'timeout' => '30s']); +``` + ### 错误处理 `flush()` 默认在响应包含错误时抛出 `RuntimeException`。用 `onError()` 自定义处理——回调收到三样原材料,自行决定: diff --git a/src/Index/Bulk.php b/src/Index/Bulk.php index b373647..bfa9919 100644 --- a/src/Index/Bulk.php +++ b/src/Index/Bulk.php @@ -24,6 +24,13 @@ class Bulk */ private int $retryOnConflict = 0; + /** + * Top-level bulk API params (refresh, timeout, ...) applied to every flush. + * + * @var array + */ + private array $defaultOptions = []; + /** * @var string|null */ @@ -94,7 +101,7 @@ public function batchSize(int $size): static * On error the callback receives three tools and decides what to do: * - $response: the raw ES response (items[] carry per-item status/error); * - $body: the full original batch in native ES format (successes included); - * - $newbulk: a fresh Bulk bound to the same index and target, for re-send. + * - $newbulk: a fresh Bulk for re-send, inheriting target, options, and retry_on_conflict. * * Extract the failures from $body using $response (items[k] matches the k-th * action), re-enqueue them on $newbulk, and call $newbulk->flush() to retry. @@ -123,6 +130,21 @@ public function retryOnConflict(int $count): static return $this; } + /** + * Top-level bulk API params applied to every flush, including the auto-flush + * triggered by batchSize(). Persists across flush() calls; flush($options) + * overrides these per-call. + * + * @param array $options + * @return $this + */ + public function options(array $options): static + { + $this->defaultOptions = $options; + + return $this; + } + /** * Queue an index (create/overwrite) action. * @@ -223,7 +245,8 @@ public function delete(string|int $id): static * preserved for the caller to retry. Call this at the end of a batch to flush * the remainder — batchSize() auto-flushes full batches during enqueue. * - * @param array $options top-level bulk API params (refresh, timeout, etc) + * @param array $options top-level bulk API params (refresh, timeout, etc), + * overriding the instance-level options() for this flush only * @return array * @throws \RuntimeException when the response has errors and no handler swallowed them */ @@ -242,7 +265,7 @@ public function flush(array $options = []): array $start = microtime(true); $response = $this->index->getClient()->bulk( - array_merge(['body' => $actions], $options) + array_merge(['body' => $actions], $this->defaultOptions, $options) )->asArray(); $duration = microtime(true) - $start; @@ -257,8 +280,13 @@ public function flush(array $options = []): array // Hand the caller the raw materials: the response, the full body, // and a fresh Bulk on the same index/target. The caller extracts // the failures and re-sends them however it likes. + // Inherits passive request settings (target, options, retry_on_conflict) + // so retries reproduce the original request. batchSize/errorHandler stay + // off: auto-flush would disrupt re-enqueueing, a handler could recurse. $newbulk = new Bulk($this->index); $newbulk->targetIndex = $this->targetIndex; + $newbulk->defaultOptions = $this->defaultOptions; + $newbulk->retryOnConflict = $this->retryOnConflict; ($this->errorHandler)($response, $actions, $newbulk); } else { $json = json_encode($response, JSON_UNESCAPED_UNICODE); diff --git a/tests/Integration/Index/BulkContractTest.php b/tests/Integration/Index/BulkContractTest.php index b913b0b..5ead20b 100644 --- a/tests/Integration/Index/BulkContractTest.php +++ b/tests/Integration/Index/BulkContractTest.php @@ -55,6 +55,33 @@ public function testBatchSizeAutoFlush(): void $this->assertTrue($index->newDoc('22')->exists()); } + public function testOptionsApplyToAutoFlush(): void + { + // batchSize triggers auto-flush mid-chain; options() must ride along so the + // bulk request carries refresh=true — without it the doc is not yet searchable. + $index = $this->makeIndex(); + (new Bulk($index)) + ->batchSize(1) + ->options(['refresh' => true]) + ->index('40', ['title' => 'refreshed via options']); + + // No manual refreshIndex() — proves refresh reached the auto-flushed bulk. + $docs = $index->newQuery()->match('title', 'refreshed')->size(10)->get()->docs(); + $this->assertCount(1, $docs); + } + + public function testFlushArgumentOptionsApply(): void + { + // Per-call flush($options) must reach the bulk request too. + $index = $this->makeIndex(); + (new Bulk($index)) + ->index('41', ['title' => 'flusharg refreshed']) + ->flush(['refresh' => true]); + + $docs = $index->newQuery()->match('title', 'flusharg')->size(10)->get()->docs(); + $this->assertCount(1, $docs); + } + public function testOnErrorReceivesFailures(): void { // create on the already-seeded id '1' triggers a bulk error -> onError From 8441db9ece8505cbb7c822e5266e80fe72ad2231 Mon Sep 17 00:00:00 2001 From: ykan821 Date: Sat, 11 Jul 2026 00:51:30 +0800 Subject: [PATCH 3/3] docs(readme): drop Laravel section and packagist/downloads badges Co-Authored-By: Claude --- README.md | 7 ------- README.zh.md | 6 ------ 2 files changed, 13 deletions(-) diff --git a/README.md b/README.md index 46afdaa..64cc9da 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,12 @@ > [中文](README.zh.md) | English -[![Latest Version](https://img.shields.io/packagist/v/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) -[![Total Downloads](https://img.shields.io/packagist/dt/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) [![Tests](https://github.com/ykan821/ElasticKit/actions/workflows/ci.yml/badge.svg)](https://github.com/ykan821/ElasticKit/actions/workflows/ci.yml) [![PHP](https://img.shields.io/packagist/php-v/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) [![License](https://img.shields.io/packagist/l/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) A PHP Elasticsearch DSL query builder covering queries, aggregations, CRUD, bulk writes, and zero-downtime rebuilds. -## Integrations - -- **[ElasticKit Laravel](https://github.com/ykan821/ElasticKitLaravel)** — Laravel integration - (native pagination, artisan rebuild). `composer require ykan/elastickit-laravel`. - ## Installation ``` diff --git a/README.zh.md b/README.zh.md index c3420f3..4a10fd9 100644 --- a/README.zh.md +++ b/README.zh.md @@ -2,18 +2,12 @@ > 中文 | [English](README.md) -[![Latest Version](https://img.shields.io/packagist/v/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) -[![Total Downloads](https://img.shields.io/packagist/dt/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) [![Tests](https://github.com/ykan821/ElasticKit/actions/workflows/ci.yml/badge.svg)](https://github.com/ykan821/ElasticKit/actions/workflows/ci.yml) [![PHP](https://img.shields.io/packagist/php-v/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) [![License](https://img.shields.io/packagist/l/ykan/elastickit)](https://packagist.org/packages/ykan/elastickit) PHP Elasticsearch DSL 查询构建库,覆盖查询、聚合、CRUD、批量写入、零停机重建。 -## 集成 - -- **[ElasticKit Laravel](https://github.com/ykan821/ElasticKitLaravel)** — Laravel 集成(原生分页、artisan 重建)。`composer require ykan/elastickit-laravel`。 - ## 安装 ```