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.
262 lines
6.1 KiB
262 lines
6.1 KiB
package mongodb
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"github.com/globalsign/mgo"
|
|
"github.com/globalsign/mgo/bson"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"git.aiterp.net/rpdata/api/models"
|
|
"git.aiterp.net/rpdata/api/repositories"
|
|
)
|
|
|
|
type characterRepository struct {
|
|
characters *mgo.Collection
|
|
cidCounter *counter
|
|
restoreIDs bool
|
|
}
|
|
|
|
func newCharacterRepository(db *mgo.Database, restoreIDs bool) (repositories.CharacterRepository, error) {
|
|
collection := db.C("common.characters")
|
|
|
|
err := collection.EnsureIndexKey("name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = collection.EnsureIndexKey("shortName")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = collection.EnsureIndexKey("author")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = collection.EnsureIndex(mgo.Index{
|
|
Key: []string{"nicks"},
|
|
Unique: true,
|
|
DropDups: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = collection.EnsureIndex(mgo.Index{
|
|
Key: []string{"$text:description"},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &characterRepository{
|
|
restoreIDs: restoreIDs,
|
|
characters: collection,
|
|
cidCounter: newCounter(db, "auto_increment", "Character"),
|
|
}, nil
|
|
}
|
|
|
|
func (r *characterRepository) Find(ctx context.Context, id string) (*models.Character, error) {
|
|
character := new(models.Character)
|
|
err := r.characters.FindId(id).One(character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (r *characterRepository) FindNick(ctx context.Context, nick string) (*models.Character, error) {
|
|
character := new(models.Character)
|
|
err := r.characters.Find(bson.M{"nick": nick}).One(character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (r *characterRepository) FindName(ctx context.Context, name string) (*models.Character, error) {
|
|
query := bson.M{
|
|
"$or": []bson.M{
|
|
{"shortName": name},
|
|
{"name": name},
|
|
},
|
|
}
|
|
|
|
// Look for all characters matching query
|
|
characters := make([]*models.Character, 0, 8)
|
|
err := r.characters.Find(query).All(&characters)
|
|
if err != nil {
|
|
if err == mgo.ErrNotFound {
|
|
return nil, repositories.ErrNotFound
|
|
}
|
|
|
|
return nil, err
|
|
} else if len(characters) == 0 {
|
|
return nil, repositories.ErrNotFound
|
|
}
|
|
|
|
// Prioritize exact match
|
|
for _, character := range characters {
|
|
if character.Name == name {
|
|
return character, nil
|
|
}
|
|
}
|
|
|
|
return characters[0], nil
|
|
}
|
|
|
|
func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
|
|
query := bson.M{}
|
|
if filter.Author != nil {
|
|
query["author"] = *filter.Author
|
|
}
|
|
if len(filter.IDs) > 0 {
|
|
query["_id"] = bson.M{"$in": filter.IDs}
|
|
}
|
|
if len(filter.Nicks) > 0 {
|
|
query["nicks"] = bson.M{"$in": filter.Nicks}
|
|
}
|
|
if len(filter.Names) > 0 {
|
|
query["$or"] = []bson.M{
|
|
{"name": bson.M{"$in": filter.Names}},
|
|
{"shortName": bson.M{"$in": filter.Names}},
|
|
}
|
|
}
|
|
if filter.Search != nil {
|
|
query["$text"] = bson.M{"$search": *filter.Search}
|
|
}
|
|
|
|
characters := make([]*models.Character, 0, 32)
|
|
err := r.characters.Find(query).Limit(filter.Limit).All(&characters)
|
|
if err != nil {
|
|
if err == mgo.ErrNotFound {
|
|
return characters, nil
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
sort.Slice(characters, func(i, j int) bool {
|
|
ni, _ := strconv.Atoi(characters[i].ID[1:])
|
|
nj, _ := strconv.Atoi(characters[j].ID[1:])
|
|
|
|
return ni < nj
|
|
})
|
|
|
|
return characters, nil
|
|
}
|
|
|
|
func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
|
|
if !r.restoreIDs {
|
|
nextId, err := r.cidCounter.Increment(1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
character.ID = "C" + strconv.Itoa(nextId)
|
|
} else {
|
|
n, err := strconv.Atoi(character.ID[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = r.cidCounter.Bump(n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
err := r.characters.Insert(&character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &character, nil
|
|
}
|
|
|
|
func (r *characterRepository) Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) {
|
|
updateBson := bson.M{}
|
|
if update.Name != nil {
|
|
updateBson["name"] = *update.Name
|
|
character.Name = *update.Name
|
|
}
|
|
if update.ShortName != nil {
|
|
updateBson["shortName"] = *update.ShortName
|
|
character.ShortName = *update.ShortName
|
|
}
|
|
if update.Description != nil {
|
|
updateBson["description"] = *update.Description
|
|
character.Description = *update.Description
|
|
}
|
|
|
|
err := r.characters.UpdateId(character.ID, bson.M{"$set": updateBson})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &character, nil
|
|
}
|
|
|
|
func (r *characterRepository) AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
|
|
if character.HasNick(nick) {
|
|
return nil, errors.New("nick already exist")
|
|
}
|
|
|
|
match := bson.M{
|
|
"_id": character.ID,
|
|
"nicks": bson.M{"$ne": nick},
|
|
}
|
|
|
|
err := r.characters.Update(match, bson.M{"$push": bson.M{"nicks": nick}})
|
|
if err == mgo.ErrNotFound {
|
|
return nil, repositories.ErrNotFound
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1)
|
|
copy(newNicks, character.Nicks)
|
|
character.Nicks = append(newNicks, nick)
|
|
|
|
return &character, nil
|
|
}
|
|
|
|
func (r *characterRepository) RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
|
|
if !character.HasNick(nick) {
|
|
return nil, errors.New("nick does not exist")
|
|
}
|
|
|
|
match := bson.M{
|
|
"_id": character.ID,
|
|
"nicks": nick,
|
|
}
|
|
|
|
err := r.characters.Update(match, bson.M{"$pull": bson.M{"nicks": nick}})
|
|
if err == mgo.ErrNotFound {
|
|
return nil, repositories.ErrNotFound
|
|
} else if mErr, ok := err.(*mgo.LastError); ok && mErr.Code == 11000 {
|
|
return nil, errors.New("The nick belongs to another character already")
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1)
|
|
copy(newNicks, character.Nicks)
|
|
for i := range newNicks {
|
|
if newNicks[i] == nick {
|
|
newNicks = append(newNicks[:i], newNicks[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
character.Nicks = newNicks
|
|
|
|
return &character, nil
|
|
}
|
|
|
|
func (r *characterRepository) Delete(ctx context.Context, character models.Character) error {
|
|
return r.characters.RemoveId(character.ID)
|
|
}
|