GraphQL API and utilities for the rpdata project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

224 lines
5.4 KiB

package mongodb
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/internal/generate"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/repositories"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"log"
"strings"
)
type storyRepository struct {
stories *mgo.Collection
chapters *mgo.Collection
comments *mgo.Collection
restoreIDs bool
}
func newStoryRepository(db *mgo.Database, restoreIDs bool) (repositories.StoryRepository, error) {
collection := db.C("story.stories")
err := collection.EnsureIndexKey("tags")
if err != nil {
return nil, err
}
err = collection.EnsureIndexKey("author")
if err != nil {
return nil, err
}
err = collection.EnsureIndexKey("updatedDate")
if err != nil {
return nil, err
}
err = collection.EnsureIndexKey("fictionalDate")
if err != nil {
return nil, err
}
err = collection.EnsureIndexKey("listed")
if err != nil {
return nil, err
}
return &storyRepository{
stories: collection,
restoreIDs: restoreIDs,
chapters: db.C("story.chapters"),
comments: db.C("story.comments"),
}, nil
}
func (r *storyRepository) Find(ctx context.Context, id string) (*models.Story, error) {
story := new(models.Story)
err := r.stories.FindId(id).One(story)
if err != nil {
return nil, err
}
return story, nil
}
func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
query := bson.M{}
if filter.Author != nil {
query["author"] = *filter.Author
}
if filter.Category != nil {
query["category"] = *filter.Category
}
if filter.Open != nil {
query["open"] = *filter.Open
}
if len(filter.Tags) > 0 {
query["tags"] = bson.M{"$all": filter.Tags}
}
if filter.Unlisted != nil {
query["listed"] = !*filter.Unlisted
} else {
query["listed"] = true
}
if !filter.EarliestFictionalDate.IsZero() && !filter.LatestFictionalDate.IsZero() {
query["fictionalDate"] = bson.M{
"$gte": filter.EarliestFictionalDate,
"$lte": filter.LatestFictionalDate,
}
} else if !filter.EarliestFictionalDate.IsZero() {
query["fictionalDate"] = bson.M{
"$gte": filter.EarliestFictionalDate,
}
} else if !filter.LatestFictionalDate.IsZero() {
query["fictionalDate"] = bson.M{
"$lte": filter.LatestFictionalDate,
}
}
stories := make([]*models.Story, 0, 32)
err := r.stories.Find(query).Sort("-updatedDate").Limit(filter.Limit).All(&stories)
if err != nil {
if err == mgo.ErrNotFound {
return stories, nil
}
return nil, err
}
return stories, nil
}
func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
if !r.restoreIDs {
story.ID = generate.StoryID()
} else {
if len(story.ID) != len(generate.StoryID()) && strings.HasPrefix(story.ID, "S") {
return nil, errors.New("invalid story id")
}
}
err := r.stories.Insert(story)
if err != nil {
return nil, err
}
return &story, nil
}
func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) {
updateBson := bson.M{}
if update.Name != nil {
updateBson["name"] = *update.Name
story.Name = *update.Name
}
if update.Open != nil {
updateBson["open"] = *update.Open
story.Open = *update.Open
}
if update.Category != nil {
updateBson["category"] = *update.Category
story.Category = *update.Category
}
if update.Author != nil {
updateBson["author"] = *update.Author
story.Author = *update.Author
}
if update.FictionalDate != nil {
updateBson["fictionalDate"] = *update.FictionalDate
story.FictionalDate = *update.FictionalDate
}
if update.UpdatedDate != nil {
updateBson["updatedDate"] = *update.UpdatedDate
story.UpdatedDate = *update.UpdatedDate
}
if update.Listed != nil {
updateBson["listed"] = *update.Listed
story.Listed = *update.Listed
}
if update.SortByFictionalDate != nil {
updateBson["sortByFictionalDate"] = *update.SortByFictionalDate
story.SortByFictionalDate = *update.SortByFictionalDate
}
err := r.stories.UpdateId(story.ID, bson.M{"$set": updateBson})
if err != nil {
return nil, err
}
return &story, nil
}
func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error {
ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$addToSet": bson.M{"tags": tag}})
if err != nil {
return err
}
if ci.Updated == 0 {
return repositories.ErrTagExists
}
return nil
}
func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error {
ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$pull": bson.M{"tags": tag}})
if err != nil {
return err
}
if ci.Updated == 0 {
return repositories.ErrTagDoesNotExist
}
return nil
}
func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
err := r.stories.RemoveId(story.ID)
if err != nil {
return err
}
chapterIds := make([]string, 0, 8)
err = r.chapters.Find(bson.M{"storyId": story.ID}).Distinct("_id", &chapterIds)
if err != nil {
log.Println("Failed to find chapterIds:", err)
return nil
}
if len(chapterIds) > 0 {
c1, err := r.chapters.RemoveAll(bson.M{"storyId": story.ID})
if err != nil {
log.Println("Failed to remove chapters:", err)
return nil
}
c2, err := r.comments.RemoveAll(bson.M{"chapterId": bson.M{"$in": chapterIds}})
if err != nil {
log.Println("Failed to remove comments:", err)
return nil
}
log.Printf("Removed story %s (%d chapters, %d comments)", story.ID, c1.Removed, c2.Removed)
}
return nil
}