package data import ( "time" "github.com/go-viper/mapstructure/v2" "github.com/google/uuid" "github.com/meilisearch/meilisearch-go" "gorm.io/gorm" ) type Event struct { Id uint `json:"id" gorm:"primarykey;autoincrement"` UUID uuid.UUID `json:"uuid" gorm:"type:uuid;uniqueIndex;not null"` EventId uuid.UUID `json:"event_id" gorm:"type:uuid;uniqueIndex;not null"` Name string `json:"name" gorm:"type:varchar(255);index;not null"` StartTime time.Time `json:"start_time" gorm:"index"` EndTime time.Time `json:"end_time" gorm:"index"` } type EventSearchDoc struct { EventId string `json:"event_id"` Name string `json:"name"` StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` } func (self *Event) GetEventById(eventId uuid.UUID) (*Event, error) { var event Event err := Database. Where("event_id = ?", eventId). First(&event).Error if err != nil { if err == gorm.ErrRecordNotFound { return nil, nil } return nil, err } return &event, nil } func (self *Event) UpdateEventById(eventId uuid.UUID) error { // DB transaction if err := Database.Transaction(func(tx *gorm.DB) error { // Update by business key if err := tx. Model(&Event{}). Where("event_id = ?", eventId). Updates(self).Error; err != nil { return err } // Reload to ensure struct is fresh return tx. Where("event_id = ?", eventId). First(self).Error }); err != nil { return err } // Sync search index if err := self.UpdateSearchIndex(); err != nil { return err } return nil } func (self *Event) Create() error { self.UUID = uuid.New() self.EventId = uuid.New() // DB transaction only if err := Database.Transaction(func(tx *gorm.DB) error { if err := tx.Create(self).Error; err != nil { return err } return nil }); err != nil { return err } // Search index (eventual consistency) if err := self.UpdateSearchIndex(); err != nil { // TODO: async retry / log return err } return nil } func (self *Event) GetFullTable() (*[]Event, error) { var events []Event err := Database.Find(&events).Error if err != nil { return nil, err } return &events, err } func (self *Event) FastListEvents(limit, offset int64) (*[]EventSearchDoc, error) { index := MeiliSearch.Index("event") // Fast read from MeiliSearch (no DB involved) result, err := index.Search("", &meilisearch.SearchRequest{ Limit: limit, Offset: offset, }) if err != nil { return nil, err } var list []EventSearchDoc if err := mapstructure.Decode(result.Hits, &list); err != nil { return nil, err } return &list, nil } func (self *Event) UpdateSearchIndex() error { doc := EventSearchDoc{ EventId: self.EventId.String(), Name: self.Name, StartTime: self.StartTime, EndTime: self.EndTime, } index := MeiliSearch.Index("event") primaryKey := "event_id" opts := &meilisearch.DocumentOptions{ PrimaryKey: &primaryKey, } if _, err := index.UpdateDocuments([]EventSearchDoc{doc}, opts); err != nil { return err } return nil } func (self *Event) DeleteSearchIndex() error { index := MeiliSearch.Index("event") _, err := index.DeleteDocument(self.EventId.String(), nil) return err }