mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-11-29 18:27:44 +08:00
Added methods lazyById
and lazyByIdDesc
for lazy queries (#6822)
This commit is contained in:
parent
ee547bee24
commit
5d724aa501
@ -9,6 +9,7 @@
|
||||
- [#6816](https://github.com/hyperf/hyperf/pull/6816) Added methods `Hyperf\Database\Model\Builder::load*`.
|
||||
- [#6820](https://github.com/hyperf/hyperf/pull/6820) Added method `Hyperf\Database\Model\Builder::valueOrFail()`.
|
||||
- [#6821](https://github.com/hyperf/hyperf/pull/6821) Added method `Hyperf\Database\Concerns\BuildsQueries::chunkMap()`.
|
||||
- [#6822](https://github.com/hyperf/hyperf/pull/6822) Added methods `lazyById` and `lazyByIdDesc` for lazy queries.
|
||||
|
||||
## Fixed
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace Hyperf\Database\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Hyperf\Collection\Collection as BaseCollection;
|
||||
use Hyperf\Collection\LazyCollection;
|
||||
use Hyperf\Context\ApplicationContext;
|
||||
use Hyperf\Contract\LengthAwarePaginatorInterface;
|
||||
use Hyperf\Contract\PaginatorInterface;
|
||||
@ -26,6 +27,7 @@ use Hyperf\Paginator\Cursor;
|
||||
use Hyperf\Paginator\CursorPaginator;
|
||||
use Hyperf\Paginator\Paginator;
|
||||
use Hyperf\Stringable\Str;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
trait BuildsQueries
|
||||
@ -102,6 +104,22 @@ trait BuildsQueries
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query lazily, by chunking the results of a query by comparing IDs.
|
||||
*/
|
||||
public function lazyById(int $chunkSize = 1000, ?string $column = null, ?string $alias = null): LazyCollection
|
||||
{
|
||||
return $this->orderedLazyById($chunkSize, $column, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query lazily, by chunking the results of a query by comparing IDs in descending order.
|
||||
*/
|
||||
public function lazyByIdDesc(int $chunkSize = 1000, ?string $column = null, ?string $alias = null): LazyCollection
|
||||
{
|
||||
return $this->orderedLazyById($chunkSize, $column, $alias, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query and get the first result.
|
||||
*
|
||||
@ -165,6 +183,48 @@ trait BuildsQueries
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query lazily, by chunking the results of a query by comparing IDs in a given order.
|
||||
*/
|
||||
protected function orderedLazyById(int $chunkSize = 1000, ?string $column = null, ?string $alias = null, bool $descending = false): LazyCollection
|
||||
{
|
||||
if ($chunkSize < 1) {
|
||||
throw new InvalidArgumentException('The chunk size should be at least 1');
|
||||
}
|
||||
|
||||
$column ??= $this->defaultKeyName();
|
||||
|
||||
$alias ??= $column;
|
||||
|
||||
return LazyCollection::make(function () use ($chunkSize, $column, $alias, $descending) {
|
||||
$lastId = null;
|
||||
|
||||
while (true) {
|
||||
$clone = clone $this;
|
||||
|
||||
if ($descending) {
|
||||
$results = $clone->forPageBeforeId($chunkSize, $lastId, $column)->get();
|
||||
} else {
|
||||
$results = $clone->forPageAfterId($chunkSize, $lastId, $column)->get();
|
||||
}
|
||||
|
||||
foreach ($results as $result) {
|
||||
yield $result;
|
||||
}
|
||||
|
||||
if ($results->count() < $chunkSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lastId = $results->last()->{$alias};
|
||||
|
||||
if ($lastId === null) {
|
||||
throw new RuntimeException("The lazyById operation was aborted because the [{$alias}] column is not present in the query result.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new length-aware paginator instance.
|
||||
*/
|
||||
|
@ -1206,6 +1206,14 @@ class Builder
|
||||
return isset(static::$macros[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default key name of the table.
|
||||
*/
|
||||
protected function defaultKeyName(): string
|
||||
{
|
||||
return $this->getModel()->getKeyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the proper order by required for cursor pagination.
|
||||
*/
|
||||
|
@ -2919,6 +2919,14 @@ class Builder
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default key name of the table.
|
||||
*/
|
||||
protected function defaultKeyName(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the proper order by required for cursor pagination.
|
||||
*/
|
||||
|
@ -26,6 +26,7 @@ use Hyperf\Database\Connectors\MySqlConnector;
|
||||
use Hyperf\Database\Events\QueryExecuted;
|
||||
use Hyperf\Database\Model\EnumCollector;
|
||||
use Hyperf\Database\Model\Events\Saved;
|
||||
use Hyperf\Database\Model\Model;
|
||||
use Hyperf\Database\MySqlBitConnection;
|
||||
use Hyperf\Database\Query\Builder as QueryBuilder;
|
||||
use Hyperf\Database\Query\Expression;
|
||||
@ -1184,6 +1185,91 @@ class ModelRealBuilderTest extends TestCase
|
||||
Schema::drop('accounting_test');
|
||||
}
|
||||
|
||||
public function testOrderedLazyById(): void
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$container->shouldReceive('get')->with(Db::class)->andReturn(new Db($container));
|
||||
Schema::create('lazy_users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
$now = Carbon::now();
|
||||
Db::table('lazy_users')->insert([
|
||||
['name' => 'Hyperf1', 'created_at' => $now, 'updated_at' => $now],
|
||||
['name' => 'Hyperf2', 'created_at' => $now->addMinutes(), 'updated_at' => $now->addMinutes()],
|
||||
['name' => 'Hyperf3', 'created_at' => $now->addMinutes(2), 'updated_at' => $now->addMinutes(2)],
|
||||
['name' => 'Hyperf4', 'created_at' => $now->addMinutes(3), 'updated_at' => $now->addMinutes(3)],
|
||||
['name' => 'Hyperf5', 'created_at' => $now->addMinutes(4), 'updated_at' => $now->addMinutes(4)],
|
||||
['name' => 'Hyperf6', 'created_at' => $now->addMinutes(5), 'updated_at' => $now->addMinutes(5)],
|
||||
['name' => 'Hyperf7', 'created_at' => $now->addMinutes(6), 'updated_at' => $now->addMinutes(6)],
|
||||
['name' => 'Hyperf8', 'created_at' => $now->addMinutes(7), 'updated_at' => $now->addMinutes(7)],
|
||||
['name' => 'Hyperf9', 'created_at' => $now->addMinutes(8), 'updated_at' => $now->addMinutes(8)],
|
||||
['name' => 'Hyperf10', 'created_at' => $now->addMinutes(9), 'updated_at' => $now->addMinutes(9)],
|
||||
]);
|
||||
$results = LazyUserModel::query()->lazyById(10);
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$dbResults = Db::table('lazy_users')->lazyById(10);
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$results = LazyUserModel::query()->lazyById(5);
|
||||
$dbResults = Db::table('lazy_users')->lazyById(5);
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$results = LazyUserModel::query()->lazyByIdDesc(10);
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . (10 - $index), $value->name);
|
||||
}
|
||||
$dbResults = Db::table('lazy_users')->lazyByIdDesc(10);
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . (10 - $index), $value->name);
|
||||
}
|
||||
$results = LazyUserModel::query()->lazyByIdDesc(5);
|
||||
$dbResults = Db::table('lazy_users')->lazyByIdDesc(5);
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . (10 - $index), $value->name);
|
||||
}
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . (10 - $index), $value->name);
|
||||
}
|
||||
$results = LazyUserModel::query()->select(['id', 'name', 'created_at as create_date', 'updated_at'])->lazyByIdDesc(10, 'created_at', 'create_date');
|
||||
$dbResults = Db::table('lazy_users')->select(['id', 'name', 'created_at as create_date', 'updated_at'])->lazyByIdDesc(10, 'created_at', 'create_date');
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$results = LazyUserModel::query()->select(['id', 'name', 'created_at as create_date', 'updated_at'])->lazyById(10, 'created_at', 'create_date');
|
||||
$dbResults = Db::table('lazy_users')->select(['id', 'name', 'created_at as create_date', 'updated_at'])->lazyById(10, 'created_at', 'create_date');
|
||||
$this->assertCount(10, $results);
|
||||
foreach ($results as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
$this->assertCount(10, $dbResults);
|
||||
foreach ($dbResults as $index => $value) {
|
||||
$this->assertSame('Hyperf' . ($index + 1), $value->name);
|
||||
}
|
||||
Schema::dropIfExists('lazy_users');
|
||||
}
|
||||
|
||||
protected function getContainer()
|
||||
{
|
||||
$dispatcher = Mockery::mock(EventDispatcherInterface::class);
|
||||
@ -1198,3 +1284,8 @@ class ModelRealBuilderTest extends TestCase
|
||||
return $container;
|
||||
}
|
||||
}
|
||||
|
||||
class LazyUserModel extends Model
|
||||
{
|
||||
protected ?string $table = 'lazy_users';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user