Add documents scraped filter functionality and related tests

- Introduced `documents_scraped_filter.go` to handle filtering tenders based on the presence of documents in MinIO, utilizing a caching mechanism to optimize performance.
- Implemented `enrichDocumentsScrapedFilter` and `cachedContractFolderIDsWithDocuments` methods in the `tenderService` for efficient retrieval of contract folder IDs with documents.
- Added unit tests in `documents_scraped_filter_test.go` to validate the new filtering logic and caching behavior, ensuring accurate results and error handling.
- Updated `SearchForm` documentation to clarify the caching aspect of `ContractFolderIDsWithDocuments`.
- Enhanced comments in the handler and repository to reflect the new filtering logic and its implications.

This update improves the tender search functionality by efficiently managing document presence checks, leading to better performance and user experience in the tender management system.
This commit is contained in:
Mazyar
2026-07-12 00:20:39 +03:30
parent c8616940ff
commit 0fde5772e2
7 changed files with 354 additions and 34 deletions
+15 -27
View File
@@ -100,11 +100,15 @@ type tenderService struct {
searchListCacheMu sync.Mutex
searchListCache map[string]searchListCacheEntry
searchListCacheGroup singleflight.Group
contractFoldersCacheMu sync.Mutex
contractFoldersCache contractFoldersWithDocsCacheEntry
contractFoldersCacheGroup singleflight.Group
}
const (
aiSummaryEnrichmentTimeout = 2 * time.Second
aiTranslationEnrichmentTimeout = 2 * time.Second
aiSummaryEnrichmentTimeout = 2 * time.Second
aiTranslationEnrichmentTimeout = 2 * time.Second
recommendTranslationEnrichmentTimeout = 5 * time.Second
)
@@ -935,6 +939,15 @@ func (s *tenderService) Search(ctx context.Context, form *SearchForm, pagination
}
func (s *tenderService) searchUncached(ctx context.Context, form *SearchForm, pagination *response.Pagination, language string) (*SearchResponse, error) {
if form.DocumentsScraped {
if err := s.enrichDocumentsScrapedFilter(ctx, form); err != nil {
return nil, err
}
if len(form.ContractFolderIDsWithDocuments) == 0 {
return s.emptySearchResponse(pagination), nil
}
}
page, err := s.repository.Search(ctx, form, pagination)
if err != nil {
s.logger.Error("Failed to list tenders", map[string]interface{}{
@@ -1759,31 +1772,6 @@ func (s *tenderService) listProceduresWithDocuments(ctx context.Context) ([]ai_s
return procedures, nil
}
func (s *tenderService) enrichDocumentsScrapedFilter(ctx context.Context, form *SearchForm) error {
procedures, err := s.listProceduresWithDocuments(ctx)
if err != nil {
s.logger.Error("Failed to resolve tenders with scraped documents from MinIO", map[string]interface{}{
"error": err.Error(),
})
if errors.Is(err, ai_summarizer.ErrMinIOUnavailable) {
return fmt.Errorf("failed to resolve tenders with scraped documents: MinIO connection unavailable: %w", err)
}
return fmt.Errorf("failed to resolve tenders with scraped documents: %w", err)
}
folderIDs := make([]string, 0, len(procedures))
for _, proc := range procedures {
if proc.DocumentCount <= 0 {
continue
}
if id := strings.TrimSpace(proc.ContractFolderID); id != "" {
folderIDs = append(folderIDs, id)
}
}
form.ContractFolderIDsWithDocuments = folderIDs
return nil
}
func (s *tenderService) resolveRecommendedTenders(ctx context.Context, recommendations []company.RecommendedTenderResponse) (map[string]Tender, error) {
out := make(map[string]Tender)
procRefs := make([]ProcedureReference, 0, len(recommendations))