siModel = $siModel; $this->misModel = $misModel; } /** * ПОИСК ТОЛЬКО В POSTGRESQL (основной) */ public function searchInPostgres( ?string $searchText, ?string $dateFrom, ?string $dateTo, ?int $status, int $pageSize = 15, string $sortBy = 'dateextract', string $sortDir = 'desc', ): LengthAwarePaginator { $query = $this->siModel->newQuery(); $this->applyStatusFilter($query, $status); $this->applyDateFilter($query, 'dateextract', $dateFrom, $dateTo); $this->applySearchConditions($query, $searchText); return $query->select($this->defaultFieldsSI) ->orderBy($sortBy, $sortDir) ->with(['archiveInfo']) ->paginate($pageSize) ->through(function ($item) { $item->database_source = 'postgresql'; return $item; }); } /** * ПОИСК ТОЛЬКО В MSSQL (исторический) */ public function searchInMssql( ?string $searchText, ?string $dateFrom, ?string $dateTo, ?int $status, int $pageSize = 15, string $sortBy = 'DateExtract', string $sortDir = 'desc' ): LengthAwarePaginator { $query = $this->misModel->newQuery(); $this->applyStatusFilter($query, $status); $this->applyDateFilter($query, 'DateExtract', $dateFrom, $dateTo); $this->applySearchConditions($query, $searchText, 'mssql'); return $query->select($this->defaultFieldsMis) ->orderBy($sortBy, $sortDir) ->with(['archiveInfo']) ->paginate($pageSize) ->through(function ($item) { $item->database_source = 'mssql'; return $item; }); } /** * УМНЫЙ ПОИСК (сначала PostgreSQL, потом MSSQL если мало) */ public function smartSearch( ?string $searchText, ?string $dateFrom, ?string $dateTo, int $pageSize = 15 ): LengthAwarePaginator { // 1. Ищем в PostgreSQL $pgPaginator = $this->searchInPostgres($searchText, $dateFrom, $dateTo, $pageSize); // 2. Если мало результатов, добавляем из MSSQL if ($pgPaginator->total() < $pageSize && $pgPaginator->total() < 10) { $needed = $pageSize - $pgPaginator->count(); $mssqlResults = $this->searchInMssql($searchText, $dateFrom, $dateTo, $needed) ->getCollection(); $allResults = $pgPaginator->getCollection() ->merge($mssqlResults) ->sortByDesc('dateextract') ->values(); return new LengthAwarePaginator( $allResults, $pgPaginator->total() + $mssqlResults->count(), $pageSize, request()->get('page', 1) ); } return $pgPaginator; } /** * РАЗДЕЛЬНЫЙ ПОИСК (отдельные результаты по БД) */ public function separateSearch( ?string $searchText, ?string $dateFrom, ?string $dateTo, ?int $status, int $perPage = 15, ): array { $pgPaginator = $this->searchInPostgres($searchText, $dateFrom, $dateTo, $status, $perPage); $mssqlPaginator = $this->searchInMssql($searchText, $dateFrom, $dateTo, $status, $perPage); return [ 'si' => $pgPaginator, 'mis' => $mssqlPaginator, 'stats' => [ 'si_total' => $pgPaginator->total(), 'mis_total' => $mssqlPaginator->total(), 'combined_total' => $pgPaginator->total() + $mssqlPaginator->total(), ] ]; } /** * ПРИМЕНЕНИЕ УСЛОВИЙ ПОИСКА */ private function applySearchConditions($query, ?string $searchText, string $dbType = 'postgresql'): void { // Разбиваем поисковую строку на слова $words = preg_split('/\s+/', trim($searchText)); $words = array_filter($words); if (empty($words)) { return; } $query->where(function($q) use ($words, $dbType) { // Если одно слово - ищем в любом поле if (count($words) === 1) { $word = Str::ucfirst($words[0]); $pattern = $word . '%'; // Префиксный поиск для использования индекса if ($dbType === 'postgresql') { $q->where('family', 'LIKE', $pattern) ->orWhere('name', 'LIKE', $pattern) ->orWhere('ot', 'LIKE', $pattern); } else { $q->where('FAMILY', 'LIKE', $pattern) ->orWhere('Name', 'LIKE', $pattern) ->orWhere('OT', 'LIKE', $pattern); } } // Если несколько слов - предполагаем Ф+И+О else { // Берем первые 3 слова $family = !empty($words[0]) ? Str::ucfirst($words[0]) : null; $name = !empty($words[1]) ? Str::ucfirst($words[1]) : null; $ot = !empty($words[2]) ? Str::ucfirst($words[2]) : null; if ($dbType === 'postgresql') { $q->where('family', 'LIKE', $family . '%'); } else { $q->where('FAMILY', 'LIKE', $family . '%'); } if ($name) { if ($dbType === 'postgresql') { $q->where('name', 'LIKE', $name . '%'); } else { $q->where('Name', 'LIKE', $name . '%'); } } if ($ot) { if ($dbType === 'postgresql') { $q->where('ot', 'LIKE', $ot . '%'); } else { $q->where('OT', 'LIKE', $ot . '%'); } } } }); } private function applyStatusFilter(Builder $query, ?int $value) { if ($value === 0) { $query->doesntHave('archiveInfo'); } else { if ($query->withExists('archiveInfo') && !empty($value)) { $query->withWhereHas('archiveInfo', function ($q) use ($value) { $q->where('status_id', '=', $value); }); } } } /** * ФИЛЬТР ПО ДАТЕ */ private function applyDateFilter($query, string $dateField, ?string $dateFrom, ?string $dateTo): void { if (!empty($dateFrom)) { $query->whereDate($dateField, '>=', $dateFrom); } if (!empty($dateTo)) { $query->whereDate($dateField, '<=', $dateTo); } } /** * ПОЛУЧИТЬ СТАТИСТИКУ ПО БАЗАМ */ public function getDatabaseStats(): array { return [ 'postgresql' => [ 'total' => $this->siModel->count(), 'connection' => config('database.connections.pgsql.database'), 'status' => 'primary', ], 'mssql' => [ 'total' => $this->misModel->count(), 'connection' => config('database.connections.sqlsrv.database'), 'status' => 'secondary', ] ]; } /** * БЫСТРЫЙ ПОИСК ПО ТИПУ */ public function quickSearch(string $type, string $value): Collection { return match($type) { 'medcard' => $this->searchByMedCard($value), 'enp' => $this->searchByEnp($value), 'fullname' => $this->searchByFullName($value), default => collect(), }; } private function searchByMedCard(string $medCard): Collection { $pgResults = $this->siModel->where('medcardnum', 'LIKE', "%{$medCard}%") ->limit(10) ->get() ->each(fn($item) => $item->database_source = 'postgresql'); $mssqlResults = $this->misModel->where('medcardnum', 'LIKE', "%{$medCard}%") ->limit(10) ->get() ->each(fn($item) => $item->database_source = 'mssql'); return $pgResults->concat($mssqlResults) ->sortBy('medcardnum') ->values(); } private function searchByFullName(string $name): Collection { $pattern = "%{$name}%"; $pgResults = $this->siModel->where('family', 'ILIKE', $pattern) ->orWhere('name', 'ILIKE', $pattern) ->orWhere('ot', 'ILIKE', $pattern) ->limit(10) ->get() ->each(fn($item) => $item->database_source = 'postgresql'); $mssqlResults = $this->misModel->where('family', 'LIKE', $pattern) ->orWhere('name', 'LIKE', $pattern) ->orWhere('ot', 'LIKE', $pattern) ->limit(10) ->get() ->each(fn($item) => $item->database_source = 'mssql'); return $pgResults->concat($mssqlResults) ->sortBy('family') ->values(); } }