package postgres import ( "context" "errors" "gorm.io/gorm/clause" otterError "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/error" "git.anthrove.art/Anthrove/otter-space-sdk/v2/pkg/models" log "github.com/sirupsen/logrus" "gorm.io/gorm" ) func CreateTag(ctx context.Context, db *gorm.DB, tagName models.AnthroveTagName, tagType models.TagType) error { if tagName == "" { return &otterError.EntityValidationFailed{Reason: "tagName cannot be empty"} } if tagType == "" { return &otterError.EntityValidationFailed{Reason: "tagType cannot be empty"} } result := db.WithContext(ctx).Create(&models.Tag{Name: string(tagName), Type: tagType}) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } if result.RowsAffected == 0 { return &otterError.NoDataWritten{} } log.WithFields(log.Fields{ "tag_name": tagName, "tag_type": tagType, }).Trace("database: created tag node") return nil } func CreateTagInBatchAndUpdate(ctx context.Context, db *gorm.DB, tags []models.Tag, batchSize int) error { if len(tags) == 0 { return &otterError.EntityValidationFailed{Reason: "tags cannot be empty"} } if tags == nil { return &otterError.EntityValidationFailed{Reason: "tags cannot be nil"} } if batchSize == 0 { return &otterError.EntityValidationFailed{Reason: "batch size cannot be zero"} } result := db.WithContext(ctx). Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "name"}}, DoUpdates: clause.AssignmentColumns([]string{"tag_type"}), }).CreateInBatches(tags, batchSize) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } if result.RowsAffected == 0 { return &otterError.NoDataWritten{} } log.WithFields(log.Fields{ "tag_size": len(tags), "batch_size": batchSize, }).Trace("database: created tag node") return nil } func DeleteTag(ctx context.Context, db *gorm.DB, tagName models.AnthroveTagName) error { if tagName == "" { return &otterError.EntityValidationFailed{Reason: "tagName cannot be empty"} } result := db.WithContext(ctx).Delete(&models.Tag{Name: string(tagName)}) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return &otterError.NoDataFound{} } return result.Error } log.WithFields(log.Fields{ "tag_name": tagName, }).Trace("database: deleted tag") return nil } func GetAllTagByTagsType(ctx context.Context, db *gorm.DB, tagType models.TagType) ([]models.Tag, error) { var tags []models.Tag if tagType == "" { return nil, &otterError.EntityValidationFailed{Reason: "tagType cannot be empty"} } result := db.WithContext(ctx).Model(&models.Tag{}).Where("tag_type = ?", tagType).Scan(&tags) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tags_length": len(tags), }).Trace("database: got tag") return tags, nil } func CreateTagAndReferenceToPost(ctx context.Context, db *gorm.DB, anthrovePostID models.AnthrovePostID, tag *models.Tag) error { if anthrovePostID == "" { return &otterError.EntityValidationFailed{Reason: "anthrovePostID cannot be empty"} } if len(anthrovePostID) != 25 { return &otterError.EntityValidationFailed{Reason: "anthrovePostID needs to be 25 characters long"} } if tag == nil { return &otterError.EntityValidationFailed{Reason: "Tag is nil"} } pgPost := models.Post{ BaseModel: models.BaseModel[models.AnthrovePostID]{ ID: anthrovePostID, }, } err := db.WithContext(ctx).Model(&pgPost).Association("Tags").Append(tag) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return &otterError.NoDataFound{} } return errors.Join(err, &otterError.NoRelationCreated{}) } log.WithFields(log.Fields{ "anthrove_post_id": anthrovePostID, "tag_name": tag.Name, "tag_type": tag.Type, }).Trace("database: created tag node") return nil } func GetTags(ctx context.Context, db *gorm.DB) ([]models.Tag, error) { var tags []models.Tag result := db.WithContext(ctx).Find(&tags) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tag_amount": len(tags), }).Trace("database: got tags") return tags, nil } func CreateTagAlias(ctx context.Context, db *gorm.DB, tagAliasName models.AnthroveTagAliasName, tagID models.AnthroveTagID) error { if tagAliasName == "" { return &otterError.EntityValidationFailed{Reason: "tagAliasName cannot be empty"} } if tagID == "" { return &otterError.EntityValidationFailed{Reason: otterError.AnthroveTagIDEmpty} } result := db.WithContext(ctx).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "name"}}, DoNothing: true, }).Create(&models.TagAlias{ Name: string(tagAliasName), TagID: string(tagID), }) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } log.WithFields(log.Fields{ "tag_alias_name": tagAliasName, "tag_alias_tag_id": tagID, }).Trace("database: created tagAlias") return nil } func CreateTagAliasInBatch(ctx context.Context, db *gorm.DB, tagAliases []models.TagAlias, batchSize int) error { if len(tagAliases) == 0 { return &otterError.EntityValidationFailed{Reason: "tagAliases cannot be empty"} } if tagAliases == nil { return &otterError.EntityValidationFailed{Reason: "tagAliases cannot be nil"} } if batchSize == 0 { return &otterError.EntityValidationFailed{Reason: "batch size cannot be zero"} } result := db.WithContext(ctx).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "name"}}, DoNothing: true, }).CreateInBatches(tagAliases, batchSize) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } if result.RowsAffected == 0 { return &otterError.NoDataWritten{} } log.WithFields(log.Fields{ "tag_size": len(tagAliases), "batch_size": batchSize, }).Trace("database: created tag node") return nil } func GetAllTagAlias(ctx context.Context, db *gorm.DB) ([]models.TagAlias, error) { var tagAliases []models.TagAlias result := db.WithContext(ctx).Find(&tagAliases) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tag_alias_length": len(tagAliases), }).Trace("database: created tagAlias") return tagAliases, nil } func GetAllTagAliasByTag(ctx context.Context, db *gorm.DB, tagID models.AnthroveTagID) ([]models.TagAlias, error) { var tagAliases []models.TagAlias if tagID == "" { return nil, &otterError.EntityValidationFailed{Reason: otterError.AnthroveTagIDEmpty} } result := db.WithContext(ctx).Where("tag_id = ?", tagID).Find(&tagAliases) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tag_alias_length": len(tagAliases), "tag_alias_tag_id": tagID, }).Trace("database: get specific tagAlias") return tagAliases, nil } func DeleteTagAlias(ctx context.Context, db *gorm.DB, tagAliasName models.AnthroveTagAliasName) error { if tagAliasName == "" { return &otterError.EntityValidationFailed{Reason: "tagAliasName cannot be empty"} } result := db.WithContext(ctx).Delete(&models.TagAlias{Name: string(tagAliasName)}) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return &otterError.NoDataFound{} } return result.Error } log.WithFields(log.Fields{ "tag_alias_name": tagAliasName, }).Trace("database: deleted tagAlias") return nil } func CreateTagGroup(ctx context.Context, db *gorm.DB, tagGroupName models.AnthroveTagGroupName, tagID models.AnthroveTagID) error { if tagGroupName == "" { return &otterError.EntityValidationFailed{Reason: "tagGroupName cannot be empty"} } if tagID == "" { return &otterError.EntityValidationFailed{Reason: otterError.AnthroveTagIDEmpty} } result := db.WithContext(ctx).Create(&models.TagGroup{ Name: string(tagGroupName), TagID: string(tagID), }) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } log.WithFields(log.Fields{ "tag_group_name": tagGroupName, "tag_group_tag_id": tagID, }).Trace("database: created tagGroup") return nil } func CreateTagGroupInBatch(ctx context.Context, db *gorm.DB, tagGroups []models.TagGroup, batchSize int) error { if len(tagGroups) == 0 { return &otterError.EntityValidationFailed{Reason: "tagAliases cannot be empty"} } if tagGroups == nil { return &otterError.EntityValidationFailed{Reason: "tagAliases cannot be nil"} } if batchSize == 0 { return &otterError.EntityValidationFailed{Reason: "batch size cannot be zero"} } result := db.WithContext(ctx).CreateInBatches(tagGroups, batchSize) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return &otterError.EntityAlreadyExists{} } return result.Error } if result.RowsAffected == 0 { return &otterError.NoDataWritten{} } log.WithFields(log.Fields{ "tag_size": len(tagGroups), "batch_size": batchSize, }).Trace("database: created tag node") return nil } func GetAllTagGroup(ctx context.Context, db *gorm.DB) ([]models.TagGroup, error) { var tagGroups []models.TagGroup result := db.WithContext(ctx).Find(&tagGroups) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tag_alias_length": len(tagGroups), }).Trace("database: created tagGroup") return tagGroups, nil } func GetAllTagGroupByTag(ctx context.Context, db *gorm.DB, tagID models.AnthroveTagID) ([]models.TagGroup, error) { var tagGroups []models.TagGroup if tagID == "" { return nil, &otterError.EntityValidationFailed{Reason: otterError.AnthroveTagIDEmpty} } result := db.WithContext(ctx).Where("tag_id = ?", tagID).Find(&tagGroups) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, &otterError.NoDataFound{} } return nil, result.Error } log.WithFields(log.Fields{ "tag_alias_length": len(tagGroups), "tag_alias_tag_id": tagID, }).Trace("database: get specific tagGroup") return tagGroups, nil } func DeleteTagGroup(ctx context.Context, db *gorm.DB, tagGroupName models.AnthroveTagGroupName) error { if tagGroupName == "" { return &otterError.EntityValidationFailed{Reason: "tagGroupName cannot be empty"} } result := db.WithContext(ctx).Delete(&models.TagGroup{Name: string(tagGroupName)}) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return &otterError.NoDataFound{} } return result.Error } log.WithFields(log.Fields{ "tag_alias_name": tagGroupName, }).Trace("database: deleted tagAlias") return nil }