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.

217 lines
5.2 KiB

  1. package mongodb
  2. import (
  3. "context"
  4. "errors"
  5. "github.com/globalsign/mgo"
  6. "github.com/globalsign/mgo/bson"
  7. "sort"
  8. "strconv"
  9. "git.aiterp.net/rpdata/api/models"
  10. "git.aiterp.net/rpdata/api/repositories"
  11. )
  12. type characterRepository struct {
  13. characters *mgo.Collection
  14. cidCounter *counter
  15. }
  16. func newCharacterRepository(db *mgo.Database) (repositories.CharacterRepository, error) {
  17. collection := db.C("common.characters")
  18. err := collection.EnsureIndexKey("name")
  19. if err != nil {
  20. return nil, err
  21. }
  22. err = collection.EnsureIndexKey("shortName")
  23. if err != nil {
  24. return nil, err
  25. }
  26. err = collection.EnsureIndexKey("author")
  27. if err != nil {
  28. return nil, err
  29. }
  30. err = collection.EnsureIndex(mgo.Index{
  31. Key: []string{"nicks"},
  32. Unique: true,
  33. DropDups: true,
  34. })
  35. if err != nil {
  36. return nil, err
  37. }
  38. err = collection.EnsureIndex(mgo.Index{
  39. Key: []string{"$text:description"},
  40. })
  41. if err != nil {
  42. return nil, err
  43. }
  44. return &characterRepository{
  45. characters: collection,
  46. cidCounter: newCounter(db, "auto_increment", "Character"),
  47. }, nil
  48. }
  49. func (r *characterRepository) Find(ctx context.Context, id string) (*models.Character, error) {
  50. character := new(models.Character)
  51. err := r.characters.FindId(id).One(character)
  52. if err != nil {
  53. return nil, err
  54. }
  55. return character, nil
  56. }
  57. func (r *characterRepository) FindNick(ctx context.Context, nick string) (*models.Character, error) {
  58. character := new(models.Character)
  59. err := r.characters.Find(bson.M{"nick": nick}).One(character)
  60. if err != nil {
  61. return nil, err
  62. }
  63. return character, nil
  64. }
  65. func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
  66. query := bson.M{}
  67. if filter.Author != nil {
  68. query["author"] = *filter.Author
  69. }
  70. if len(filter.IDs) > 0 {
  71. query["_id"] = bson.M{"$in": filter.IDs}
  72. }
  73. if len(filter.Nicks) > 0 {
  74. query["nicks"] = bson.M{"$in": filter.Nicks}
  75. }
  76. if len(filter.Names) > 0 {
  77. query["$or"] = []bson.M{
  78. {"name": bson.M{"$in": filter.Names}},
  79. {"shortName": bson.M{"$in": filter.Names}},
  80. }
  81. }
  82. if filter.Search != nil {
  83. query["$text"] = bson.M{"$search": *filter.Search}
  84. }
  85. characters := make([]*models.Character, 0, 32)
  86. err := r.characters.Find(query).Limit(filter.Limit).All(&characters)
  87. if err != nil {
  88. if err == mgo.ErrNotFound {
  89. return characters, nil
  90. }
  91. return nil, err
  92. }
  93. sort.Slice(characters, func(i, j int) bool {
  94. ni, _ := strconv.Atoi(characters[i].ID[1:])
  95. nj, _ := strconv.Atoi(characters[j].ID[1:])
  96. return ni < nj
  97. })
  98. return characters, nil
  99. }
  100. func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
  101. nextId, err := r.cidCounter.Increment(1)
  102. if err != nil {
  103. return nil, err
  104. }
  105. character.ID = "C" + strconv.Itoa(nextId)
  106. err = r.characters.Insert(&character)
  107. if err != nil {
  108. return nil, err
  109. }
  110. return &character, nil
  111. }
  112. func (r *characterRepository) Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) {
  113. updateBson := bson.M{}
  114. if update.Name != nil {
  115. updateBson["name"] = *update.Name
  116. character.Name = *update.Name
  117. }
  118. if update.ShortName != nil {
  119. updateBson["shortName"] = *update.ShortName
  120. character.ShortName = *update.ShortName
  121. }
  122. if update.Description != nil {
  123. updateBson["description"] = *update.Description
  124. character.Description = *update.Description
  125. }
  126. err := r.characters.UpdateId(character.ID, bson.M{"$set": updateBson})
  127. if err != nil {
  128. return nil, err
  129. }
  130. return &character, nil
  131. }
  132. func (r *characterRepository) AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
  133. if character.HasNick(nick) {
  134. return nil, errors.New("nick already exist")
  135. }
  136. match := bson.M{
  137. "_id": character.ID,
  138. "nicks": bson.M{"$ne": nick},
  139. }
  140. err := r.characters.Update(match, bson.M{"$push": bson.M{"nicks": nick}})
  141. if err == mgo.ErrNotFound {
  142. return nil, repositories.ErrNotFound
  143. } else if err != nil {
  144. return nil, err
  145. }
  146. newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1)
  147. copy(newNicks, character.Nicks)
  148. character.Nicks = append(newNicks, nick)
  149. return &character, nil
  150. }
  151. func (r *characterRepository) RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
  152. if !character.HasNick(nick) {
  153. return nil, errors.New("nick does not exist")
  154. }
  155. match := bson.M{
  156. "_id": character.ID,
  157. "nicks": nick,
  158. }
  159. err := r.characters.Update(match, bson.M{"$pull": bson.M{"nicks": nick}})
  160. if err == mgo.ErrNotFound {
  161. return nil, repositories.ErrNotFound
  162. } else if mErr, ok := err.(*mgo.LastError); ok && mErr.Code == 11000 {
  163. return nil, errors.New("The nick belongs to another character already")
  164. } else if err != nil {
  165. return nil, err
  166. }
  167. newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1)
  168. copy(newNicks, character.Nicks)
  169. for i := range newNicks {
  170. if newNicks[i] == nick {
  171. newNicks = append(newNicks[:i], newNicks[i+1:]...)
  172. break
  173. }
  174. }
  175. character.Nicks = newNicks
  176. return &character, nil
  177. }
  178. func (r *characterRepository) Delete(ctx context.Context, character models.Character) error {
  179. return r.characters.RemoveId(character.ID)
  180. }