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.

208 lines
5.0 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. }
  69. if !filter.EarliestFictionalDate.IsZero() && !filter.LatestFictionalDate.IsZero() {
  70. query["fictionalDate"] = bson.M{
  71. "$gte": filter.EarliestFictionalDate,
  72. "$lte": filter.LatestFictionalDate,
  73. }
  74. } else if !filter.EarliestFictionalDate.IsZero() {
  75. query["fictionalDate"] = bson.M{
  76. "$gte": filter.EarliestFictionalDate,
  77. }
  78. } else if !filter.LatestFictionalDate.IsZero() {
  79. query["fictionalDate"] = bson.M{
  80. "$lte": filter.LatestFictionalDate,
  81. }
  82. }
  83. stories := make([]*models.Story, 0, 32)
  84. err := r.stories.Find(query).Sort("-updatedDate ").Limit(filter.Limit).All(&stories)
  85. if err != nil {
  86. if err == mgo.ErrNotFound {
  87. return stories, nil
  88. }
  89. return nil, err
  90. }
  91. return stories, nil
  92. }
  93. func (r *storyRepository) Insert(ctx context.Context, story models.Story) (*models.Story, error) {
  94. story.ID = generate.StoryID()
  95. err := r.stories.Insert(story)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return &story, nil
  100. }
  101. func (r *storyRepository) Update(ctx context.Context, story models.Story, update models.StoryUpdate) (*models.Story, error) {
  102. updateBson := bson.M{}
  103. if update.Name != nil {
  104. updateBson["name"] = *update.Name
  105. story.Name = *update.Name
  106. }
  107. if update.Open != nil {
  108. updateBson["open"] = *update.Open
  109. story.Open = *update.Open
  110. }
  111. if update.Category != nil {
  112. updateBson["category"] = *update.Category
  113. story.Category = *update.Category
  114. }
  115. if update.Author != nil {
  116. updateBson["author"] = *update.Author
  117. story.Author = *update.Author
  118. }
  119. if update.FictionalDate != nil {
  120. updateBson["fictionalDate"] = *update.FictionalDate
  121. story.FictionalDate = *update.FictionalDate
  122. }
  123. if update.UpdatedDate != nil {
  124. updateBson["updatedDate"] = *update.UpdatedDate
  125. story.UpdatedDate = *update.UpdatedDate
  126. }
  127. if update.Listed != nil {
  128. updateBson["listed"] = *update.Listed
  129. story.Listed = *update.Listed
  130. }
  131. err := r.stories.UpdateId(story.ID, bson.M{"$set": updateBson})
  132. if err != nil {
  133. return nil, err
  134. }
  135. return &story, nil
  136. }
  137. func (r *storyRepository) AddTag(ctx context.Context, story models.Story, tag models.Tag) error {
  138. ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$addToSet": bson.M{"tags": tag}})
  139. if err != nil {
  140. return err
  141. }
  142. if ci.Updated == 0 {
  143. return repositories.ErrTagExists
  144. }
  145. return nil
  146. }
  147. func (r *storyRepository) RemoveTag(ctx context.Context, story models.Story, tag models.Tag) error {
  148. ci, err := r.stories.UpdateAll(bson.M{"_id": story.ID}, bson.M{"$pull": bson.M{"tags": tag}})
  149. if err != nil {
  150. return err
  151. }
  152. if ci.Updated == 0 {
  153. return repositories.ErrTagDoesNotExist
  154. }
  155. return nil
  156. }
  157. func (r *storyRepository) Delete(ctx context.Context, story models.Story) error {
  158. err := r.stories.RemoveId(story.ID)
  159. if err != nil {
  160. return err
  161. }
  162. chapterIds := make([]string, 0, 8)
  163. err = r.chapters.Find(bson.M{"storyId": story.ID}).Distinct("_id", &chapterIds)
  164. if err != nil {
  165. log.Println("Failed to find chapterIds:", err)
  166. return nil
  167. }
  168. if len(chapterIds) > 0 {
  169. c1, err := r.chapters.RemoveAll(bson.M{"storyId": story.ID})
  170. if err != nil {
  171. log.Println("Failed to remove chapters:", err)
  172. return nil
  173. }
  174. c2, err := r.chapters.RemoveAll(bson.M{"comments": bson.M{"$in": chapterIds}})
  175. if err != nil {
  176. log.Println("Failed to remove comments:", err)
  177. return nil
  178. }
  179. log.Printf("Removed story %s (%d chapters, %d comments)", story.ID, c1.Removed, c2.Removed)
  180. }
  181. return nil
  182. }