feat: add books APIs
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
||||
)
|
||||
|
||||
type TranscriptionRepo struct {
|
||||
col *mongo.Collection
|
||||
}
|
||||
|
||||
func NewTranscriptionRepo(db *MongoDB) *TranscriptionRepo {
|
||||
return &TranscriptionRepo{col: db.Collection("transcriptions")}
|
||||
}
|
||||
|
||||
type ContentBlockDoc struct {
|
||||
Text string `bson:"text" json:"text"`
|
||||
Format string `bson:"format" json:"format"`
|
||||
Alignment string `bson:"alignment" json:"alignment"`
|
||||
Speaker *string `bson:"speaker,omitempty" json:"speaker,omitempty"`
|
||||
}
|
||||
|
||||
type TranscriptionDoc struct {
|
||||
ID bson.ObjectID `bson:"_id" json:"id"`
|
||||
Slug string `bson:"slug" json:"slug"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
Type string `bson:"type" json:"type"`
|
||||
Date *string `bson:"date,omitempty" json:"date,omitempty"`
|
||||
Year *int `bson:"year,omitempty" json:"year,omitempty"`
|
||||
Month *int `bson:"month,omitempty" json:"month,omitempty"`
|
||||
Day *int `bson:"day,omitempty" json:"day,omitempty"`
|
||||
Place *string `bson:"place,omitempty" json:"place,omitempty"`
|
||||
Context *string `bson:"context,omitempty" json:"context,omitempty"`
|
||||
Reference *string `bson:"reference,omitempty" json:"reference,omitempty"`
|
||||
DurationMinutes *int `bson:"durationMinutes,omitempty" json:"duration_minutes,omitempty"`
|
||||
AudioUrl *string `bson:"audioUrl,omitempty" json:"audio_url,omitempty"`
|
||||
Participants []string `bson:"participants" json:"participants"`
|
||||
Content []ContentBlockDoc `bson:"content" json:"content"`
|
||||
ContentJson *string `bson:"contentJson,omitempty" json:"-"`
|
||||
Tags []string `bson:"tags" json:"tags"`
|
||||
Language string `bson:"language" json:"language"`
|
||||
Source *string `bson:"source,omitempty" json:"source,omitempty"`
|
||||
IsPublished bool `bson:"isPublished" json:"is_published"`
|
||||
}
|
||||
|
||||
type TranscriptionSummaryDoc struct {
|
||||
ID bson.ObjectID `bson:"_id" json:"id"`
|
||||
Slug string `bson:"slug" json:"slug"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
Type string `bson:"type" json:"type"`
|
||||
Date *string `bson:"date,omitempty" json:"date,omitempty"`
|
||||
Year *int `bson:"year,omitempty" json:"year,omitempty"`
|
||||
Month *int `bson:"month,omitempty" json:"month,omitempty"`
|
||||
Day *int `bson:"day,omitempty" json:"day,omitempty"`
|
||||
Place *string `bson:"place,omitempty" json:"place,omitempty"`
|
||||
Context *string `bson:"context,omitempty" json:"context,omitempty"`
|
||||
Reference *string `bson:"reference,omitempty" json:"reference,omitempty"`
|
||||
DurationMinutes *int `bson:"durationMinutes,omitempty" json:"duration_minutes,omitempty"`
|
||||
AudioUrl *string `bson:"audioUrl,omitempty" json:"audio_url,omitempty"`
|
||||
Participants []string `bson:"participants" json:"participants"`
|
||||
}
|
||||
|
||||
var publishedFilter = bson.M{"isPublished": true}
|
||||
|
||||
var transcriptionSummaryProjection = bson.M{
|
||||
"slug": 1, "name": 1, "type": 1, "date": 1,
|
||||
"year": 1, "month": 1, "day": 1, "place": 1,
|
||||
"context": 1, "reference": 1, "durationMinutes": 1,
|
||||
"audioUrl": 1, "participants": 1,
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetBySlug(ctx context.Context, slug string) (*TranscriptionDoc, error) {
|
||||
filter := bson.M{"slug": slug, "isPublished": true}
|
||||
var doc TranscriptionDoc
|
||||
err := r.col.FindOne(ctx, filter).Decode(&doc)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if len(doc.Content) == 0 && doc.ContentJson != nil && *doc.ContentJson != "" {
|
||||
var blocks []ContentBlockDoc
|
||||
if err := json.Unmarshal([]byte(*doc.ContentJson), &blocks); err == nil {
|
||||
doc.Content = blocks
|
||||
}
|
||||
}
|
||||
doc.ContentJson = nil
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetByYear(ctx context.Context, year int) ([]TranscriptionSummaryDoc, error) {
|
||||
filter := bson.M{"year": year, "isPublished": true}
|
||||
opts := options.Find().
|
||||
SetSort(bson.M{"month": 1, "day": 1}).
|
||||
SetProjection(transcriptionSummaryProjection)
|
||||
cursor, err := r.col.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []TranscriptionSummaryDoc
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetByPlace(ctx context.Context, place string) ([]TranscriptionSummaryDoc, error) {
|
||||
filter := bson.M{"place": place, "isPublished": true}
|
||||
opts := options.Find().
|
||||
SetSort(bson.M{"year": 1, "month": 1, "day": 1}).
|
||||
SetProjection(transcriptionSummaryProjection)
|
||||
cursor, err := r.col.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []TranscriptionSummaryDoc
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetByType(ctx context.Context, docType string) ([]TranscriptionSummaryDoc, error) {
|
||||
filter := bson.M{"type": docType, "isPublished": true}
|
||||
opts := options.Find().
|
||||
SetSort(bson.M{"year": 1, "month": 1, "day": 1}).
|
||||
SetProjection(transcriptionSummaryProjection)
|
||||
cursor, err := r.col.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []TranscriptionSummaryDoc
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetYears(ctx context.Context) ([]bson.M, error) {
|
||||
pipeline := bson.A{
|
||||
bson.M{"$match": bson.M{"isPublished": true, "year": bson.M{"$ne": nil}}},
|
||||
bson.M{"$group": bson.M{"_id": "$year", "count": bson.M{"$sum": 1}}},
|
||||
bson.M{"$sort": bson.M{"_id": 1}},
|
||||
}
|
||||
cursor, err := r.col.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []bson.M
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetPlaces(ctx context.Context) ([]bson.M, error) {
|
||||
pipeline := bson.A{
|
||||
bson.M{"$match": bson.M{"isPublished": true, "place": bson.M{"$ne": nil}}},
|
||||
bson.M{"$group": bson.M{"_id": "$place", "count": bson.M{"$sum": 1}}},
|
||||
bson.M{"$sort": bson.M{"count": -1}},
|
||||
}
|
||||
cursor, err := r.col.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []bson.M
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetTypeDistribution(ctx context.Context) (int64, int64, error) {
|
||||
lectureCount, err := r.col.CountDocuments(ctx, bson.M{"type": "lecture", "isPublished": true})
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
conversationCount, err := r.col.CountDocuments(ctx, bson.M{"type": "conversation", "isPublished": true})
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return lectureCount, conversationCount, nil
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) Count(ctx context.Context) (int64, error) {
|
||||
return r.col.CountDocuments(ctx, publishedFilter)
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetAdjacent(ctx context.Context, slug string, year int) (*TranscriptionSummaryDoc, *TranscriptionSummaryDoc, error) {
|
||||
filter := bson.M{"year": year, "isPublished": true}
|
||||
opts := options.Find().
|
||||
SetSort(bson.M{"month": 1, "day": 1}).
|
||||
SetProjection(transcriptionSummaryProjection)
|
||||
cursor, err := r.col.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var summaries []TranscriptionSummaryDoc
|
||||
if err := cursor.All(ctx, &summaries); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
idx := -1
|
||||
for i, s := range summaries {
|
||||
if s.Slug == slug {
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var prev, next *TranscriptionSummaryDoc
|
||||
if idx > 0 {
|
||||
prev = &summaries[idx-1]
|
||||
}
|
||||
if idx < len(summaries)-1 {
|
||||
next = &summaries[idx+1]
|
||||
}
|
||||
return prev, next, nil
|
||||
}
|
||||
Reference in New Issue
Block a user