pesquisa avancada no portal
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
/** Combines independent, already-authorised sources without loading complete catalogues. */
|
||||
final class FederatedSearch
|
||||
{
|
||||
/** @param array<string, callable(): array{count:int, fetch:callable(int,int): iterable}> $factories */
|
||||
public function page(array $factories, int $requestedPage, int $size = 5, ?callable $score = null): array
|
||||
{
|
||||
$sources = [];
|
||||
$unavailable = [];
|
||||
foreach ($factories as $label => $factory) {
|
||||
try {
|
||||
$sources[$label] = $factory();
|
||||
} catch (QueryException $exception) {
|
||||
report($exception);
|
||||
$unavailable[] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
// If a source fails after its count, remove it and recompute the page offsets.
|
||||
do {
|
||||
$failed = false;
|
||||
$total = array_sum(array_column($sources, 'count'));
|
||||
$last = max(1, (int) ceil($total / $size));
|
||||
$page = min(max(1, $requestedPage), $last);
|
||||
$first = ($page - 1) * $size;
|
||||
$items = [];
|
||||
$positions = array_fill_keys(array_keys($sources), 0);
|
||||
$buffers = array_fill_keys(array_keys($sources), []);
|
||||
for ($position = 0; $position < min($total, $first + $size); $position++) {
|
||||
$best = null;
|
||||
$bestScore = PHP_INT_MIN;
|
||||
foreach ($sources as $label => $source) {
|
||||
if ($buffers[$label] === [] && $positions[$label] < $source['count']) {
|
||||
try {
|
||||
$batch = iterator_to_array((function () use ($source, $positions, $label, $size) {
|
||||
yield from ($source['fetch'])($positions[$label], $size);
|
||||
})(), false);
|
||||
$positions[$label] += count($batch);
|
||||
$buffers[$label] = $batch;
|
||||
if ($batch === []) {
|
||||
$positions[$label] = $source['count'];
|
||||
}
|
||||
} catch (QueryException $exception) {
|
||||
report($exception);
|
||||
$unavailable[] = $label;
|
||||
unset($sources[$label]);
|
||||
$failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($buffers[$label] === []) {
|
||||
continue;
|
||||
}
|
||||
$candidateScore = $score ? $score($buffers[$label][0]) : 0;
|
||||
if ($best === null || $candidateScore > $bestScore) {
|
||||
$best = $label;
|
||||
$bestScore = $candidateScore;
|
||||
}
|
||||
}
|
||||
if ($failed || $best === null) {
|
||||
break;
|
||||
}
|
||||
$item = array_shift($buffers[$best]);
|
||||
if ($position >= $first) {
|
||||
$items[] = [...$item, 'category' => $best];
|
||||
}
|
||||
}
|
||||
} while ($failed);
|
||||
|
||||
return ['data' => $items, 'total' => $total, 'current_page' => $page, 'last_page' => $last,
|
||||
'per_page' => $size, 'unavailable' => $unavailable];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user