Files
tm_back/internal/tender/repository.go
T
n.nakhostin 0a23ff985a Enhance Tender Management with Company-Based Matching and API Updates
- Updated the tender service to include company-based matching for tenders, allowing for more relevant results based on company CPV codes.
- Modified the ListTenders endpoint to accept a company ID parameter for calculating match percentages and sorting tenders accordingly.
- Refactored the tender response structure to include match percentage and days until deadline for better client-side handling.
- Updated API documentation to reflect changes in the tender listing functionality and added new endpoints for public tender access.
- Removed deprecated customer-related methods and streamlined customer listing functionality for improved clarity and performance.
2025-08-13 19:47:58 +03:30

836 lines
23 KiB
Go

package tender
import (
"context"
"time"
"tm/pkg/logger"
mongopkg "tm/pkg/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
// TenderRepository interface defines tender data access methods
type TenderRepository interface {
// Tender CRUD operations
Create(ctx context.Context, tender *Tender) error
GetByID(ctx context.Context, id string) (*Tender, error)
GetByContractNoticeID(ctx context.Context, contractNoticeID string) (*Tender, error)
GetByNoticePublicationID(ctx context.Context, noticePublicationID string) (*Tender, error)
Update(ctx context.Context, tender *Tender) error
Delete(ctx context.Context, id string) error
List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int) ([]Tender, int64, error)
// Bulk operations
CreateBatch(ctx context.Context, tenders []Tender) error
UpdateBatch(ctx context.Context, tenders []Tender) error
// Search and filtering
Search(ctx context.Context, criteria TenderSearchCriteria) ([]Tender, error)
GetByCountryCode(ctx context.Context, countryCode string, limit, offset int) ([]Tender, error)
GetByStatus(ctx context.Context, status TenderStatus, limit, offset int) ([]Tender, error)
GetExpiredTenders(ctx context.Context, beforeDate int64) ([]Tender, error)
GetActiveTenders(ctx context.Context, limit, offset int) ([]Tender, error)
// Statistics and aggregations
GetStatistics(ctx context.Context) (*TenderStatistics, error)
GetTenderCountByCountry(ctx context.Context) (map[string]int64, error)
GetTenderCountByType(ctx context.Context) (map[string]int64, error)
GetTenderCountByClassification(ctx context.Context) (map[string]int64, error)
// Scraping state management
GetScrapingState(ctx context.Context) (*ScrapingState, error)
SaveScrapingState(ctx context.Context, state *ScrapingState) error
// Scraping jobs management
CreateScrapingJob(ctx context.Context, job *ScrapingJob) error
GetScrapingJob(ctx context.Context, id string) (*ScrapingJob, error)
UpdateScrapingJob(ctx context.Context, job *ScrapingJob) error
ListScrapingJobs(ctx context.Context, limit, offset int) ([]ScrapingJob, int64, error)
GetActiveScrapingJobs(ctx context.Context) ([]ScrapingJob, error)
}
// tenderRepository implements TenderRepository interface using MongoDB ORM
type tenderRepository struct {
ormRepo mongopkg.Repository[Tender]
stateOrmRepo mongopkg.Repository[ScrapingState]
jobOrmRepo mongopkg.Repository[ScrapingJob]
mongoManager *mongopkg.ConnectionManager
logger logger.Logger
}
// NewTenderRepository creates a new tender repository
func NewTenderRepository(mongoManager *mongopkg.ConnectionManager, logger logger.Logger) TenderRepository {
// Create indexes for tenders collection
tenderIndexes := []mongopkg.Index{
*mongopkg.CreateUniqueIndex("notice_publication_id_idx", bson.D{{Key: "notice_publication_id", Value: 1}}),
*mongopkg.NewIndex("contract_notice_id_idx", bson.D{{Key: "contract_notice_id", Value: 1}}),
*mongopkg.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
*mongopkg.NewIndex("source_idx", bson.D{{Key: "source", Value: 1}}),
*mongopkg.NewIndex("country_code_idx", bson.D{{Key: "country_code", Value: 1}}),
*mongopkg.NewIndex("notice_type_code_idx", bson.D{{Key: "notice_type_code", Value: 1}}),
*mongopkg.NewIndex("procurement_type_code_idx", bson.D{{Key: "procurement_type_code", Value: 1}}),
*mongopkg.NewIndex("main_classification_idx", bson.D{{Key: "main_classification", Value: 1}}),
*mongopkg.NewIndex("publication_date_idx", bson.D{{Key: "publication_date", Value: -1}}),
*mongopkg.NewIndex("tender_deadline_idx", bson.D{{Key: "tender_deadline", Value: 1}}),
*mongopkg.NewIndex("estimated_value_idx", bson.D{{Key: "estimated_value", Value: -1}}),
*mongopkg.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
*mongopkg.NewIndex("updated_at_idx", bson.D{{Key: "updated_at", Value: -1}}),
// Compound indexes for common queries
*mongopkg.NewIndex("status_country_idx", bson.D{
{Key: "status", Value: 1},
{Key: "country_code", Value: 1},
}),
*mongopkg.NewIndex("notice_type_status_idx", bson.D{
{Key: "notice_type_code", Value: 1},
{Key: "status", Value: 1},
}),
// Index for CPV matching queries
*mongopkg.NewIndex("cpv_matching_idx", bson.D{
{Key: "status", Value: 1},
{Key: "main_classification", Value: 1},
{Key: "additional_classifications", Value: 1},
{Key: "publication_date", Value: -1},
}),
// Text index for search
*mongopkg.CreateTextIndex("search_idx", "title", "description"),
}
// Create indexes for scraping jobs collection
jobIndexes := []mongopkg.Index{
*mongopkg.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
*mongopkg.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
*mongopkg.NewIndex("started_at_idx", bson.D{{Key: "started_at", Value: -1}}),
*mongopkg.NewIndex("type_status_idx", bson.D{
{Key: "type", Value: 1},
{Key: "status", Value: 1},
}),
}
// Create indexes
if err := mongoManager.CreateIndexes("tenders", tenderIndexes); err != nil {
logger.Warn("Failed to create tender indexes", map[string]interface{}{
"error": err.Error(),
})
}
if err := mongoManager.CreateIndexes("scraping_jobs", jobIndexes); err != nil {
logger.Warn("Failed to create scraping job indexes", map[string]interface{}{
"error": err.Error(),
})
}
// Create ORM repositories
tenderOrmRepo := mongopkg.NewRepository[Tender](mongoManager.GetCollection("tenders"), logger)
stateOrmRepo := mongopkg.NewRepository[ScrapingState](mongoManager.GetCollection("scraping_state"), logger)
jobOrmRepo := mongopkg.NewRepository[ScrapingJob](mongoManager.GetCollection("scraping_jobs"), logger)
return &tenderRepository{
ormRepo: tenderOrmRepo,
stateOrmRepo: stateOrmRepo,
jobOrmRepo: jobOrmRepo,
mongoManager: mongoManager,
logger: logger,
}
}
// Create creates a new tender
func (r *tenderRepository) Create(ctx context.Context, tender *Tender) error {
err := r.ormRepo.Create(ctx, tender)
if err != nil {
r.logger.Error("Failed to create tender", map[string]interface{}{
"error": err.Error(),
"notice_publication_id": tender.NoticePublicationID,
})
return err
}
r.logger.Info("Tender created successfully", map[string]interface{}{
"tender_id": tender.ID,
"notice_publication_id": tender.NoticePublicationID,
})
return nil
}
// GetByID retrieves a tender by ID
func (r *tenderRepository) GetByID(ctx context.Context, id string) (*Tender, error) {
tender, err := r.ormRepo.FindByID(ctx, id)
if err != nil {
r.logger.Error("Failed to get tender by ID", map[string]interface{}{
"tender_id": id,
"error": err.Error(),
})
return nil, err
}
return tender, nil
}
// GetByContractNoticeID retrieves a tender by contract notice ID
func (r *tenderRepository) GetByContractNoticeID(ctx context.Context, contractNoticeID string) (*Tender, error) {
filter := bson.M{"contract_notice_id": contractNoticeID}
pagination := mongopkg.Pagination{Limit: 1}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get tender by contract notice ID", map[string]interface{}{
"contract_notice_id": contractNoticeID,
"error": err.Error(),
})
return nil, err
}
if len(result.Items) == 0 {
return nil, mongopkg.ErrDocumentNotFound
}
return &result.Items[0], nil
}
// GetByNoticePublicationID retrieves a tender by notice publication ID
func (r *tenderRepository) GetByNoticePublicationID(ctx context.Context, noticePublicationID string) (*Tender, error) {
filter := bson.M{"notice_publication_id": noticePublicationID}
pagination := mongopkg.Pagination{Limit: 1}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get tender by notice publication ID", map[string]interface{}{
"notice_publication_id": noticePublicationID,
"error": err.Error(),
})
return nil, err
}
if len(result.Items) == 0 {
return nil, mongopkg.ErrDocumentNotFound
}
return &result.Items[0], nil
}
// Update updates an existing tender
func (r *tenderRepository) Update(ctx context.Context, tender *Tender) error {
tender.UpdatedAt = time.Now().Unix()
err := r.ormRepo.Update(ctx, tender)
if err != nil {
r.logger.Error("Failed to update tender", map[string]interface{}{
"tender_id": tender.ID,
"error": err.Error(),
})
return err
}
r.logger.Info("Tender updated successfully", map[string]interface{}{
"tender_id": tender.ID,
})
return nil
}
// Delete deletes a tender by ID
func (r *tenderRepository) Delete(ctx context.Context, id string) error {
err := r.ormRepo.Delete(ctx, id)
if err != nil {
r.logger.Error("Failed to delete tender", map[string]interface{}{
"tender_id": id,
"error": err.Error(),
})
return err
}
r.logger.Info("Tender deleted successfully", map[string]interface{}{
"tender_id": id,
})
return nil
}
// List retrieves tenders with pagination and filtering
func (r *tenderRepository) List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int) ([]Tender, int64, error) {
filter := r.buildSearchFilter(criteria)
pagination := mongopkg.Pagination{
Limit: limit,
Skip: offset,
SortField: "created_at",
SortOrder: -1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to list tenders", map[string]interface{}{
"error": err.Error(),
})
return nil, 0, err
}
return result.Items, result.TotalCount, nil
}
// CreateBatch creates multiple tenders in a single operation
func (r *tenderRepository) CreateBatch(ctx context.Context, tenders []Tender) error {
if len(tenders) == 0 {
return nil
}
for i := range tenders {
if err := r.ormRepo.Create(ctx, &tenders[i]); err != nil {
r.logger.Error("Failed to create tender in batch", map[string]interface{}{
"tender_id": tenders[i].ID,
"error": err.Error(),
})
return err
}
}
r.logger.Info("Tender batch created successfully", map[string]interface{}{
"count": len(tenders),
})
return nil
}
// UpdateBatch updates multiple tenders
func (r *tenderRepository) UpdateBatch(ctx context.Context, tenders []Tender) error {
if len(tenders) == 0 {
return nil
}
now := time.Now().Unix()
for i := range tenders {
tenders[i].UpdatedAt = now
if err := r.ormRepo.Update(ctx, &tenders[i]); err != nil {
r.logger.Error("Failed to update tender in batch", map[string]interface{}{
"tender_id": tenders[i].ID,
"error": err.Error(),
})
return err
}
}
r.logger.Info("Tender batch updated successfully", map[string]interface{}{
"count": len(tenders),
})
return nil
}
// Search searches tenders based on criteria
func (r *tenderRepository) Search(ctx context.Context, criteria TenderSearchCriteria) ([]Tender, error) {
filter := r.buildSearchFilter(criteria)
pagination := mongopkg.Pagination{
Limit: 100,
SortField: "created_at",
SortOrder: -1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to search tenders", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// GetByCountryCode retrieves tenders by country code
func (r *tenderRepository) GetByCountryCode(ctx context.Context, countryCode string, limit, offset int) ([]Tender, error) {
filter := bson.M{"country_code": countryCode}
pagination := mongopkg.Pagination{
Limit: limit,
Skip: offset,
SortField: "created_at",
SortOrder: -1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get tenders by country code", map[string]interface{}{
"country_code": countryCode,
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// GetByStatus retrieves tenders by status
func (r *tenderRepository) GetByStatus(ctx context.Context, status TenderStatus, limit, offset int) ([]Tender, error) {
filter := bson.M{"status": status}
pagination := mongopkg.Pagination{
Limit: limit,
Skip: offset,
SortField: "created_at",
SortOrder: -1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get tenders by status", map[string]interface{}{
"status": status,
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// GetExpiredTenders retrieves tenders that are expired
func (r *tenderRepository) GetExpiredTenders(ctx context.Context, beforeDate int64) ([]Tender, error) {
filter := bson.M{
"$or": []bson.M{
{"status": TenderStatusExpired},
{
"tender_deadline": bson.M{
"$lt": beforeDate,
},
"status": bson.M{"$ne": TenderStatusExpired},
},
},
}
pagination := mongopkg.Pagination{
Limit: 1000, // Large limit for maintenance operations
SortField: "tender_deadline",
SortOrder: 1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get expired tenders", map[string]interface{}{
"before_date": beforeDate,
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// GetActiveTenders retrieves active tenders
func (r *tenderRepository) GetActiveTenders(ctx context.Context, limit, offset int) ([]Tender, error) {
filter := bson.M{"status": TenderStatusActive}
pagination := mongopkg.Pagination{
Limit: limit,
Skip: offset,
SortField: "tender_deadline",
SortOrder: 1,
}
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get active tenders", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// GetStatistics calculates tender statistics using aggregation
func (r *tenderRepository) GetStatistics(ctx context.Context) (*TenderStatistics, error) {
stats := &TenderStatistics{
LastUpdated: time.Now().Unix(),
}
// Get total count
total, err := r.ormRepo.Count(ctx, bson.M{})
if err != nil {
return nil, err
}
stats.TotalTenders = total
// Get active count
active, err := r.ormRepo.Count(ctx, bson.M{"status": TenderStatusActive})
if err != nil {
return nil, err
}
stats.ActiveTenders = active
// Get expired count
expired, err := r.ormRepo.Count(ctx, bson.M{"status": TenderStatusExpired})
if err != nil {
return nil, err
}
stats.ExpiredTenders = expired
// Get statistics by country, type, and classification
stats.TendersByCountry, _ = r.GetTenderCountByCountry(ctx)
stats.TendersByType, _ = r.GetTenderCountByType(ctx)
stats.TendersByClassification, _ = r.GetTenderCountByClassification(ctx)
// Calculate value statistics using aggregation
pipeline := mongo.Pipeline{
{
{Key: "$group", Value: bson.M{
"_id": nil,
"total_estimated_value": bson.M{"$sum": "$estimated_value"},
"count": bson.M{"$sum": 1},
}},
},
}
result, err := r.ormRepo.Aggregate(ctx, pipeline)
if err == nil && len(result) > 0 {
if totalValue, ok := result[0]["total_estimated_value"].(float64); ok {
stats.TotalEstimatedValue = totalValue
if count, ok := result[0]["count"].(int32); ok && count > 0 {
stats.AverageEstimatedValue = totalValue / float64(count)
}
}
}
return stats, nil
}
// GetTenderCountByCountry gets tender count grouped by country
func (r *tenderRepository) GetTenderCountByCountry(ctx context.Context) (map[string]int64, error) {
pipeline := mongo.Pipeline{
{
{Key: "$group", Value: bson.M{
"_id": "$country_code",
"count": bson.M{"$sum": 1},
}},
},
}
results, err := r.ormRepo.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
counts := make(map[string]int64)
for _, result := range results {
if country, ok := result["_id"].(string); ok {
if count, ok := result["count"].(int32); ok {
counts[country] = int64(count)
}
}
}
return counts, nil
}
// GetTenderCountByType gets tender count grouped by type
func (r *tenderRepository) GetTenderCountByType(ctx context.Context) (map[string]int64, error) {
pipeline := mongo.Pipeline{
{
{Key: "$group", Value: bson.M{
"_id": "$notice_type_code",
"count": bson.M{"$sum": 1},
}},
},
}
results, err := r.ormRepo.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
counts := make(map[string]int64)
for _, result := range results {
if noticeType, ok := result["_id"].(string); ok {
if count, ok := result["count"].(int32); ok {
counts[noticeType] = int64(count)
}
}
}
return counts, nil
}
// GetTenderCountByClassification gets tender count grouped by classification
func (r *tenderRepository) GetTenderCountByClassification(ctx context.Context) (map[string]int64, error) {
pipeline := mongo.Pipeline{
{
{Key: "$group", Value: bson.M{
"_id": "$main_classification",
"count": bson.M{"$sum": 1},
}},
},
{
{Key: "$limit", Value: 20},
},
}
results, err := r.ormRepo.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
counts := make(map[string]int64)
for _, result := range results {
if classification, ok := result["_id"].(string); ok {
if count, ok := result["count"].(int32); ok {
counts[classification] = int64(count)
}
}
}
return counts, nil
}
// GetScrapingState retrieves the current scraping state
func (r *tenderRepository) GetScrapingState(ctx context.Context) (*ScrapingState, error) {
// Try to find existing state
filter := bson.M{}
pagination := mongopkg.Pagination{Limit: 1}
result, err := r.stateOrmRepo.FindAll(ctx, filter, pagination)
if err != nil {
return nil, err
}
if len(result.Items) == 0 {
// Return default state if none exists
return &ScrapingState{
ID: "default",
LastProcessedYear: time.Now().Year(),
LastProcessedNumber: 0,
LastRunAt: 0,
NextRunAt: time.Now().Add(4 * time.Hour).Unix(),
IsRunning: false,
CreatedAt: time.Now().Unix(),
UpdatedAt: time.Now().Unix(),
}, nil
}
return &result.Items[0], nil
}
// SaveScrapingState saves the scraping state
func (r *tenderRepository) SaveScrapingState(ctx context.Context, state *ScrapingState) error {
if state.ID == "" {
state.ID = "default"
}
now := time.Now().Unix()
if state.CreatedAt == 0 {
state.CreatedAt = now
}
state.UpdatedAt = now
// Try to update existing state first
existing, err := r.GetScrapingState(ctx)
if err == nil && existing.ID != "" {
state.ID = existing.ID
state.CreatedAt = existing.CreatedAt
return r.stateOrmRepo.Update(ctx, state)
}
// Create new state if none exists
return r.stateOrmRepo.Create(ctx, state)
}
// CreateScrapingJob creates a new scraping job
func (r *tenderRepository) CreateScrapingJob(ctx context.Context, job *ScrapingJob) error {
if job.ID == "" {
job.ID = primitive.NewObjectID().Hex()
}
now := time.Now().Unix()
job.CreatedAt = now
job.UpdatedAt = now
err := r.jobOrmRepo.Create(ctx, job)
if err != nil {
r.logger.Error("Failed to create scraping job", map[string]interface{}{
"error": err.Error(),
"type": job.Type,
})
return err
}
r.logger.Info("Scraping job created successfully", map[string]interface{}{
"job_id": job.ID,
"type": job.Type,
})
return nil
}
// GetScrapingJob retrieves a scraping job by ID
func (r *tenderRepository) GetScrapingJob(ctx context.Context, id string) (*ScrapingJob, error) {
job, err := r.jobOrmRepo.FindByID(ctx, id)
if err != nil {
r.logger.Error("Failed to get scraping job", map[string]interface{}{
"job_id": id,
"error": err.Error(),
})
return nil, err
}
return job, nil
}
// UpdateScrapingJob updates a scraping job
func (r *tenderRepository) UpdateScrapingJob(ctx context.Context, job *ScrapingJob) error {
job.UpdatedAt = time.Now().Unix()
err := r.jobOrmRepo.Update(ctx, job)
if err != nil {
r.logger.Error("Failed to update scraping job", map[string]interface{}{
"job_id": job.ID,
"error": err.Error(),
})
return err
}
r.logger.Info("Scraping job updated successfully", map[string]interface{}{
"job_id": job.ID,
})
return nil
}
// ListScrapingJobs retrieves scraping jobs with pagination
func (r *tenderRepository) ListScrapingJobs(ctx context.Context, limit, offset int) ([]ScrapingJob, int64, error) {
filter := bson.M{}
pagination := mongopkg.Pagination{
Limit: limit,
Skip: offset,
SortField: "started_at",
SortOrder: -1,
}
result, err := r.jobOrmRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to list scraping jobs", map[string]interface{}{
"error": err.Error(),
})
return nil, 0, err
}
return result.Items, result.TotalCount, nil
}
// GetActiveScrapingJobs retrieves currently active scraping jobs
func (r *tenderRepository) GetActiveScrapingJobs(ctx context.Context) ([]ScrapingJob, error) {
filter := bson.M{
"status": bson.M{
"$in": []ScrapingJobStatus{
ScrapingJobStatusPending,
ScrapingJobStatusRunning,
},
},
}
pagination := mongopkg.Pagination{
Limit: 100,
SortField: "started_at",
SortOrder: -1,
}
result, err := r.jobOrmRepo.FindAll(ctx, filter, pagination)
if err != nil {
r.logger.Error("Failed to get active scraping jobs", map[string]interface{}{
"error": err.Error(),
})
return nil, err
}
return result.Items, nil
}
// buildSearchFilter builds MongoDB filter from search criteria
func (r *tenderRepository) buildSearchFilter(criteria TenderSearchCriteria) bson.M {
filter := bson.M{}
if criteria.Query != "" {
filter["$text"] = bson.M{"$search": criteria.Query}
}
if criteria.NoticeType != "" {
filter["notice_type_code"] = criteria.NoticeType
}
if criteria.ProcurementType != "" {
filter["procurement_type_code"] = criteria.ProcurementType
}
if len(criteria.CountryCodes) > 0 {
filter["country_code"] = bson.M{"$in": criteria.CountryCodes}
}
if len(criteria.RegionCodes) > 0 {
filter["region_code"] = bson.M{"$in": criteria.RegionCodes}
}
if len(criteria.Classifications) > 0 {
filter["$or"] = []bson.M{
{"main_classification": bson.M{"$in": criteria.Classifications}},
{"additional_classifications": bson.M{"$in": criteria.Classifications}},
}
}
if criteria.MinEstimatedValue != nil || criteria.MaxEstimatedValue != nil {
valueFilter := bson.M{}
if criteria.MinEstimatedValue != nil {
valueFilter["$gte"] = *criteria.MinEstimatedValue
}
if criteria.MaxEstimatedValue != nil {
valueFilter["$lte"] = *criteria.MaxEstimatedValue
}
filter["estimated_value"] = valueFilter
}
if criteria.Currency != "" {
filter["currency"] = criteria.Currency
}
if criteria.DeadlineFrom != nil || criteria.DeadlineTo != nil {
deadlineFilter := bson.M{}
if criteria.DeadlineFrom != nil {
deadlineFilter["$gte"] = *criteria.DeadlineFrom
}
if criteria.DeadlineTo != nil {
deadlineFilter["$lte"] = *criteria.DeadlineTo
}
filter["tender_deadline"] = deadlineFilter
}
if criteria.PublicationDateFrom != nil || criteria.PublicationDateTo != nil {
pubDateFilter := bson.M{}
if criteria.PublicationDateFrom != nil {
pubDateFilter["$gte"] = *criteria.PublicationDateFrom
}
if criteria.PublicationDateTo != nil {
pubDateFilter["$lte"] = *criteria.PublicationDateTo
}
filter["publication_date"] = pubDateFilter
}
if len(criteria.Status) > 0 {
filter["status"] = bson.M{"$in": criteria.Status}
}
if len(criteria.Source) > 0 {
filter["source"] = bson.M{"$in": criteria.Source}
}
if criteria.BuyerOrganizationID != "" {
filter["buyer_organization.id"] = criteria.BuyerOrganizationID
}
if len(criteria.Languages) > 0 {
filter["notice_language_code"] = bson.M{"$in": criteria.Languages}
}
return filter
}