Implement Company Search and Update API Documentation
- Introduced a new search functionality for companies, allowing advanced filtering capabilities including tags, business criteria, and location. - Updated the API documentation to reflect changes in the search endpoint, enhancing clarity for API consumers. - Refactored existing company-related API endpoints for consistency, including renaming and restructuring routes. - Enhanced response structures to return company entities directly, simplifying the response handling in API endpoints. - Removed unused query parameters and handlers, streamlining the company management functionality.
This commit is contained in:
+89
-538
@@ -5,70 +5,38 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
"tm/pkg/logger"
|
||||
"tm/pkg/mongo"
|
||||
orm "tm/pkg/mongo"
|
||||
"tm/pkg/response"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// Repository defines the interface for company data operations
|
||||
type Repository interface {
|
||||
Create(ctx context.Context, company *Company) error
|
||||
Update(ctx context.Context, company *Company) error
|
||||
GetByID(ctx context.Context, id string) (*Company, error)
|
||||
GetByIDs(ctx context.Context, ids []string) ([]Company, error)
|
||||
GetByName(ctx context.Context, name string) (*Company, error)
|
||||
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error)
|
||||
GetByTaxID(ctx context.Context, taxID string) (*Company, error)
|
||||
GetByIDs(ctx context.Context, ids []string) ([]*Company, error)
|
||||
Update(ctx context.Context, company *Company) error
|
||||
Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) ([]Company, int64, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
List(ctx context.Context, limit, offset int) ([]*Company, error)
|
||||
Search(ctx context.Context, search string, companyType *string, status *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, cpvCodes []string, categories []string, keywords []string, specializations []string, employeeCountMin *int, employeeCountMax *int, annualRevenueMin *float64, annualRevenueMax *float64, foundedYearMin *int, foundedYearMax *int, limit, offset int, sortBy, sortOrder string) ([]*Company, error)
|
||||
Count(ctx context.Context) (int64, error)
|
||||
CountByType(ctx context.Context, companyType CompanyType) (int64, error)
|
||||
CountByStatus(ctx context.Context, status CompanyStatus) (int64, error)
|
||||
CountByIndustry(ctx context.Context, industry string) (int64, error)
|
||||
CountVerified(ctx context.Context) (int64, error)
|
||||
UpdateStatus(ctx context.Context, id string, status CompanyStatus) error
|
||||
UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error
|
||||
UpdateTags(ctx context.Context, id string, tags *CompanyTags) error
|
||||
AddTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error
|
||||
RemoveTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error
|
||||
GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error)
|
||||
SearchByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error)
|
||||
}
|
||||
|
||||
// companyRepository implements the Repository interface using the MongoDB ORM
|
||||
type companyRepository struct {
|
||||
ormRepo mongo.Repository[Company]
|
||||
ormRepo orm.Repository[Company]
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewCompanyRepository creates a new company repository
|
||||
func NewCompanyRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository {
|
||||
func NewCompanyRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
||||
// Create indexes using the ORM's index management
|
||||
indexes := []mongo.Index{
|
||||
*mongo.CreateUniqueIndex("name_idx", bson.D{{Key: "name", Value: 1}}),
|
||||
*mongo.CreateUniqueIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
|
||||
*mongo.CreateUniqueIndex("tax_id_idx", bson.D{{Key: "tax_id", Value: 1}}),
|
||||
*mongo.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
|
||||
*mongo.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
|
||||
*mongo.NewIndex("industry_idx", bson.D{{Key: "industry", Value: 1}}),
|
||||
*mongo.NewIndex("is_verified_idx", bson.D{{Key: "is_verified", Value: 1}}),
|
||||
*mongo.NewIndex("is_compliant_idx", bson.D{{Key: "is_compliant", Value: 1}}),
|
||||
*mongo.NewIndex("language_idx", bson.D{{Key: "language", Value: 1}}),
|
||||
*mongo.NewIndex("currency_idx", bson.D{{Key: "currency", Value: 1}}),
|
||||
*mongo.NewIndex("employee_count_idx", bson.D{{Key: "employee_count", Value: 1}}),
|
||||
*mongo.NewIndex("annual_revenue_idx", bson.D{{Key: "annual_revenue", Value: 1}}),
|
||||
*mongo.NewIndex("founded_year_idx", bson.D{{Key: "founded_year", Value: 1}}),
|
||||
*mongo.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
|
||||
// Tag indexes for efficient search
|
||||
*mongo.NewIndex("cpv_codes_idx", bson.D{{Key: "tags.cpv_codes", Value: 1}}),
|
||||
*mongo.NewIndex("categories_idx", bson.D{{Key: "tags.categories", Value: 1}}),
|
||||
*mongo.NewIndex("keywords_idx", bson.D{{Key: "tags.keywords", Value: 1}}),
|
||||
*mongo.NewIndex("specializations_idx", bson.D{{Key: "tags.specializations", Value: 1}}),
|
||||
*mongo.NewIndex("certifications_idx", bson.D{{Key: "tags.certifications", Value: 1}}),
|
||||
// Text index for search
|
||||
*mongo.CreateTextIndex("search_idx", "name", "description", "industry"),
|
||||
indexes := []orm.Index{
|
||||
*orm.NewIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
|
||||
}
|
||||
|
||||
// Create indexes
|
||||
@@ -80,7 +48,7 @@ func NewCompanyRepository(mongoManager *mongo.ConnectionManager, logger logger.L
|
||||
}
|
||||
|
||||
// Create ORM repository
|
||||
ormRepo := mongo.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
|
||||
ormRepo := orm.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
|
||||
|
||||
return &companyRepository{
|
||||
ormRepo: ormRepo,
|
||||
@@ -93,7 +61,6 @@ func (r *companyRepository) Create(ctx context.Context, company *Company) error
|
||||
// Set created/updated timestamps using Unix timestamps
|
||||
now := time.Now().Unix()
|
||||
company.SetCreatedAt(now)
|
||||
company.SetUpdatedAt(now)
|
||||
|
||||
// Use ORM to create company
|
||||
err := r.ormRepo.Create(ctx, company)
|
||||
@@ -119,7 +86,7 @@ func (r *companyRepository) Create(ctx context.Context, company *Company) error
|
||||
func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, error) {
|
||||
company, err := r.ormRepo.FindByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||
return nil, errors.New("company not found")
|
||||
}
|
||||
r.logger.Error("Failed to get company by ID", map[string]interface{}{
|
||||
@@ -132,108 +99,43 @@ func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, e
|
||||
return company, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a company by name
|
||||
func (r *companyRepository) GetByName(ctx context.Context, name string) (*Company, error) {
|
||||
filter := bson.M{"name": name}
|
||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
||||
// GetByIDs retrieves companies by IDs
|
||||
func (r *companyRepository) GetByIDs(ctx context.Context, ids []string) ([]Company, error) {
|
||||
companies, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": ids}}, orm.NewPaginationBuilder().Build())
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
return nil, errors.New("company not found")
|
||||
}
|
||||
r.logger.Error("Failed to get company by name", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"name": name,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return companies.Items, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a company by name
|
||||
func (r *companyRepository) GetByName(ctx context.Context, name string) (*Company, error) {
|
||||
company, err := r.ormRepo.FindOne(ctx, bson.M{"name": name})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return company, nil
|
||||
}
|
||||
|
||||
// GetByRegistrationNumber retrieves a company by registration number
|
||||
func (r *companyRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error) {
|
||||
filter := bson.M{"registration_number": registrationNumber}
|
||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
||||
company, err := r.ormRepo.FindOne(ctx, bson.M{"registration_number": registrationNumber})
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
return nil, errors.New("company not found")
|
||||
}
|
||||
r.logger.Error("Failed to get company by registration number", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"registration_number": registrationNumber,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return company, nil
|
||||
}
|
||||
|
||||
// GetByTaxID retrieves a company by tax ID
|
||||
func (r *companyRepository) GetByTaxID(ctx context.Context, taxID string) (*Company, error) {
|
||||
filter := bson.M{"tax_id": taxID}
|
||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
||||
company, err := r.ormRepo.FindOne(ctx, bson.M{"tax_id": taxID})
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
||||
return nil, errors.New("company not found")
|
||||
}
|
||||
r.logger.Error("Failed to get company by tax ID", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"tax_id": taxID,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return company, nil
|
||||
}
|
||||
|
||||
// GetByIDs retrieves multiple companies by their IDs
|
||||
func (r *companyRepository) GetByIDs(ctx context.Context, ids []string) ([]*Company, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*Company{}, nil
|
||||
}
|
||||
|
||||
// Convert string IDs to ObjectIDs for MongoDB query
|
||||
objectIDs := make([]interface{}, len(ids))
|
||||
for i, id := range ids {
|
||||
objectID, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
r.logger.Warn("Invalid company ID format", map[string]interface{}{
|
||||
"id": id,
|
||||
"error": err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
objectIDs[i] = objectID
|
||||
}
|
||||
|
||||
if len(objectIDs) == 0 {
|
||||
return []*Company{}, nil
|
||||
}
|
||||
|
||||
filter := bson.M{"_id": bson.M{"$in": objectIDs}}
|
||||
companies, err := r.ormRepo.FindMany(ctx, filter, len(ids))
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get companies by IDs", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"ids": ids,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert to pointer slice
|
||||
result := make([]*Company, len(companies))
|
||||
for i := range companies {
|
||||
result[i] = &companies[i]
|
||||
}
|
||||
|
||||
r.logger.Info("Retrieved companies by IDs", map[string]interface{}{
|
||||
"requested_count": len(ids),
|
||||
"found_count": len(result),
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Update updates a company
|
||||
func (r *companyRepository) Update(ctx context.Context, company *Company) error {
|
||||
// Set updated timestamp using Unix timestamp
|
||||
@@ -257,20 +159,16 @@ func (r *companyRepository) Update(ctx context.Context, company *Company) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a company (soft delete by setting status to inactive)
|
||||
// Delete deletes a company
|
||||
func (r *companyRepository) Delete(ctx context.Context, id string) error {
|
||||
// Get company first
|
||||
company, err := r.GetByID(ctx, id)
|
||||
_, err := r.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update status to inactive
|
||||
company.Status = CompanyStatusInactive
|
||||
company.SetUpdatedAt(time.Now().Unix())
|
||||
|
||||
// Update in database
|
||||
err = r.ormRepo.Update(ctx, company)
|
||||
err = r.ormRepo.Delete(ctx, id)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to delete company", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
@@ -286,246 +184,122 @@ func (r *companyRepository) Delete(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List retrieves companies with pagination
|
||||
func (r *companyRepository) List(ctx context.Context, limit, offset int) ([]*Company, error) {
|
||||
// Build pagination
|
||||
pagination := mongo.NewPaginationBuilder().
|
||||
Limit(limit).
|
||||
Skip(offset).
|
||||
SortDesc("created_at").
|
||||
Build()
|
||||
|
||||
// Only active companies by default
|
||||
filter := bson.M{"status": bson.M{"$ne": CompanyStatusInactive}}
|
||||
|
||||
// Use ORM to find companies
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to list companies", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert []Company to []*Company
|
||||
companies := make([]*Company, len(result.Items))
|
||||
for i := range result.Items {
|
||||
companies[i] = &result.Items[i]
|
||||
}
|
||||
|
||||
return companies, nil
|
||||
}
|
||||
|
||||
// Search retrieves companies with search and filters
|
||||
func (r *companyRepository) Search(ctx context.Context, search string, companyType *string, status *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, cpvCodes []string, categories []string, keywords []string, specializations []string, employeeCountMin *int, employeeCountMax *int, annualRevenueMin *float64, annualRevenueMax *float64, foundedYearMin *int, foundedYearMax *int, limit, offset int, sortBy, sortOrder string) ([]*Company, error) {
|
||||
func (r *companyRepository) Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) ([]Company, int64, error) {
|
||||
// Build filter
|
||||
filter := bson.M{}
|
||||
|
||||
if search != "" {
|
||||
filter["$text"] = bson.M{"$search": search}
|
||||
if form.Search != nil {
|
||||
filter["$text"] = bson.M{"$search": *form.Search}
|
||||
}
|
||||
|
||||
if companyType != nil {
|
||||
filter["type"] = *companyType
|
||||
if form.Type != nil {
|
||||
filter["type"] = *form.Type
|
||||
}
|
||||
|
||||
if status != nil {
|
||||
filter["status"] = *status
|
||||
if form.Status != nil {
|
||||
filter["status"] = *form.Status
|
||||
}
|
||||
|
||||
if industry != nil {
|
||||
filter["industry"] = *industry
|
||||
if form.Industry != nil {
|
||||
filter["industry"] = *form.Industry
|
||||
}
|
||||
|
||||
if isVerified != nil {
|
||||
filter["is_verified"] = *isVerified
|
||||
if form.IsVerified != nil {
|
||||
filter["is_verified"] = *form.IsVerified
|
||||
}
|
||||
|
||||
if isCompliant != nil {
|
||||
filter["is_compliant"] = *isCompliant
|
||||
if form.IsCompliant != nil {
|
||||
filter["is_compliant"] = *form.IsCompliant
|
||||
}
|
||||
|
||||
if language != nil {
|
||||
filter["language"] = *language
|
||||
if form.Language != nil {
|
||||
filter["language"] = *form.Language
|
||||
}
|
||||
|
||||
if currency != nil {
|
||||
filter["currency"] = *currency
|
||||
if form.Currency != nil {
|
||||
filter["currency"] = *form.Currency
|
||||
}
|
||||
|
||||
// Tag filters
|
||||
if len(cpvCodes) > 0 {
|
||||
filter["tags.cpv_codes"] = bson.M{"$in": cpvCodes}
|
||||
if len(form.CPVCodes) > 0 {
|
||||
filter["tags.cpv_codes"] = bson.M{"$in": form.CPVCodes}
|
||||
}
|
||||
|
||||
if len(categories) > 0 {
|
||||
filter["tags.categories"] = bson.M{"$in": categories}
|
||||
if len(form.Categories) > 0 {
|
||||
filter["tags.categories"] = bson.M{"$in": form.Categories}
|
||||
}
|
||||
|
||||
if len(keywords) > 0 {
|
||||
filter["tags.keywords"] = bson.M{"$in": keywords}
|
||||
if len(form.Keywords) > 0 {
|
||||
filter["tags.keywords"] = bson.M{"$in": form.Keywords}
|
||||
}
|
||||
|
||||
if len(specializations) > 0 {
|
||||
filter["tags.specializations"] = bson.M{"$in": specializations}
|
||||
if len(form.Specializations) > 0 {
|
||||
filter["tags.specializations"] = bson.M{"$in": form.Specializations}
|
||||
}
|
||||
|
||||
// Range filters
|
||||
if employeeCountMin != nil || employeeCountMax != nil {
|
||||
if form.EmployeeCountMin != nil || form.EmployeeCountMax != nil {
|
||||
rangeFilter := bson.M{}
|
||||
if employeeCountMin != nil {
|
||||
rangeFilter["$gte"] = *employeeCountMin
|
||||
if form.EmployeeCountMin != nil {
|
||||
rangeFilter["$gte"] = *form.EmployeeCountMin
|
||||
}
|
||||
if employeeCountMax != nil {
|
||||
rangeFilter["$lte"] = *employeeCountMax
|
||||
if form.EmployeeCountMax != nil {
|
||||
rangeFilter["$lte"] = *form.EmployeeCountMax
|
||||
}
|
||||
filter["employee_count"] = rangeFilter
|
||||
}
|
||||
|
||||
if annualRevenueMin != nil || annualRevenueMax != nil {
|
||||
if form.AnnualRevenueMin != nil || form.AnnualRevenueMax != nil {
|
||||
rangeFilter := bson.M{}
|
||||
if annualRevenueMin != nil {
|
||||
rangeFilter["$gte"] = *annualRevenueMin
|
||||
if form.AnnualRevenueMin != nil {
|
||||
rangeFilter["$gte"] = *form.AnnualRevenueMin
|
||||
}
|
||||
if annualRevenueMax != nil {
|
||||
rangeFilter["$lte"] = *annualRevenueMax
|
||||
if form.AnnualRevenueMax != nil {
|
||||
rangeFilter["$lte"] = *form.AnnualRevenueMax
|
||||
}
|
||||
filter["annual_revenue"] = rangeFilter
|
||||
}
|
||||
|
||||
if foundedYearMin != nil || foundedYearMax != nil {
|
||||
if form.FoundedYearMin != nil || form.FoundedYearMax != nil {
|
||||
rangeFilter := bson.M{}
|
||||
if foundedYearMin != nil {
|
||||
rangeFilter["$gte"] = *foundedYearMin
|
||||
if form.FoundedYearMin != nil {
|
||||
rangeFilter["$gte"] = *form.FoundedYearMin
|
||||
}
|
||||
if foundedYearMax != nil {
|
||||
rangeFilter["$lte"] = *foundedYearMax
|
||||
if form.FoundedYearMax != nil {
|
||||
rangeFilter["$lte"] = *form.FoundedYearMax
|
||||
}
|
||||
filter["founded_year"] = rangeFilter
|
||||
}
|
||||
|
||||
// Build sort
|
||||
sort := "created_at"
|
||||
if pagination.SortBy != "" {
|
||||
sort = pagination.SortBy
|
||||
}
|
||||
|
||||
sortOrder := "desc"
|
||||
if pagination.SortOrder != "" {
|
||||
sortOrder = pagination.SortOrder
|
||||
}
|
||||
|
||||
// Build pagination
|
||||
pagination := mongo.NewPaginationBuilder().
|
||||
Limit(limit).
|
||||
Skip(offset)
|
||||
paginationBuilder := orm.NewPaginationBuilder().
|
||||
Limit(pagination.Limit).
|
||||
Skip(pagination.Offset).
|
||||
SortBy(sort, sortOrder).
|
||||
Build()
|
||||
|
||||
// Set sort
|
||||
if sortBy != "" {
|
||||
if sortOrder == "desc" {
|
||||
pagination.SortDesc(sortBy)
|
||||
} else {
|
||||
pagination.SortAsc(sortBy)
|
||||
}
|
||||
} else {
|
||||
pagination.SortDesc("created_at") // Default sort
|
||||
}
|
||||
|
||||
// Use ORM to find companies
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination.Build())
|
||||
// Use ORM to find users
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to search companies", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"search": search,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert []Company to []*Company
|
||||
companies := make([]*Company, len(result.Items))
|
||||
for i := range result.Items {
|
||||
companies[i] = &result.Items[i]
|
||||
}
|
||||
|
||||
return companies, nil
|
||||
}
|
||||
|
||||
// Count counts all companies
|
||||
func (r *companyRepository) Count(ctx context.Context) (int64, error) {
|
||||
filter := bson.M{"status": bson.M{"$ne": CompanyStatusInactive}}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count companies", map[string]interface{}{
|
||||
r.logger.Error("Failed to list users", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByType counts companies by type
|
||||
func (r *companyRepository) CountByType(ctx context.Context, companyType CompanyType) (int64, error) {
|
||||
filter := bson.M{
|
||||
"type": companyType,
|
||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
||||
}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count companies by type", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_type": companyType,
|
||||
})
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByStatus counts companies by status
|
||||
func (r *companyRepository) CountByStatus(ctx context.Context, status CompanyStatus) (int64, error) {
|
||||
filter := bson.M{"status": status}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count companies by status", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"status": status,
|
||||
})
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountByIndustry counts companies by industry
|
||||
func (r *companyRepository) CountByIndustry(ctx context.Context, industry string) (int64, error) {
|
||||
filter := bson.M{
|
||||
"industry": industry,
|
||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
||||
}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count companies by industry", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"industry": industry,
|
||||
})
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// CountVerified counts verified companies
|
||||
func (r *companyRepository) CountVerified(ctx context.Context) (int64, error) {
|
||||
filter := bson.M{
|
||||
"is_verified": true,
|
||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
||||
}
|
||||
|
||||
count, err := r.ormRepo.Count(ctx, filter)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to count verified companies", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
return result.Items, result.TotalCount, nil
|
||||
}
|
||||
|
||||
// UpdateStatus updates company status
|
||||
@@ -591,226 +365,3 @@ func (r *companyRepository) UpdateVerification(ctx context.Context, id string, i
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTags updates company tags
|
||||
func (r *companyRepository) UpdateTags(ctx context.Context, id string, tags *CompanyTags) error {
|
||||
// Get company first
|
||||
company, err := r.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update tags
|
||||
company.Tags = tags
|
||||
company.SetUpdatedAt(time.Now().Unix())
|
||||
|
||||
// Update in database
|
||||
err = r.ormRepo.Update(ctx, company)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to update company tags", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_id": id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
r.logger.Info("Company tags updated successfully", map[string]interface{}{
|
||||
"company_id": id,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTags adds tags to a company
|
||||
func (r *companyRepository) AddTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error {
|
||||
// Get company first
|
||||
company, err := r.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize tags if nil
|
||||
if company.Tags == nil {
|
||||
company.Tags = &CompanyTags{}
|
||||
}
|
||||
|
||||
// Add tags (avoiding duplicates)
|
||||
company.Tags.CPVCodes = r.addUniqueStrings(company.Tags.CPVCodes, cpvCodes)
|
||||
company.Tags.Categories = r.addUniqueStrings(company.Tags.Categories, categories)
|
||||
company.Tags.Keywords = r.addUniqueStrings(company.Tags.Keywords, keywords)
|
||||
company.Tags.Specializations = r.addUniqueStrings(company.Tags.Specializations, specializations)
|
||||
company.Tags.Certifications = r.addUniqueStrings(company.Tags.Certifications, certifications)
|
||||
|
||||
company.SetUpdatedAt(time.Now().Unix())
|
||||
|
||||
// Update in database
|
||||
err = r.ormRepo.Update(ctx, company)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to add tags to company", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_id": id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
r.logger.Info("Tags added to company successfully", map[string]interface{}{
|
||||
"company_id": id,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveTags removes tags from a company
|
||||
func (r *companyRepository) RemoveTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error {
|
||||
// Get company first
|
||||
company, err := r.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize tags if nil
|
||||
if company.Tags == nil {
|
||||
company.Tags = &CompanyTags{}
|
||||
}
|
||||
|
||||
// Remove tags
|
||||
company.Tags.CPVCodes = r.removeStrings(company.Tags.CPVCodes, cpvCodes)
|
||||
company.Tags.Categories = r.removeStrings(company.Tags.Categories, categories)
|
||||
company.Tags.Keywords = r.removeStrings(company.Tags.Keywords, keywords)
|
||||
company.Tags.Specializations = r.removeStrings(company.Tags.Specializations, specializations)
|
||||
company.Tags.Certifications = r.removeStrings(company.Tags.Certifications, certifications)
|
||||
|
||||
company.SetUpdatedAt(time.Now().Unix())
|
||||
|
||||
// Update in database
|
||||
err = r.ormRepo.Update(ctx, company)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to remove tags from company", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"company_id": id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
r.logger.Info("Tags removed from company successfully", map[string]interface{}{
|
||||
"company_id": id,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCompanyStats returns company statistics
|
||||
func (r *companyRepository) GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error) {
|
||||
// This would typically use aggregation pipelines for better performance
|
||||
// For now, we'll use basic counts
|
||||
|
||||
totalCompanies, _ := r.Count(ctx)
|
||||
activeCompanies, _ := r.CountByStatus(ctx, CompanyStatusActive)
|
||||
verifiedCompanies, _ := r.CountVerified(ctx)
|
||||
|
||||
// For this implementation, we'll return basic stats
|
||||
// In a real implementation, you'd use MongoDB aggregation pipelines
|
||||
stats := &CompanyStatsResponse{
|
||||
TotalCompanies: totalCompanies,
|
||||
ActiveCompanies: activeCompanies,
|
||||
VerifiedCompanies: verifiedCompanies,
|
||||
CompaniesByType: make(map[string]int64),
|
||||
CompaniesByIndustry: make(map[string]int64),
|
||||
CompaniesByStatus: make(map[string]int64),
|
||||
TopCategories: []CategoryCount{},
|
||||
TopCPVCodes: []CPVCodeCount{},
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// SearchByTags searches companies by tags
|
||||
func (r *companyRepository) SearchByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
// Build tag filters
|
||||
var tagFilters []bson.M
|
||||
|
||||
if len(cpvCodes) > 0 {
|
||||
tagFilters = append(tagFilters, bson.M{"tags.cpv_codes": bson.M{"$in": cpvCodes}})
|
||||
}
|
||||
|
||||
if len(categories) > 0 {
|
||||
tagFilters = append(tagFilters, bson.M{"tags.categories": bson.M{"$in": categories}})
|
||||
}
|
||||
|
||||
if len(keywords) > 0 {
|
||||
tagFilters = append(tagFilters, bson.M{"tags.keywords": bson.M{"$in": keywords}})
|
||||
}
|
||||
|
||||
if len(specializations) > 0 {
|
||||
tagFilters = append(tagFilters, bson.M{"tags.specializations": bson.M{"$in": specializations}})
|
||||
}
|
||||
|
||||
if len(tagFilters) > 0 {
|
||||
filter["$or"] = tagFilters
|
||||
}
|
||||
|
||||
// Only active companies
|
||||
filter["status"] = bson.M{"$ne": CompanyStatusInactive}
|
||||
|
||||
// Build pagination
|
||||
pagination := mongo.NewPaginationBuilder().
|
||||
Limit(limit).
|
||||
Skip(offset).
|
||||
SortDesc("created_at").
|
||||
Build()
|
||||
|
||||
// Use ORM to find companies
|
||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to search companies by tags", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert []Company to []*Company
|
||||
companies := make([]*Company, len(result.Items))
|
||||
for i := range result.Items {
|
||||
companies[i] = &result.Items[i]
|
||||
}
|
||||
|
||||
return companies, nil
|
||||
}
|
||||
|
||||
// Helper functions for tag management
|
||||
func (r *companyRepository) addUniqueStrings(existing []string, new []string) []string {
|
||||
existingMap := make(map[string]bool)
|
||||
for _, s := range existing {
|
||||
existingMap[s] = true
|
||||
}
|
||||
|
||||
result := make([]string, len(existing))
|
||||
copy(result, existing)
|
||||
|
||||
for _, s := range new {
|
||||
if !existingMap[s] {
|
||||
result = append(result, s)
|
||||
existingMap[s] = true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *companyRepository) removeStrings(existing []string, toRemove []string) []string {
|
||||
removeMap := make(map[string]bool)
|
||||
for _, s := range toRemove {
|
||||
removeMap[s] = true
|
||||
}
|
||||
|
||||
var result []string
|
||||
for _, s := range existing {
|
||||
if !removeMap[s] {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user