Remove search engine, add event list api
Signed-off-by: Asai Neko <sugar@sne.moe>
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -103,10 +102,6 @@ func (self *Attendance) Create(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -147,60 +142,9 @@ func (self *Attendance) Update(ctx context.Context, attendanceId uuid.UUID, chec
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sync to MeiliSearch (eventual consistency)
|
||||
if err := attendance.UpdateSearchIndex(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &attendance, nil
|
||||
}
|
||||
|
||||
func (self *Attendance) SearchUsersByEvent(ctx context.Context, eventID string) (*meilisearch.SearchResponse, error) {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
|
||||
return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Filter: "event_id = \"" + eventID + "\"",
|
||||
Sort: []string{"checkin_at:asc"},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *Attendance) SearchEventsByUser(ctx context.Context, userID string) (*meilisearch.SearchResponse, error) {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
|
||||
return index.SearchWithContext(ctx, "", &meilisearch.SearchRequest{
|
||||
Filter: "user_id = \"" + userID + "\"",
|
||||
Sort: []string{"checkin_at:asc"},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *Attendance) UpdateSearchIndex(ctx context.Context) error {
|
||||
doc := AttendanceSearchDoc{
|
||||
AttendanceId: self.AttendanceId.String(),
|
||||
EventId: self.EventId.String(),
|
||||
UserId: self.UserId.String(),
|
||||
CheckinAt: self.CheckinAt,
|
||||
}
|
||||
|
||||
index := MeiliSearch.Index("attendance")
|
||||
|
||||
primaryKey := "attendance_id"
|
||||
opts := &meilisearch.DocumentOptions{
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocumentsWithContext(ctx, []AttendanceSearchDoc{doc}, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Attendance) DeleteSearchIndex(ctx context.Context) error {
|
||||
index := MeiliSearch.Index("attendance")
|
||||
_, err := index.DeleteDocumentWithContext(ctx, self.AttendanceId.String(), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *Attendance) GenCheckinCode(ctx context.Context, eventId uuid.UUID) (*string, error) {
|
||||
ttl := viper.GetDuration("ttl.checkin_code_ttl")
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
10
data/data.go
10
data/data.go
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"log/slog"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/gorm"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
|
||||
var Database *gorm.DB
|
||||
var Redis redis.UniversalClient
|
||||
var MeiliSearch meilisearch.ServiceManager
|
||||
|
||||
func Init(ctx context.Context) {
|
||||
// Init database
|
||||
@@ -62,12 +60,4 @@ func Init(ctx context.Context) {
|
||||
os.Exit(1)
|
||||
}
|
||||
Redis = rdb
|
||||
|
||||
// Init meilisearch
|
||||
mDSN := drivers.MeiliDSN{
|
||||
Host: viper.GetString("search.host"),
|
||||
ApiKey: viper.GetString("search.api_key"),
|
||||
}
|
||||
mdb := drivers.MeiliSearch(mDSN)
|
||||
MeiliSearch = mdb
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"github.com/spf13/viper"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
)
|
||||
|
||||
func MeiliSearch(dsn MeiliDSN) meilisearch.ServiceManager {
|
||||
serviceName := viper.GetString("search.service_name")
|
||||
|
||||
otelTransport := otelhttp.NewTransport(
|
||||
http.DefaultTransport,
|
||||
otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
|
||||
return fmt.Sprintf("%s %s", serviceName, r.Method)
|
||||
}),
|
||||
)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: otelTransport,
|
||||
}
|
||||
|
||||
return meilisearch.New(dsn.Host,
|
||||
meilisearch.WithAPIKey(dsn.ApiKey),
|
||||
meilisearch.WithCustomClient(httpClient),
|
||||
meilisearch.WithContentEncoding(
|
||||
meilisearch.GzipEncoding,
|
||||
meilisearch.BestCompression,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -23,7 +21,7 @@ type Event struct {
|
||||
EnableKYC bool `json:"enable_kyc" gorm:"not null"`
|
||||
}
|
||||
|
||||
type EventSearchDoc struct {
|
||||
type EventIndexDoc struct {
|
||||
EventId string `json:"event_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
@@ -68,11 +66,6 @@ func (self *Event) UpdateEventById(ctx context.Context, eventId uuid.UUID) error
|
||||
return err
|
||||
}
|
||||
|
||||
// Sync search index
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,12 +83,6 @@ func (self *Event) Create(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Search index (eventual consistency)
|
||||
if err := self.UpdateSearchIndex(ctx); err != nil {
|
||||
// TODO: async retry / log
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -108,51 +95,19 @@ func (self *Event) GetFullTable(ctx context.Context) (*[]Event, error) {
|
||||
return &events, err
|
||||
}
|
||||
|
||||
func (self *Event) FastListEvents(ctx context.Context, limit, offset int64) (*[]EventSearchDoc, error) {
|
||||
index := MeiliSearch.Index("event")
|
||||
func (self *Event) FastListEvents(ctx context.Context, limit, offset int64) (*[]EventIndexDoc, error) {
|
||||
var results []EventIndexDoc
|
||||
|
||||
err := Database.WithContext(ctx).
|
||||
Model(&Event{}).
|
||||
Select("event_id", "name", "type", "description", "start_time", "end_time").
|
||||
Limit(int(limit)).
|
||||
Offset(int(offset)).
|
||||
Scan(&results).Error
|
||||
|
||||
// Fast read from MeiliSearch (no DB involved)
|
||||
result, err := index.SearchWithContext(ctx, "", &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(ctx context.Context) error {
|
||||
doc := EventSearchDoc{
|
||||
EventId: self.EventId.String(),
|
||||
Name: self.Name,
|
||||
Type: self.Type,
|
||||
Description: self.Description,
|
||||
StartTime: self.StartTime,
|
||||
EndTime: self.EndTime,
|
||||
}
|
||||
index := MeiliSearch.Index("event")
|
||||
|
||||
primaryKey := "event_id"
|
||||
opts := &meilisearch.DocumentOptions{
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocumentsWithContext(ctx, []EventSearchDoc{doc}, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Event) DeleteSearchIndex(ctx context.Context) error {
|
||||
index := MeiliSearch.Index("event")
|
||||
_, err := index.DeleteDocumentWithContext(ctx, self.EventId.String(), nil)
|
||||
return err
|
||||
return &results, nil
|
||||
}
|
||||
|
||||
69
data/user.go
69
data/user.go
@@ -3,9 +3,7 @@ package data
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -23,7 +21,7 @@ type User struct {
|
||||
AllowPublic bool `json:"allow_public" gorm:"default:false;not null"`
|
||||
}
|
||||
|
||||
type UserSearchDoc struct {
|
||||
type UserIndexDoc struct {
|
||||
UserId string `json:"user_id"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
@@ -118,12 +116,6 @@ func (self *User) Create(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Search index (eventual consistency)
|
||||
if err := self.UpdateSearchIndex(&ctx); err != nil {
|
||||
// TODO: async retry / log
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -139,8 +131,7 @@ func (self *User) UpdateByUserID(ctx context.Context, userId *uuid.UUID, updates
|
||||
if err := tx.Where("user_id = ?", userId).First(&updatedUser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updatedUser.UpdateSearchIndex(&ctx)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -153,55 +144,19 @@ func (self *User) GetFullTable(ctx context.Context) (*[]User, error) {
|
||||
return &users, nil
|
||||
}
|
||||
|
||||
func (self *User) FastListUsers(ctx context.Context, limit, offset *int64) (*[]UserSearchDoc, error) {
|
||||
index := MeiliSearch.Index("user")
|
||||
func (self *User) FastListUsers(ctx context.Context, limit, offset *int) (*[]UserIndexDoc, error) {
|
||||
var results []UserIndexDoc
|
||||
|
||||
query := Database.WithContext(ctx).Model(&User{})
|
||||
|
||||
err := query.Select("user_id", "email", "username", "nickname", "subtitle", "avatar").
|
||||
Limit(*limit).
|
||||
Offset(*offset).
|
||||
Scan(&results).Error
|
||||
|
||||
// Fast read from MeiliSearch, no DB involved
|
||||
result, err := index.SearchWithContext(ctx, "", &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
|
||||
}
|
||||
|
||||
func (self *User) UpdateSearchIndex(ctx *context.Context) error {
|
||||
doc := UserSearchDoc{
|
||||
UserId: self.UserId.String(),
|
||||
Email: self.Email,
|
||||
Username: self.Username,
|
||||
Nickname: self.Nickname,
|
||||
Subtitle: self.Subtitle,
|
||||
Avatar: self.Avatar,
|
||||
}
|
||||
index := MeiliSearch.Index("user")
|
||||
|
||||
primaryKey := "user_id"
|
||||
opts := &meilisearch.DocumentOptions{
|
||||
PrimaryKey: &primaryKey,
|
||||
}
|
||||
|
||||
if _, err := index.UpdateDocumentsWithContext(
|
||||
*ctx,
|
||||
[]UserSearchDoc{doc},
|
||||
opts,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *User) DeleteSearchIndex(ctx *context.Context) error {
|
||||
index := MeiliSearch.Index("user")
|
||||
_, err := index.DeleteDocumentWithContext(*ctx, self.UserId.String(), nil)
|
||||
return err
|
||||
return &results, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user