@@ -0,0 +1,217 @@
|
||||
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
|
||||
}
|
||||
|
||||
func newCharacterRepository(db *mgo.Database) (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{
|
||||
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) 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).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) {
|
||||
nextId, err := r.cidCounter.Increment(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
character.ID = "C" + strconv.Itoa(nextId)
|
||||
|
||||
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)
|
||||
}
|
||||
+52
-1
@@ -2,6 +2,7 @@ package mongodb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/globalsign/mgo/bson"
|
||||
"time"
|
||||
|
||||
"git.aiterp.net/rpdata/api/internal/config"
|
||||
@@ -31,8 +32,15 @@ func Init(cfg config.Database) (bundle *repositories.Bundle, closeFn func() erro
|
||||
|
||||
db := session.DB(cfg.Db)
|
||||
|
||||
characters, err := newCharacterRepository(db)
|
||||
if err != nil {
|
||||
session.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bundle = &repositories.Bundle{
|
||||
Tags: newTagRepository(db),
|
||||
Characters: characters,
|
||||
Tags: newTagRepository(db),
|
||||
}
|
||||
|
||||
closeFn = func() error {
|
||||
@@ -42,3 +50,46 @@ func Init(cfg config.Database) (bundle *repositories.Bundle, closeFn func() erro
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type counter struct {
|
||||
coll *mgo.Collection
|
||||
category string
|
||||
name string
|
||||
}
|
||||
|
||||
func newCounter(db *mgo.Database, category, name string) *counter {
|
||||
return &counter{
|
||||
coll: db.C("core.counters"),
|
||||
category: category,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *counter) WithName(name string) *counter {
|
||||
return &counter{
|
||||
coll: c.coll,
|
||||
category: c.category,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *counter) Increment(amount int) (int, error) {
|
||||
type counterDoc struct {
|
||||
ID string `bson:"_id"`
|
||||
Value int `bson:"value"`
|
||||
}
|
||||
|
||||
id := c.category + "." + c.name
|
||||
doc := counterDoc{}
|
||||
|
||||
_, err := c.coll.Find(bson.M{"_id": id}).Apply(mgo.Change{
|
||||
Update: bson.M{"$inc": bson.M{"value": amount}},
|
||||
Upsert: true,
|
||||
ReturnNew: true,
|
||||
}, &doc)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return doc.Value, nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func newTagRepository(db *mgo.Database) repositories.TagRepository {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *tagRepository) Find(ctx context.Context, kind, name string) (*models.Tag, error) {
|
||||
func (r *tagRepository) Find(ctx context.Context, kind models.TagKind, name string) (*models.Tag, error) {
|
||||
tags := make([]*models.Tag, 0, 1)
|
||||
err := r.stories.Find(bson.M{"listed": true, "tags": bson.M{"kind": kind, "name": name}}).Distinct("tag", &tags)
|
||||
if err != nil {
|
||||
@@ -30,7 +30,13 @@ func (r *tagRepository) Find(ctx context.Context, kind, name string) (*models.Ta
|
||||
return nil, repositories.ErrNotFound
|
||||
}
|
||||
|
||||
return tags[0], nil
|
||||
for _, tag := range tags {
|
||||
if tag.Kind == kind && tag.Name == name {
|
||||
return tag, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, repositories.ErrNotFound
|
||||
}
|
||||
|
||||
func (r *tagRepository) List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) {
|
||||
|
||||
Reference in New Issue
Block a user