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.

214 lines
5.2 KiB

  1. package mongodb
  2. import (
  3. "context"
  4. "git.aiterp.net/rpdata/api/internal/generate"
  5. "git.aiterp.net/rpdata/api/models"
  6. "git.aiterp.net/rpdata/api/repositories"
  7. "github.com/globalsign/mgo"
  8. "github.com/globalsign/mgo/bson"
  9. "log"
  10. )
  11. type storyRepository struct {
  12. stories *mgo.Collection
  13. chapters *mgo.Collection
  14. comments *mgo.Collection
  15. }
  16. func newStoryRepository(db *mgo.Database) (repositories.StoryRepository, error) {
  17. collection := db.C("story.stories")
  18. err := collection.EnsureIndexKey("tags")
  19. if err != nil {
  20. return nil, err
  21. }
  22. err = collection.EnsureIndexKey("author")
  23. if err != nil {
  24. return nil, err
  25. }
  26. err = collection.EnsureIndexKey("updatedDate")
  27. if err != nil {
  28. return nil, err
  29. }
  30. err = collection.EnsureIndexKey("fictionalDate")
  31. if err != nil {
  32. return nil, err
  33. }
  34. err = collection.EnsureIndexKey("listed")
  35. if err != nil {
  36. return nil, err
  37. }
  38. return &storyRepository{
  39. stories: collection,
  40. chapters: db.C("story.chapters"),
  41. comments: db.C("story.comments"),
  42. }, nil
  43. }
  44. func (r *storyRepository) Find(ctx context.Context, id string) (*models.Story, error) {
  45. story := new(models.Story)
  46. err := r.stories.FindId(id).One(story)
  47. if err != nil {
  48. return nil, err
  49. }
  50. return story, nil
  51. }
  52. func (r *storyRepository) List(ctx context.Context, filter models.StoryFilter) ([]*models.Story, error) {
  53. query := bson.M{}
  54. if filter.Author != nil {
  55. query["author"] = *filter.Author
  56. }
  57. if filter.Category != nil {
  58. query["category"] = *filter.Category
  59. }
  60. if filter.Open != nil {
  61. query["open"] = *filter.Open
  62. }
  63. if len(filter.Tags) > 0 {
  64. query["tags"] = bson.M{"$all": filter.Tags}
  65. }
  66. if filter.Unlisted != nil {
  67. query["listed"] = !*filter.Unlisted
  68. } else {
  69. query["listed"] = true
  70. }
  71. if !filter.EarliestFictionalDate.IsZero() && !filter.LatestFictionalDate.IsZero() {
  72. query["fictionalDate"] = bson.M{
  73. "$gte": filter.EarliestFictionalDate,
  74. "$lte": filter.LatestFictionalDate,
  75. }
  76. } else if !filter.EarliestFictionalDate.IsZero() {
  77. query["fictionalDate"] = bson.M{
  78. "$gte": filter.EarliestFictionalDate,
  79. }
  80. } else if !filter.LatestFictionalDate.IsZero() {
  81. query["fictionalDate"] = bson.M{
  82. "$lte": filter.LatestFictionalDate,
  83. }
  84. }
  85. stories := make([]*models.Story, 0, 32)
  86. err := r.stories.Find(query).Sort("-updatedDate").Limit(filter.Limit).All(&stories)
  87. if err != nil {
  88. if err == mgo.ErrNotFound {
  89. return stories, nil
  90. }
  91. return nil, err
  92. }
  93. return stories, nil
  94. }
  95. func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
  96. story.ID = generate.StoryID()
  97. err := r.stories.Insert(story)
  98. if err != nil {
  99. return nil, err
  100. }
  101. return &story, nil
  102. }
  103. func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) {
  104. updateBson := bson.M{}
  105. if update.Name != nil {
  106. updateBson["name"] = *update.Name
  107. story.Name = *update.Name
  108. }
  109. if update.Open != nil {
  110. updateBson["open"] = *update.Open
  111. story.Open = *update.Open
  112. }
  113. if update.Category != nil {
  114. updateBson["category"] = *update.Category
  115. story.Category = *update.Category
  116. }
  117. if update.Author != nil {
  118. updateBson["author"] = *update.Author
  119. story.Author = *update.Author
  120. }
  121. if update.FictionalDate != nil {
  122. updateBson["fictionalDate"] = *update.FictionalDate
  123. story.FictionalDate = *update.FictionalDate
  124. }
  125. if update.UpdatedDate != nil {
  126. updateBson["updatedDate"] = *update.UpdatedDate
  127. story.UpdatedDate = *update.UpdatedDate
  128. }
  129. if update.Listed != nil {
  130. updateBson["listed"] = *update.Listed
  131. story.Listed = *update.Listed
  132. }
  133. if update.SortByFictionalDate != nil {
  134. updateBson["sortByFictionalDate"] = *update.SortByFictionalDate
  135. story.SortByFictionalDate = *update.SortByFictionalDate
  136. }
  137. err := r.stories.UpdateId(story.ID, bson.M{"$set": updateBson})
  138. if err != nil {
  139. return nil, err
  140. }
  141. return &story, nil
  142. }
  143. func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error {
  144. ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$addToSet": bson.M{"tags": tag}})
  145. if err != nil {
  146. return err
  147. }
  148. if ci.Updated == 0 {
  149. return repositories.ErrTagExists
  150. }
  151. return nil
  152. }
  153. func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error {
  154. ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$pull": bson.M{"tags": tag}})
  155. if err != nil {
  156. return err
  157. }
  158. if ci.Updated == 0 {
  159. return repositories.ErrTagDoesNotExist
  160. }
  161. return nil
  162. }
  163. func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
  164. err := r.stories.RemoveId(story.ID)
  165. if err != nil {
  166. return err
  167. }
  168. chapterIds := make([]string, 0, 8)
  169. err = r.chapters.Find(bson.M{"storyId": story.ID}).Distinct("_id", &chapterIds)
  170. if err != nil {
  171. log.Println("Failed to find chapterIds:", err)
  172. return nil
  173. }
  174. if len(chapterIds) > 0 {
  175. c1, err := r.chapters.RemoveAll(bson.M{"storyId": story.ID})
  176. if err != nil {
  177. log.Println("Failed to remove chapters:", err)
  178. return nil
  179. }
  180. c2, err := r.comments.RemoveAll(bson.M{"chapterId": bson.M{"$in": chapterIds}})
  181. if err != nil {
  182. log.Println("Failed to remove comments:", err)
  183. return nil
  184. }
  185. log.Printf("Removed story %s (%d chapters, %d comments)", story.ID, c1.Removed, c2.Removed)
  186. }
  187. return nil
  188. }