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:
Jakub Zych
2026-11-08 19:50:50 +01:00
parent 4ffce6e6ac
commit ffd19342b2
12 changed files with 624 additions and 12 deletions

View File

@@ -498,6 +498,7 @@ func (h *Handlers) UpsertMaterial(w http.ResponseWriter, r *http.Request) {
writeOpaque500(w)
return
}
h.MaterialIndex.Sync(r.Context(), db, m.ID)
status := http.StatusOK
if created {
status = http.StatusCreated
@@ -556,6 +557,7 @@ func (h *Handlers) UpdateMaterial(w http.ResponseWriter, r *http.Request) {
case taken:
writeValidation(w, map[string][]string{"title": {"Another material already has this title and course."}})
default:
h.MaterialIndex.Sync(r.Context(), db, m.ID)
writeManaged(w, r, db, m.ID, http.StatusOK, nil)
}
}
@@ -659,6 +661,9 @@ func (h *Handlers) DeleteMaterial(w http.ResponseWriter, r *http.Request) {
writeNotFound(w)
return
}
// The delete statement has no primary key on its model, so no callback
// removes the document: remove it now that the transaction committed.
h.MaterialIndex.Remove(r.Context(), db, uint(id))
// The files go with the material: rows in the transaction, blobs after it.
if b := h.Bucket; b != nil && len(keys) > 0 {
if bucket := b(); bucket != nil {
@@ -734,6 +739,8 @@ func (h *Handlers) managedAccessChange(w http.ResponseWriter, r *http.Request, d
writeOpaque500(w)
return
}
// An access change touches only visibility (and the author), which the
// search index does not hold, so it does not sync.
writeManaged(w, r, db, m.ID, http.StatusOK, nil)
}