Files
Investidor-V2/app/Services/Ai/EvidenceRepository.php
T
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

61 lines
3.2 KiB
PHP

<?php
namespace App\Services\Ai;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
final class EvidenceRepository
{
public function eligible(): Builder
{
return DB::table('ai_sources as s')->join('documents as d', 'd.id', '=', 's.document_id')
->where('s.status', 'approved')->where('s.purpose', 'investment_information')
->where('s.valid_until', '>=', now()->toDateString())->where('s.source_date', '<=', now()->toDateString())
->where('d.status', 'active')->whereNull('d.deleted_at')->whereIn('d.data_classification', ['public', 'internal'])
->where('d.malware_scan_status', 'clean')->whereColumn('d.malware_scanned_sha256', 'd.sha256')
->whereColumn('s.document_version', 'd.version_no')->whereColumn('s.document_checksum', 'd.sha256');
}
/** @param array<string, mixed> $filters
* @return list<array<string, mixed>>
*/
public function search(string $query, string $language, array $filters = []): array
{
$configuration = $language === 'pt' ? 'portuguese' : 'english';
$builder = $this->eligible()->join('ai_chunks as c', 'c.source_id', '=', 's.id')
->where('s.language', $language)->whereRaw('c.lexical @@ websearch_to_tsquery(?::regconfig, ?)', [$configuration, $query])
->select(['c.id as chunk_id', 's.id as source_id', 's.document_id', 's.document_version', 's.title', 's.language',
's.nature', 's.source_url', 's.source_date', 's.valid_until', 'c.content', 'c.checksum'])
->selectRaw('ts_rank_cd(c.lexical, websearch_to_tsquery(?::regconfig, ?)) as rank', [$configuration, $query]);
if (! empty($filters['source_id'])) {
$builder->where('s.id', $filters['source_id']);
}
if (! empty($filters['nature'])) {
$builder->where('s.nature', $filters['nature']);
}
if ($filters['public_only'] ?? false) {
$builder->where('d.data_classification', 'public')->where('d.rights_status', '!=', 'restricted')
->whereExists(fn ($q) => $q->selectRaw('1')->from('portal_resources as r')->whereColumn('r.document_id', 'd.id')->where('r.published', true)->where('r.rights_status', '!=', 'restricted'));
}
return array_values($builder->orderByDesc('rank')->orderBy('c.id')->limit((int) config('investmoz_ai.retrieval_limit', 5))
->get()->map(fn ($row) => (array) $row)->all());
}
/** @return list<array<string, mixed>> */
public function excerpts(string $id): array
{
return array_values($this->eligible()->join('ai_chunks as c', 'c.source_id', '=', 's.id')->where('s.id', $id)
->select(['c.id as chunk_id', 's.id as source_id', 's.document_id', 's.document_version', 's.title', 's.language',
's.nature', 's.source_url', 's.source_date', 's.valid_until', 'c.content', 'c.checksum'])
->orderBy('c.position')->limit(3)->get()->map(fn ($row) => (array) $row)->all());
}
/** @param array<array-key, string> $ids */
public function allStillEligible(array $ids): bool
{
return $ids === [] || $this->eligible()->whereIn('s.id', $ids)->count() === count(array_unique($ids));
}
}