feat: keep the materials index in sync and add bm:materials-reindex
- the materials admin syncs after create and update and removes after delete, once the save transaction commits (UseMaterialIndex, set at Boot) - POST and PUT /api/v1/bm/materials sync after the transaction, DELETE removes the document (its statement has no primary key, so no callback) - bm:materials-reindex [--fresh] [--if-configured] ensures the index and syncs every material; it refuses without an engine and runs nightly at 04:15 with --if-configured - TestMaterialSearchAgainstDatabase (Postgres) is written for the final verification phase
This commit is contained in:
77
controllers/materials_hooks.go
Normal file
77
controllers/materials_hooks.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/cabana"
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
"git.golem15.com/jakub/sm-bm-plugin/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
_ pact.FormAfterCreate = materialsAdmin{}
|
||||
_ pact.FormAfterUpdate = materialsAdmin{}
|
||||
_ pact.FormAfterDelete = materialsAdmin{}
|
||||
)
|
||||
|
||||
// MaterialIndex keeps the materials search index current once a write
|
||||
// commits (classes.MaterialIndexer). Its calls never fail the write.
|
||||
type MaterialIndex interface {
|
||||
SyncAfterCommit(ctx context.Context, tx *gorm.DB, id uint)
|
||||
RemoveAfterCommit(ctx context.Context, tx *gorm.DB, id uint)
|
||||
}
|
||||
|
||||
type materialIndexHolder struct{ ix MaterialIndex }
|
||||
|
||||
var materialIndex atomic.Value
|
||||
|
||||
// UseMaterialIndex installs the index the materials admin syncs after a
|
||||
// save or delete; nil turns the syncs off. Boot installs it, because the
|
||||
// generated controller value has no field to carry it.
|
||||
func UseMaterialIndex(ix MaterialIndex) {
|
||||
materialIndex.Store(materialIndexHolder{ix: ix})
|
||||
}
|
||||
|
||||
func installedMaterialIndex() MaterialIndex {
|
||||
h, _ := materialIndex.Load().(materialIndexHolder)
|
||||
return h.ix
|
||||
}
|
||||
|
||||
// FormAfterCreate syncs the new material once the save transaction commits.
|
||||
func (materialsAdmin) FormAfterCreate(ctx context.Context, model any) error {
|
||||
return materialAfterCommit(ctx, model, false)
|
||||
}
|
||||
|
||||
// FormAfterUpdate syncs the material once the save transaction commits.
|
||||
func (materialsAdmin) FormAfterUpdate(ctx context.Context, model any) error {
|
||||
return materialAfterCommit(ctx, model, false)
|
||||
}
|
||||
|
||||
// FormAfterDelete removes the material's document once the delete commits.
|
||||
func (materialsAdmin) FormAfterDelete(ctx context.Context, model any) error {
|
||||
return materialAfterCommit(ctx, model, true)
|
||||
}
|
||||
|
||||
// materialAfterCommit schedules the index work of an admin save on the save
|
||||
// transaction. Only a model that is not a material is an error: the index
|
||||
// never fails a save.
|
||||
func materialAfterCommit(ctx context.Context, model any, remove bool) error {
|
||||
m, ok := model.(*models.Material)
|
||||
if !ok || m == nil {
|
||||
return errors.New("materials admin: form model is not a material")
|
||||
}
|
||||
ix := installedMaterialIndex()
|
||||
if ix == nil || m.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
tx, _ := cabana.TxFromContext(ctx)
|
||||
if remove {
|
||||
ix.RemoveAfterCommit(ctx, tx, m.ID)
|
||||
} else {
|
||||
ix.SyncAfterCommit(ctx, tx, m.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user