Add meilisearch for user and event

Signed-off-by: Asai Neko <sugar@sne.moe>
This commit is contained in:
2025-12-26 02:16:23 +08:00
parent 3dbcc00a2d
commit 6681ffccdf
19 changed files with 229 additions and 15 deletions

View File

@@ -3,13 +3,16 @@ package data
import (
"nixcn-cms/data/drivers"
"github.com/meilisearch/meilisearch-go"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gorm.io/gorm"
)
var Database *drivers.DBClient
var Database *gorm.DB
var Redis redis.UniversalClient
var MeiliSearch meilisearch.ServiceManager
func Init() {
// Init database
@@ -32,7 +35,7 @@ func Init() {
}
// Auto migrate
err = db.DB.AutoMigrate(&User{}, &Event{})
err = db.AutoMigrate(&User{}, &Event{})
if err != nil {
log.Error("[Database] Error migrating database: ", err)
}
@@ -40,16 +43,24 @@ func Init() {
// Init redis conection
rdbAddress := viper.GetStringSlice("cache.hosts")
dsn := drivers.RedisDSN{
rDSN := drivers.RedisDSN{
Hosts: rdbAddress,
Master: viper.GetString("cache.master"),
Username: viper.GetString("cache.username"),
Password: viper.GetString("cache.password"),
DB: viper.GetInt("cache.db"),
}
rdb, err := drivers.Redis(dsn)
rdb, err := drivers.Redis(rDSN)
if err != nil {
log.Fatal("[Redis] Error connecting to Redis: ", err)
}
Redis = rdb
// Init meilisearch
mDSN := drivers.MeiliDSN{
Host: viper.GetString("search.host"),
ApiKey: viper.GetString("search.api_key"),
}
mdb := drivers.MeiliSearch(mDSN)
MeiliSearch = mdb
}

View File

@@ -0,0 +1,13 @@
package drivers
import "github.com/meilisearch/meilisearch-go"
func MeiliSearch(dsn MeiliDSN) meilisearch.ServiceManager {
return meilisearch.New(dsn.Host,
meilisearch.WithAPIKey(dsn.ApiKey),
meilisearch.WithContentEncoding(
meilisearch.GzipEncoding,
meilisearch.BestCompression,
),
)
}

View File

@@ -16,9 +16,9 @@ func SplitHostPort(url string) (host, port string) {
return split[0], split[1]
}
func Postgres(dsn ExternalDSN) (*DBClient, error) {
func Postgres(dsn ExternalDSN) (*gorm.DB, error) {
host, port := SplitHostPort(dsn.Host)
conn := "host=" + host + " user=" + dsn.Username + " password=" + dsn.Password + " dbname=" + dsn.Name + " port=" + port + " sslmode=disable TimeZone=" + config.TZ()
db, err := gorm.Open(postgres.Open(conn), &gorm.Config{})
return &DBClient{db}, err
return db, err
}

View File

@@ -1,9 +1,5 @@
package drivers
import (
"gorm.io/gorm"
)
type ExternalDSN struct {
Host string
Name string
@@ -19,6 +15,7 @@ type RedisDSN struct {
DB int
}
type DBClient struct {
*gorm.DB
type MeiliDSN struct {
Host string
ApiKey string
}

View File

@@ -5,7 +5,9 @@ import (
"slices"
"time"
"github.com/go-viper/mapstructure/v2"
"github.com/google/uuid"
"github.com/meilisearch/meilisearch-go"
"gorm.io/datatypes"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@@ -21,6 +23,13 @@ type Event struct {
JoinedUsers datatypes.JSONSlice[uuid.UUID] `json:"joined_users"`
}
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) error {
return Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("event_id = ?", eventId).First(&self).Error; err != nil {
@@ -35,6 +44,21 @@ func (self *Event) UpdateEventById(eventId uuid.UUID) error {
if err := tx.Model(&Event{}).Where("event_id = ?", eventId).Updates(&self).Error; err != nil {
return err
}
// Update event to document index
doc := EventSearchDoc{
EventId: self.EventId.String(),
Name: self.Name,
StartTime: self.StartTime,
EndTime: self.EndTime,
}
index := MeiliSearch.Index("event")
docPrimaryKey := "event_id"
meiliOptions := &meilisearch.DocumentOptions{PrimaryKey: &docPrimaryKey}
if _, err := index.UpdateDocuments([]EventSearchDoc{doc}, meiliOptions); err != nil {
return err
}
return nil
})
}
@@ -51,6 +75,21 @@ func (self *Event) CreateEvent() error {
if err := tx.Create(&self).Error; err != nil {
return err
}
// Add event to document index
doc := EventSearchDoc{
EventId: self.EventId.String(),
Name: self.Name,
StartTime: self.StartTime,
EndTime: self.EndTime,
}
index := MeiliSearch.Index("event")
docPrimaryKey := "event_id"
meiliOptions := &meilisearch.DocumentOptions{PrimaryKey: &docPrimaryKey}
if _, err := index.AddDocuments([]EventSearchDoc{doc}, meiliOptions); err != nil {
return err
}
return nil
})
}
@@ -79,3 +118,19 @@ func (self *Event) UserJoinEvent(userId, eventId uuid.UUID) error {
return nil
})
}
func (self *Event) FastListEvents(limit, offset int64) ([]EventSearchDoc, error) {
index := MeiliSearch.Index("event")
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
}

View File

@@ -3,7 +3,9 @@ package data
import (
"time"
"github.com/go-viper/mapstructure/v2"
"github.com/google/uuid"
"github.com/meilisearch/meilisearch-go"
"gorm.io/datatypes"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@@ -28,6 +30,16 @@ type User struct {
PermissionLevel uint `json:"permission_level" gorm:"default:10;not null"`
}
type UserSearchDoc struct {
UserId string `json:"user_id"`
Email string `json:"email"`
Type string `json:"type"`
Nickname string `json:"nickname"`
Subtitle string `json:"subtitle"`
Avatar string `json:"avatar"`
PermissionLevel uint `json:"permission_level"`
}
func (self *User) GetByEmail(email string) error {
if err := Database.Where("email = ?", email).First(&self).Error; err != nil {
return err
@@ -57,6 +69,23 @@ func (self *User) UpdateCheckin(userId, eventId uuid.UUID, time time.Time) error
return err // rollback
}
// Update user to document index
doc := UserSearchDoc{
UserId: self.UserId.String(),
Email: self.Email,
Type: self.Type,
Nickname: self.Nickname,
Subtitle: self.Subtitle,
Avatar: self.Avatar,
PermissionLevel: self.PermissionLevel,
}
index := MeiliSearch.Index("user")
docPrimaryKey := "user_id"
meiliOptions := &meilisearch.DocumentOptions{PrimaryKey: &docPrimaryKey}
if _, err := index.UpdateDocuments([]UserSearchDoc{doc}, meiliOptions); err != nil {
return err
}
return nil // commit
})
}
@@ -74,6 +103,23 @@ func (self *User) Create() error {
return err
}
// Create user to document index
doc := UserSearchDoc{
UserId: self.UserId.String(),
Email: self.Email,
Type: self.Type,
Nickname: self.Nickname,
Subtitle: self.Subtitle,
Avatar: self.Avatar,
PermissionLevel: self.PermissionLevel,
}
index := MeiliSearch.Index("user")
docPrimaryKey := "user_id"
meiliOptions := &meilisearch.DocumentOptions{PrimaryKey: &docPrimaryKey}
if _, err := index.AddDocuments([]UserSearchDoc{doc}, meiliOptions); err != nil {
return err
}
return nil
})
}
@@ -86,3 +132,19 @@ func (self *User) UpdateByUserID(userId uuid.UUID) error {
return nil
})
}
func (self *User) FastListUsers(limit, offset int64) ([]UserSearchDoc, error) {
index := MeiliSearch.Index("user")
result, err := index.Search("", &meilisearch.SearchRequest{
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var list []UserSearchDoc
if err := mapstructure.Decode(result.Hits, &list); err != nil {
return nil, err
}
return list, nil
}