feat: add sb front matter

This commit is contained in:
Krispire Admin
2026-06-14 14:49:14 +05:30
parent 025c83eec5
commit 4946ce1062
2 changed files with 37 additions and 11 deletions
+33 -10
View File
@@ -23,6 +23,7 @@ import (
var ( var (
fullReindex = flag.Bool("full", false, "Drop all collections and reindex everything") fullReindex = flag.Bool("full", false, "Drop all collections and reindex everything")
onlyFlag = flag.String("only", "", "Comma-separated list of collections to index (e.g., verses,letters)") onlyFlag = flag.String("only", "", "Comma-separated list of collections to index (e.g., verses,letters)")
noDrop = flag.Bool("no-drop", false, "Do not drop the collection first — upsert into the existing one (resume a partial import)")
) )
// All supported collection names // All supported collection names
@@ -146,8 +147,11 @@ func main() {
for _, name := range collectionsToIndex { for _, name := range collectionsToIndex {
sugar.Infof("=== Processing collection: %s ===", name) sugar.Infof("=== Processing collection: %s ===", name)
shouldDrop := *fullReindex || *onlyFlag != "" shouldDrop := (*fullReindex || *onlyFlag != "") && !*noDrop
if !shouldDrop && existingCollections[name] { // On a bare run (no targeting), skip collections that already exist.
// An explicit --full / --only / --no-drop always (re)processes them.
targeted := *fullReindex || *onlyFlag != "" || *noDrop
if !targeted && existingCollections[name] {
sugar.Infof("[%s] Collection already exists, skipping (use --full to force reindex)", name) sugar.Infof("[%s] Collection already exists, skipping (use --full to force reindex)", name)
continue continue
} }
@@ -192,18 +196,32 @@ func main() {
// importBatch imports documents into Typesense in batches of batchSize. // importBatch imports documents into Typesense in batches of batchSize.
func importBatch(sugar *zap.SugaredLogger, tsClient *typesense.Client, collectionName string, docs []interface{}, batchSize int) { func importBatch(sugar *zap.SugaredLogger, tsClient *typesense.Client, collectionName string, docs []interface{}, batchSize int) {
total := len(docs) total := len(docs)
action := "create" // Upsert (not create) so a re-run over an existing collection is
// idempotent and additive — essential for resuming a partial import
// when a flaky link drops batches mid-run.
action := "upsert"
for i := 0; i < total; i += batchSize { for i := 0; i < total; i += batchSize {
end := i + batchSize end := i + batchSize
if end > total { if end > total {
end = total end = total
} }
batch := docs[i:end] batch := docs[i:end]
responses, err := tsClient.Collection(collectionName).Documents().Import(context.Background(), batch, &api.ImportDocumentsParams{ // Retry the batch on transient transport errors (flaky networks can
Action: &action, // drop large TLS writes — "broken pipe" / "bad record MAC").
}) var responses []*api.ImportDocumentResponse
var err error
for attempt := 1; attempt <= 8; attempt++ {
responses, err = tsClient.Collection(collectionName).Documents().Import(context.Background(), batch, &api.ImportDocumentsParams{
Action: &action,
})
if err == nil {
break
}
sugar.Warnf("[%s] batch %d-%d attempt %d/5 failed: %v", collectionName, i, end, attempt, err)
time.Sleep(time.Duration(attempt) * time.Second)
}
if err != nil { if err != nil {
sugar.Errorf("[%s] Import error at batch %d-%d: %v", collectionName, i, end, err) sugar.Errorf("[%s] Import error at batch %d-%d (gave up): %v", collectionName, i, end, err)
continue continue
} }
// Check for per-document errors // Check for per-document errors
@@ -371,9 +389,12 @@ func indexProse(sugar *zap.SugaredLogger, tsClient *typesense.Client, mongoDB *r
_, err := tsClient.Collections().Create(context.Background(), schema) _, err := tsClient.Collections().Create(context.Background(), schema)
if err != nil { if err != nil {
sugar.Fatalf("[prose] Failed to create collection: %v", err) // An already-existing collection is fine: with --no-drop we resume an
// upsert into it (the schema is unchanged).
sugar.Warnf("[prose] Create collection: %v (continuing — assuming it exists)", err)
} else {
sugar.Info("[prose] Collection created")
} }
sugar.Info("[prose] Collection created")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel() defer cancel()
@@ -428,7 +449,9 @@ func indexProse(sugar *zap.SugaredLogger, tsClient *typesense.Client, mongoDB *r
} }
sugar.Infof("[prose] Read %d documents from MongoDB", len(docs)) sugar.Infof("[prose] Read %d documents from MongoDB", len(docs))
importBatch(sugar, tsClient, "prose", docs, 500) // Small batches keep each import POST modest in size — large multi-MB
// bodies (front-matter intro segments) are prone to drops on flaky links.
importBatch(sugar, tsClient, "prose", docs, 10)
} }
// ==================== LETTERS ==================== // ==================== LETTERS ====================
+4 -1
View File
@@ -411,10 +411,13 @@ func (s *Server) setupRouter() {
// --- Verse books with divisions --- // --- Verse books with divisions ---
// SB — canto divisions // SB — canto divisions (plus prose front-matter segments;
// the static /chapters routes take precedence over /{division}).
r.Route("/sb", func(r chi.Router) { r.Route("/sb", func(r chi.Router) {
r.Use(injectBookCode("sb")) r.Use(injectBookCode("sb"))
r.Get("/", s.bookH.GetBook) r.Get("/", s.bookH.GetBook)
r.Get("/chapters", s.bookH.ListProseChapters)
r.Get("/chapters/{slug}", s.bookH.GetProseChapter)
r.Get("/{division}", s.bookH.GetDivisionInfo) r.Get("/{division}", s.bookH.GetDivisionInfo)
r.Get("/{division}/{chapter}", s.bookH.GetDivisionChapterVerseIndex) r.Get("/{division}/{chapter}", s.bookH.GetDivisionChapterVerseIndex)
r.Get("/{division}/{chapter}/verses", s.bookH.GetDivisionChapterVerses) r.Get("/{division}/{chapter}/verses", s.bookH.GetDivisionChapterVerses)