package repository import ( "context" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) type DiscipleRepo struct { col *mongo.Collection } func NewDiscipleRepo(db *MongoDB) *DiscipleRepo { return &DiscipleRepo{col: db.Collection("disciples")} } type DiscipleDoc struct { ID bson.ObjectID `bson:"_id" json:"id"` InitiatedName string `bson:"initiatedName" json:"initiated_name"` Suffix string `bson:"suffix" json:"suffix"` InitiationDate *time.Time `bson:"initiationDate,omitempty" json:"initiation_date,omitempty"` InitiationPlace *string `bson:"initiationPlace,omitempty" json:"initiation_place,omitempty"` KarmiName *string `bson:"karmiName,omitempty" json:"karmi_name,omitempty"` Avatar *string `bson:"avatar,omitempty" json:"avatar,omitempty"` About *string `bson:"about,omitempty" json:"about,omitempty"` Tags []string `bson:"tags" json:"tags"` IsVerified bool `bson:"isVerified" json:"is_verified"` } type DiscipleSummaryDoc struct { ID bson.ObjectID `bson:"_id" json:"id"` InitiatedName string `bson:"initiatedName" json:"initiated_name"` Suffix string `bson:"suffix" json:"suffix"` InitiationDate *time.Time `bson:"initiationDate,omitempty" json:"initiation_date,omitempty"` InitiationPlace *string `bson:"initiationPlace,omitempty" json:"initiation_place,omitempty"` IsVerified bool `bson:"isVerified" json:"is_verified"` } var discipleSummaryProjection = bson.M{ "initiatedName": 1, "suffix": 1, "initiationDate": 1, "initiationPlace": 1, "isVerified": 1, } func (r *DiscipleRepo) GetByLetter(ctx context.Context, letter string) ([]DiscipleSummaryDoc, error) { // Case-insensitive starts-with using regex filter := bson.M{"initiatedName": bson.M{"$regex": "^" + letter, "$options": "i"}} opts := options.Find(). SetSort(bson.M{"initiatedName": 1}). SetProjection(discipleSummaryProjection) cursor, err := r.col.Find(ctx, filter, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var results []DiscipleSummaryDoc if err := cursor.All(ctx, &results); err != nil { return nil, err } return results, nil } func (r *DiscipleRepo) GetByLetterAndSuffix(ctx context.Context, letter *string, suffix *string) ([]DiscipleSummaryDoc, error) { filter := bson.M{} if letter != nil { filter["initiatedName"] = bson.M{"$regex": "^" + *letter, "$options": "i"} } if suffix != nil && *suffix != "all" { filter["suffix"] = *suffix } opts := options.Find(). SetSort(bson.M{"initiatedName": 1}). SetProjection(discipleSummaryProjection) cursor, err := r.col.Find(ctx, filter, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var results []DiscipleSummaryDoc if err := cursor.All(ctx, &results); err != nil { return nil, err } return results, nil } func (r *DiscipleRepo) Count(ctx context.Context) (int64, error) { return r.col.CountDocuments(ctx, bson.M{}) } func (r *DiscipleRepo) CountFiltered(ctx context.Context, letter *string, suffix *string) (int64, error) { filter := bson.M{} if letter != nil { filter["initiatedName"] = bson.M{"$regex": "^" + *letter, "$options": "i"} } if suffix != nil && *suffix != "all" { filter["suffix"] = *suffix } return r.col.CountDocuments(ctx, filter) } func (r *DiscipleRepo) GetLetterCounts(ctx context.Context) ([]bson.M, error) { pipeline := bson.A{ bson.M{"$group": bson.M{ "_id": bson.M{"$toUpper": bson.M{"$substrBytes": bson.A{"$initiatedName", 0, 1}}}, "count": bson.M{"$sum": 1}, }}, bson.M{"$sort": bson.M{"_id": 1}}, } cursor, err := r.col.Aggregate(ctx, pipeline) if err != nil { return nil, err } defer cursor.Close(ctx) var results []bson.M if err := cursor.All(ctx, &results); err != nil { return nil, err } return results, nil } func (r *DiscipleRepo) GetSuffixCounts(ctx context.Context) ([]bson.M, error) { pipeline := bson.A{ bson.M{"$group": bson.M{"_id": "$suffix", "count": bson.M{"$sum": 1}}}, bson.M{"$sort": bson.M{"count": -1}}, } cursor, err := r.col.Aggregate(ctx, pipeline) if err != nil { return nil, err } defer cursor.Close(ctx) var results []bson.M if err := cursor.All(ctx, &results); err != nil { return nil, err } return results, nil } func (r *DiscipleRepo) Search(ctx context.Context, query string, limit int) ([]DiscipleSummaryDoc, error) { filter := bson.M{"initiatedName": bson.M{"$regex": query, "$options": "i"}} opts := options.Find(). SetSort(bson.M{"initiatedName": 1}). SetLimit(int64(limit)). SetProjection(discipleSummaryProjection) cursor, err := r.col.Find(ctx, filter, opts) if err != nil { return nil, err } defer cursor.Close(ctx) var results []DiscipleSummaryDoc if err := cursor.All(ctx, &results); err != nil { return nil, err } return results, nil }