Compare commits
No commits in common. "f5530edcea370ae45f23b0f2a767a6b9b0b9d744" and "c4a78b46326dbb6f6e3fd5dd4b9897aaa01b2cb7" have entirely different histories.
f5530edcea
...
c4a78b4632
|
|
@ -12,7 +12,6 @@
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Laravel\Scout\Attributes\SearchUsingFullText;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
||||
class Person extends Model
|
||||
|
|
@ -65,14 +64,6 @@ protected static function booted()
|
|||
$person->nu = static::generateUniqueNu();
|
||||
}
|
||||
});
|
||||
|
||||
static::saving(function (Person $person) {
|
||||
$person->full_name_search = static::buildFullNameSearchPayload(
|
||||
$person->first_name,
|
||||
$person->last_name,
|
||||
$person->full_name
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
protected function makeAllSearchableUsing(Builder $query): Builder
|
||||
|
|
@ -80,20 +71,16 @@ protected function makeAllSearchableUsing(Builder $query): Builder
|
|||
return $query->with(['addresses', 'phones', 'emails']);
|
||||
}
|
||||
|
||||
#[SearchUsingFullText(['full_name_search'], ['config' => 'simple'])]
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
$columns = [
|
||||
'first_name' => (string) $this->first_name,
|
||||
'last_name' => (string) $this->last_name,
|
||||
'full_name' => (string) $this->full_name,
|
||||
return [
|
||||
'first_name' => '',
|
||||
'last_name' => '',
|
||||
'full_name' => '',
|
||||
'person_addresses.address' => '',
|
||||
'person_phones.nu' => '',
|
||||
'emails.value' => '',
|
||||
'full_name_search' => (string) $this->full_name_search,
|
||||
];
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function phones(): HasMany
|
||||
|
|
@ -157,43 +144,4 @@ protected static function generateUniqueNu(): string
|
|||
|
||||
return $nu;
|
||||
}
|
||||
|
||||
protected static function buildFullNameSearchPayload(?string $firstName, ?string $lastName, ?string $fullName): string
|
||||
{
|
||||
$segments = collect([
|
||||
static::joinNameParts($firstName, $lastName),
|
||||
static::joinNameParts($lastName, $firstName),
|
||||
$fullName,
|
||||
])->filter();
|
||||
|
||||
if ($segments->isEmpty()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $segments
|
||||
->map(fn (string $segment): string => static::normalizeSegment($segment))
|
||||
->filter()
|
||||
->unique()
|
||||
->implode(' ');
|
||||
}
|
||||
|
||||
protected static function joinNameParts(?string $first, ?string $second): ?string
|
||||
{
|
||||
$parts = collect([$first, $second])->filter(fn ($value) => filled($value));
|
||||
|
||||
if ($parts->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $parts->implode(' ');
|
||||
}
|
||||
|
||||
protected static function normalizeSegment(?string $value): ?string
|
||||
{
|
||||
if (blank($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) Str::of($value)->squish()->lower();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('person', function (Blueprint $table) {
|
||||
$table->text('full_name_search')->nullable();
|
||||
});
|
||||
|
||||
$this->backfillSearchColumn();
|
||||
|
||||
if ($this->isPostgres()) {
|
||||
DB::statement(<<<'SQL'
|
||||
ALTER TABLE person
|
||||
ADD COLUMN full_name_search_vector tsvector
|
||||
GENERATED ALWAYS AS (to_tsvector('simple', coalesce(full_name_search, '')))
|
||||
STORED
|
||||
SQL);
|
||||
|
||||
DB::statement('CREATE INDEX person_full_name_search_vector_idx ON person USING GIN (full_name_search_vector)');
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if ($this->isPostgres()) {
|
||||
DB::statement('DROP INDEX IF EXISTS person_full_name_search_vector_idx');
|
||||
DB::statement('ALTER TABLE person DROP COLUMN IF EXISTS full_name_search_vector');
|
||||
}
|
||||
|
||||
Schema::table('person', function (Blueprint $table) {
|
||||
$table->dropColumn('full_name_search');
|
||||
});
|
||||
}
|
||||
|
||||
private function backfillSearchColumn(): void
|
||||
{
|
||||
DB::table('person')
|
||||
->select('id', 'first_name', 'last_name', 'full_name')
|
||||
->lazyById()
|
||||
->each(function ($row): void {
|
||||
DB::table('person')
|
||||
->where('id', $row->id)
|
||||
->update(['full_name_search' => $this->buildSearchValue($row)]);
|
||||
});
|
||||
}
|
||||
|
||||
private function buildSearchValue(object $row): string
|
||||
{
|
||||
$segments = array_filter([
|
||||
$this->joinParts($row->first_name ?? null, $row->last_name ?? null),
|
||||
$this->joinParts($row->last_name ?? null, $row->first_name ?? null),
|
||||
$row->full_name ?? null,
|
||||
]);
|
||||
|
||||
if (empty($segments)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$normalized = array_unique(array_map(function (string $value): string {
|
||||
$collapsed = preg_replace('/\s+/u', ' ', trim($value)) ?: '';
|
||||
|
||||
return mb_strtolower($collapsed);
|
||||
}, $segments));
|
||||
|
||||
return trim(implode(' ', array_filter($normalized)));
|
||||
}
|
||||
|
||||
private function joinParts(?string $first, ?string $second): ?string
|
||||
{
|
||||
$parts = array_filter([$first, $second], fn ($part) => filled($part));
|
||||
|
||||
if (empty($parts)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trim(implode(' ', $parts));
|
||||
}
|
||||
|
||||
private function isPostgres(): bool
|
||||
{
|
||||
return DB::connection()->getDriverName() === 'pgsql';
|
||||
}
|
||||
};
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Person\Person;
|
||||
|
||||
it('finds a person when the query uses the inverted name order', function (): void {
|
||||
config()->set('scout.driver', 'collection');
|
||||
|
||||
$person = Person::factory()->create([
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Dou',
|
||||
'full_name' => 'Dou John',
|
||||
]);
|
||||
|
||||
$results = Person::search('John Dou')->get();
|
||||
|
||||
expect($results)->toHaveCount(1)
|
||||
->and($results->first()->is($person))->toBeTrue();
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user