123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449 |
- package dao
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "sort"
- "strings"
- "time"
- "github.com/willie68/schematic-service-go/config"
- slicesutils "github.com/willie68/schematic-service-go/internal"
- "github.com/willie68/schematic-service-go/model"
- "go.mongodb.org/mongo-driver/bson"
- "go.mongodb.org/mongo-driver/bson/primitive"
- "go.mongodb.org/mongo-driver/mongo"
- "go.mongodb.org/mongo-driver/mongo/gridfs"
- "go.mongodb.org/mongo-driver/mongo/options"
- )
- // time to reload all users
- const userReloadPeriod = 1 * time.Hour
- const timeout = 1 * time.Minute
- const attachmentsCollectionName = "attachments"
- const schematicsCollectionName = "schematics"
- const tagsCollectionName = "tags"
- const manufacturersCollectionName = "manufacturers"
- const usersCollectionName = "users"
- const effectsCollectionName = "effects"
- const effectTypesCollectionName = "effectTypes"
- // MongoDAO a mongodb based dao
- type MongoDAO struct {
- initialised bool
- client *mongo.Client
- mongoConfig config.MongoDB
- bucket gridfs.Bucket
- database mongo.Database
- tags model.Tags
- manufacturers model.Manufacturers
- users map[string]string
- ticker time.Ticker
- done chan bool
- }
- // InitDAO initialise the mongodb connection, build up all collections and indexes
- func (m *MongoDAO) InitDAO(MongoConfig config.MongoDB) {
- m.initialised = false
- m.mongoConfig = MongoConfig
- // uri := fmt.Sprintf("mongodb://%s:%s@%s:%d", mongoConfig.Username, mongoConfig.Password, mongoConfig.Host, mongoConfig.Port)
- uri := fmt.Sprintf("mongodb://%s:%d", m.mongoConfig.Host, m.mongoConfig.Port)
- clientOptions := options.Client()
- clientOptions.ApplyURI(uri)
- clientOptions.Auth = &options.Credential{Username: m.mongoConfig.Username, Password: m.mongoConfig.Password, AuthSource: m.mongoConfig.AuthDB}
- var err error
- m.client, err = mongo.NewClient(clientOptions)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- }
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
- err = m.client.Connect(ctx)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- }
- m.database = *m.client.Database(m.mongoConfig.Database)
- myBucket, err := gridfs.NewBucket(&m.database, options.GridFSBucket().SetName(attachmentsCollectionName))
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- }
- m.bucket = *myBucket
- m.initIndexSchematics()
- m.initIndexTags()
- m.initIndexManufacturers()
- m.initIndexEffectTypes()
- m.initIndexEffects()
- m.tags = model.NewTags()
- m.manufacturers = model.NewManufacturers()
- m.users = make(map[string]string)
- m.initTags()
- m.initManufacturers()
- m.initUsers()
- m.initialised = true
- }
- func (m *MongoDAO) initIndexSchematics() {
- collection := m.database.Collection(schematicsCollectionName)
- indexView := collection.Indexes()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := indexView.List(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- myIndexes := make([]string, 0)
- for cursor.Next(ctx) {
- var index bson.M
- if err = cursor.Decode(&index); err != nil {
- log.Fatal(err)
- }
- myIndexes = append(myIndexes, index["name"].(string))
- }
- if !slicesutils.Contains(myIndexes, "manufacturer") {
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- models := []mongo.IndexModel{
- {
- Keys: bson.D{{"manufacturer", 1}},
- Options: options.Index().SetName("manufacturer").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"model", 1}},
- Options: options.Index().SetName("model").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"tags", 1}},
- Options: options.Index().SetName("tags").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"subtitle", 1}},
- Options: options.Index().SetName("subtitle").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"manufacturer", "text"}, {"model", "text"}, {"tags", "text"}, {"subtitle", "text"}, {"description", "text"}, {"owner", "text"}},
- Options: options.Index().SetName("$text"),
- },
- }
- // Specify the MaxTime option to limit the amount of time the operation can run on the server
- opts := options.CreateIndexes().SetMaxTime(2 * time.Second)
- names, err := indexView.CreateMany(context.TODO(), models, opts)
- if err != nil {
- log.Fatal(err)
- }
- log.Print("create indexes:")
- for _, name := range names {
- log.Println(name)
- }
- }
- }
- func (m *MongoDAO) initIndexTags() {
- collection := m.database.Collection(tagsCollectionName)
- indexView := collection.Indexes()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := indexView.List(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- myIndexes := make([]string, 0)
- for cursor.Next(ctx) {
- var index bson.M
- if err = cursor.Decode(&index); err != nil {
- log.Fatal(err)
- }
- myIndexes = append(myIndexes, index["name"].(string))
- }
- if !slicesutils.Contains(myIndexes, "name") {
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- models := []mongo.IndexModel{
- {
- Keys: bson.D{{"name", 1}},
- Options: options.Index().SetUnique(true).SetName("name").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- }
- // Specify the MaxTime option to limit the amount of time the operation can run on the server
- opts := options.CreateIndexes().SetMaxTime(2 * time.Second)
- names, err := indexView.CreateMany(context.TODO(), models, opts)
- if err != nil {
- log.Fatal(err)
- }
- log.Print("create indexes:")
- for _, name := range names {
- log.Println(name)
- }
- }
- }
- func (m *MongoDAO) initIndexManufacturers() {
- collection := m.database.Collection(manufacturersCollectionName)
- indexView := collection.Indexes()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := indexView.List(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- myIndexes := make([]string, 0)
- for cursor.Next(ctx) {
- var index bson.M
- if err = cursor.Decode(&index); err != nil {
- log.Fatal(err)
- }
- myIndexes = append(myIndexes, index["name"].(string))
- }
- if !slicesutils.Contains(myIndexes, "name") {
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- models := []mongo.IndexModel{
- {
- Keys: bson.D{{"name", 1}},
- Options: options.Index().SetUnique(true).SetName("name").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- }
- // Specify the MaxTime option to limit the amount of time the operation can run on the server
- opts := options.CreateIndexes().SetMaxTime(2 * time.Second)
- names, err := indexView.CreateMany(context.TODO(), models, opts)
- if err != nil {
- log.Fatal(err)
- }
- log.Print("create indexes:")
- for _, name := range names {
- log.Println(name)
- }
- }
- }
- func (m *MongoDAO) initIndexEffects() {
- collection := m.database.Collection(effectsCollectionName)
- indexView := collection.Indexes()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := indexView.List(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- myIndexes := make([]string, 0)
- for cursor.Next(ctx) {
- var index bson.M
- if err = cursor.Decode(&index); err != nil {
- log.Fatal(err)
- }
- myIndexes = append(myIndexes, index["name"].(string))
- }
- if !slicesutils.Contains(myIndexes, "effectType") {
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- models := []mongo.IndexModel{
- {
- Keys: bson.D{{"effectType", 1}},
- Options: options.Index().SetName("effectType").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"manufacturer", 1}},
- Options: options.Index().SetName("manufacturer").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"model", 1}},
- Options: options.Index().SetName("model").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"tags", 1}},
- Options: options.Index().SetName("tags").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"comment", 1}},
- Options: options.Index().SetName("comment").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- {
- Keys: bson.D{{"effectType", "text"}, {"manufacturer", "text"}, {"model", "text"}, {"tags", "text"}, {"comment", "text"}, {"connector", "text"}, {"current", "text"}, {"voltage", "text"}},
- Options: options.Index().SetName("$text"),
- },
- }
- // Specify the MaxTime option to limit the amount of time the operation can run on the server
- opts := options.CreateIndexes().SetMaxTime(2 * time.Second)
- names, err := indexView.CreateMany(context.TODO(), models, opts)
- if err != nil {
- log.Fatal(err)
- }
- log.Print("create indexes:")
- for _, name := range names {
- log.Println(name)
- }
- }
- }
- func (m *MongoDAO) initIndexEffectTypes() {
- collection := m.database.Collection(effectTypesCollectionName)
- indexView := collection.Indexes()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := indexView.List(ctx)
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- myIndexes := make([]string, 0)
- for cursor.Next(ctx) {
- var index bson.M
- if err = cursor.Decode(&index); err != nil {
- log.Fatal(err)
- }
- myIndexes = append(myIndexes, index["name"].(string))
- }
- if !slicesutils.Contains(myIndexes, "typeName") {
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- models := []mongo.IndexModel{
- {
- Keys: bson.D{{"typeName", 1}},
- Options: options.Index().SetName("typeName").SetCollation(&options.Collation{Locale: "en", Strength: 2}),
- },
- }
- // Specify the MaxTime option to limit the amount of time the operation can run on the server
- opts := options.CreateIndexes().SetMaxTime(2 * time.Second)
- names, err := indexView.CreateMany(context.TODO(), models, opts)
- if err != nil {
- log.Fatal(err)
- }
- log.Print("create indexes:")
- for _, name := range names {
- log.Println(name)
- }
- }
- }
- func (m *MongoDAO) initTags() {
- m.reloadTags()
- }
- func (m *MongoDAO) initManufacturers() {
- m.reloadManufacturers()
- }
- func (m *MongoDAO) initUsers() {
- m.reloadUsers()
- go func() {
- background := time.NewTicker(userReloadPeriod)
- for _ = range background.C {
- m.reloadUsers()
- m.reloadTags()
- m.reloadManufacturers()
- }
- }()
- }
- func (m *MongoDAO) reloadUsers() {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- usersCollection := m.database.Collection(usersCollectionName)
- cursor, err := usersCollection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- localUsers := make(map[string]string)
- for cursor.Next(ctx) {
- var user bson.M
- if err = cursor.Decode(&user); err != nil {
- log.Fatal(err)
- } else {
- username := strings.ToLower(user["name"].(string))
- password := user["password"].(string)
- localUsers[username] = BuildPasswordHash(password)
- }
- }
- m.users = localUsers
- if len(m.users) == 0 {
- admin := model.User{
- Name: "w.klaas@gmx.de",
- Password: "akteon0000",
- Admin: true,
- }
- m.AddUser(admin)
- guest := model.User{
- Name: "gast",
- Password: "gast1234",
- Admin: false,
- Guest: true,
- }
- m.AddUser(guest)
- }
- }
- func (m *MongoDAO) reloadTags() {
- myTags := model.NewTags()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- tagsCollection := m.database.Collection(tagsCollectionName)
- cursor, err := tagsCollection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- tagList := make([]string, 0)
- for cursor.Next(ctx) {
- var tag bson.M
- if err = cursor.Decode(&tag); err != nil {
- log.Fatal(err)
- } else {
- tagList = append(tagList, tag["name"].(string))
- }
- }
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- for _, tagName := range tagList {
- queryDoc := bson.M{
- "tags": tagName,
- }
- n, err := schematicCollection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Println(err)
- }
- //log.Printf("tag: %s has %d schematics.", tagName, n)
- myTags.Add(tagName, int(n))
- }
- m.tags = myTags
- }
- func (m *MongoDAO) reloadManufacturers() {
- myManufacturer := model.NewManufacturers()
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- manufacturersCollection := m.database.Collection(manufacturersCollectionName)
- cursor, err := manufacturersCollection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- manuList := make([]string, 0)
- for cursor.Next(ctx) {
- var manufacturer bson.M
- if err = cursor.Decode(&manufacturer); err != nil {
- log.Fatal(err)
- } else {
- manuList = append(manuList, manufacturer["name"].(string))
- }
- }
- ctx, _ = context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- for _, manuName := range manuList {
- queryDoc := bson.M{
- "manufacturer": manuName,
- }
- n, err := schematicCollection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Println(err)
- }
- //log.Printf("manufacturer: %s has %d schematics.", manuName, n)
- myManufacturer.Add(manuName, int(n))
- }
- m.manufacturers = myManufacturer
- }
- // AddFile adding a file to the storage, stream like
- func (m *MongoDAO) AddFile(filename string, reader io.Reader) (string, error) {
- uploadOpts := options.GridFSUpload().SetMetadata(bson.D{{"tag", "tag"}})
- fileID, err := m.bucket.UploadFromStream(filename, reader, uploadOpts)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- log.Printf("Write file to DB was successful. File id: %s \n", fileID)
- id := fileID.Hex()
- return id, nil
- }
- // CreateSchematic creating a new schematic in the database
- func (m *MongoDAO) CreateSchematic(schematic model.Schematic) (string, error) {
- for _, tag := range schematic.Tags {
- if !m.tags.Contains(tag) {
- m.CreateTag(tag)
- }
- }
- if !m.manufacturers.Contains(schematic.Manufacturer) {
- m.CreateManufacturer(schematic.Manufacturer)
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(schematicsCollectionName)
- result, err := collection.InsertOne(ctx, schematic)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- filter := bson.M{"_id": result.InsertedID}
- err = collection.FindOne(ctx, filter).Decode(&schematic)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- switch v := result.InsertedID.(type) {
- case primitive.ObjectID:
- return v.Hex(), nil
- }
- return "", nil
- }
- // UpdateSchematic creating a new schematic in the database
- func (m *MongoDAO) UpdateSchematic(schematic model.Schematic) (string, error) {
- for _, tag := range schematic.Tags {
- if !m.tags.Contains(tag) {
- m.CreateTag(tag)
- }
- }
- if !m.manufacturers.Contains(schematic.Manufacturer) {
- m.CreateManufacturer(schematic.Manufacturer)
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(schematicsCollectionName)
- filter := bson.M{"_id": schematic.ID}
- updateDoc := bson.D{{"$set", schematic}}
- result, err := collection.UpdateOne(ctx, filter, updateDoc)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- if result.ModifiedCount != 1 {
- return "", errors.New("can't update document.")
- }
- err = collection.FindOne(ctx, filter).Decode(&schematic)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- return schematic.ID.Hex(), nil
- }
- // GetSchematic getting a sdingle schematic
- func (m *MongoDAO) GetSchematic(schematicID string) (model.Schematic, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- objectID, _ := primitive.ObjectIDFromHex(schematicID)
- result := schematicCollection.FindOne(ctx, bson.M{"_id": objectID})
- err := result.Err()
- if err == mongo.ErrNoDocuments {
- log.Print(err)
- return model.Schematic{}, ErrNoDocument
- }
- if err != nil {
- log.Print(err)
- return model.Schematic{}, err
- }
- var schematic model.Schematic
- if err := result.Decode(&schematic); err != nil {
- log.Print(err)
- return model.Schematic{}, err
- } else {
- return schematic, nil
- }
- }
- // DeleteSchematic getting a sdingle schematic
- func (m *MongoDAO) DeleteSchematic(schematicID string) error {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- objectID, _ := primitive.ObjectIDFromHex(schematicID)
- result, err := schematicCollection.DeleteOne(ctx, bson.M{"_id": objectID})
- if err != nil {
- log.Print(err)
- return err
- } else {
- if result.DeletedCount > 0 {
- return nil
- }
- return ErrNoDocument
- }
- }
- //GetFile getting a single from the database with the id
- func (m *MongoDAO) GetFilename(fileid string) (string, error) {
- objectID, err := primitive.ObjectIDFromHex(fileid)
- if err != nil {
- log.Print(err)
- return "", err
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- cursor, err := m.bucket.Find(bson.M{"_id": objectID})
- if err != nil {
- log.Print(err)
- return "", err
- }
- defer cursor.Close(ctx)
- cursor.Next(ctx)
- var file bson.M
- var filename string
- if err = cursor.Decode(&file); err != nil {
- log.Print(err)
- return "", err
- } else {
- filename = file["filename"].(string)
- }
- return filename, nil
- }
- //GetFile getting a single from the database with the id
- func (m *MongoDAO) GetFile(fileid string, stream io.Writer) error {
- objectID, err := primitive.ObjectIDFromHex(fileid)
- if err != nil {
- log.Print(err)
- return err
- }
- _, err = m.bucket.DownloadToStream(objectID, stream)
- if err != nil {
- log.Print(err)
- return err
- }
- return nil
- }
- // GetSchematics getting a sdingle schematic
- func (m *MongoDAO) GetSchematics(query string, offset int, limit int, owner string) (int64, []model.Schematic, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- var queryM map[string]interface{}
- err := json.Unmarshal([]byte(query), &queryM)
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- queryDoc := bson.M{}
- for k, v := range queryM {
- if k == "$fulltext" {
- queryDoc["$text"] = bson.M{"$search": v}
- } else {
- switch v := v.(type) {
- // case float64:
- // case int:
- // case bool:
- case string:
- queryDoc[k] = bson.M{"$regex": v}
- }
- //queryDoc[k] = v
- }
- }
- data, _ := json.Marshal(queryDoc)
- log.Printf("mongoquery: %s\n", string(data))
- n, err := schematicCollection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- cursor, err := schematicCollection.Find(ctx, queryDoc, &options.FindOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- defer cursor.Close(ctx)
- schematics := make([]model.Schematic, 0)
- count := 0
- docs := 0
- for cursor.Next(ctx) {
- if count >= offset {
- if docs < limit {
- var schematic model.Schematic
- if err = cursor.Decode(&schematic); err != nil {
- log.Print(err)
- return 0, nil, err
- } else {
- if !schematic.PrivateFile || schematic.Owner == owner {
- schematics = append(schematics, schematic)
- docs++
- }
- }
- } else {
- break
- }
- }
- count++
- }
- return n, schematics, nil
- }
- // GetSchematicsCount getting a sdingle schematic
- func (m *MongoDAO) GetSchematicsCount(query string, owner string) (int64, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- schematicCollection := m.database.Collection(schematicsCollectionName)
- queryDoc := bson.M{}
- if query != "" {
- var queryM map[string]interface{}
- err := json.Unmarshal([]byte(query), &queryM)
- if err != nil {
- log.Print(err)
- return 0, err
- }
- for k, v := range queryM {
- if k == "$fulltext" {
- queryDoc["$text"] = bson.M{"$search": v}
- } else {
- switch v := v.(type) {
- // case float64:
- // case int:
- // case bool:
- case string:
- queryDoc[k] = bson.M{"$regex": v}
- }
- //queryDoc[k] = v
- }
- }
- data, _ := json.Marshal(queryDoc)
- log.Printf("mongoquery: %s\n", string(data))
- }
- n, err := schematicCollection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Print(err)
- return 0, err
- }
- return n, nil
- }
- // CreateTag create a new tag in the storage
- func (m *MongoDAO) CreateTag(tag string) error {
- tag = strings.ToLower(tag)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(tagsCollectionName)
- tagModel := bson.M{"name": tag}
- _, err := collection.InsertOne(ctx, tagModel)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return err
- }
- m.tags.Add(tag, 1)
- return nil
- }
- //GetTags getting a list of all tags
- func (m *MongoDAO) GetTags() []model.Tag {
- sort.Slice(m.tags.List, func(i, j int) bool { return m.tags.List[i].Name < m.tags.List[j].Name })
- return m.tags.List
- }
- // GetTagsCount getting the count of all tags
- func (m *MongoDAO) GetTagsCount() int {
- return len(m.tags.List)
- }
- // CreateManufacturer create a new manufacturer in the storage
- func (m *MongoDAO) CreateManufacturer(manufacturer string) error {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(manufacturersCollectionName)
- manufacturerModel := bson.M{"name": manufacturer}
- _, err := collection.InsertOne(ctx, manufacturerModel)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return err
- }
- m.manufacturers.Add(manufacturer, 1)
- return nil
- }
- // GetManufacturers getting a list of all manufacturers
- func (m *MongoDAO) GetManufacturers() []model.Manufacturer {
- sort.Slice(m.manufacturers.List, func(i, j int) bool { return m.manufacturers.List[i].Name < m.manufacturers.List[j].Name })
- return m.manufacturers.List
- }
- // GetManufacturersCount getting the count of all manufacturers
- func (m *MongoDAO) GetManufacturersCount() int {
- return len(m.manufacturers.List)
- }
- //CreateEffectType cerating a new effect type
- func (m *MongoDAO) CreateEffectType(effectType model.EffectType) (string, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectTypesCollectionName)
- result, err := collection.InsertOne(ctx, effectType)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- filter := bson.M{"_id": result.InsertedID}
- err = collection.FindOne(ctx, filter).Decode(&effectType)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- switch v := result.InsertedID.(type) {
- case primitive.ObjectID:
- return v.Hex(), nil
- }
- return "", nil
- }
- // GetEffectTypes getting a sdingle schematic
- func (m *MongoDAO) GetEffectTypes() ([]model.EffectType, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectTypesCollectionName)
- findOptions := options.Find()
- // Sort by `price` field descending
- findOptions.SetSort(bson.D{{"typeName", 1}})
- queryDoc := bson.M{}
- cursor, err := collection.Find(ctx, queryDoc, &options.FindOptions{Collation: &options.Collation{Locale: "en", Strength: 2}, Sort: bson.D{{"typeName", 1}}})
- if err != nil {
- log.Print(err)
- return nil, err
- }
- defer cursor.Close(ctx)
- effectTypes := make([]model.EffectType, 0)
- for cursor.Next(ctx) {
- var effectType model.EffectType
- if err = cursor.Decode(&effectType); err != nil {
- log.Print(err)
- return nil, err
- } else {
- effectTypes = append(effectTypes, effectType)
- }
- }
- return effectTypes, nil
- }
- //CreateEffect cerating a new effect type
- func (m *MongoDAO) CreateEffect(effect model.Effect) (string, error) {
- for _, tag := range effect.Tags {
- if !m.tags.Contains(tag) {
- m.CreateTag(tag)
- }
- }
- if !m.manufacturers.Contains(effect.Manufacturer) {
- m.CreateManufacturer(effect.Manufacturer)
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectsCollectionName)
- result, err := collection.InsertOne(ctx, effect)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- filter := bson.M{"_id": result.InsertedID}
- err = collection.FindOne(ctx, filter).Decode(&effect)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return "", err
- }
- switch v := result.InsertedID.(type) {
- case primitive.ObjectID:
- return v.Hex(), nil
- }
- return "", nil
- }
- //GetEffectsCount getting the count of effects regarding to the query
- func (m *MongoDAO) GetEffectsCount(query string) (int, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectsCollectionName)
- queryDoc := bson.M{}
- if query != "" {
- var queryM map[string]interface{}
- err := json.Unmarshal([]byte(query), &queryM)
- if err != nil {
- log.Print(err)
- return 0, err
- }
- for k, v := range queryM {
- if k == "$fulltext" {
- queryDoc["$text"] = bson.M{"$search": v}
- } else {
- switch v := v.(type) {
- // case float64:
- // case int:
- // case bool:
- case string:
- queryDoc[k] = bson.M{"$regex": v}
- }
- //queryDoc[k] = v
- }
- }
- data, _ := json.Marshal(queryDoc)
- log.Printf("mongoquery: %s\n", string(data))
- }
- n, err := collection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Print(err)
- return 0, err
- }
- return int(n), nil
- }
- //GetEffects getting a list of effects regarding to the query
- func (m *MongoDAO) GetEffects(query string, offset int, limit int) (int, []model.Effect, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectsCollectionName)
- queryDoc := bson.M{}
- if query != "" {
- var queryM map[string]interface{}
- err := json.Unmarshal([]byte(query), &queryM)
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- for k, v := range queryM {
- if k == "$fulltext" {
- queryDoc["$text"] = bson.M{"$search": v}
- } else {
- switch v := v.(type) {
- // case float64:
- // case int:
- // case bool:
- case string:
- queryDoc[k] = bson.M{"$regex": v}
- }
- //queryDoc[k] = v
- }
- }
- data, _ := json.Marshal(queryDoc)
- log.Printf("mongoquery: %s\n", string(data))
- }
- n, err := collection.CountDocuments(ctx, queryDoc, &options.CountOptions{Collation: &options.Collation{Locale: "en", Strength: 2}})
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- cursor, err := collection.Find(ctx, queryDoc, &options.FindOptions{Collation: &options.Collation{Locale: "en", Strength: 2}, Sort: bson.D{{"manufacturer", 1}, {"model", 1}}})
- if err != nil {
- log.Print(err)
- return 0, nil, err
- }
- defer cursor.Close(ctx)
- effects := make([]model.Effect, 0)
- count := 0
- docs := 0
- for cursor.Next(ctx) {
- if count >= offset {
- if docs < limit {
- var effect model.Effect
- if err = cursor.Decode(&effect); err != nil {
- log.Print(err)
- return 0, nil, err
- } else {
- effects = append(effects, effect)
- docs++
- }
- } else {
- break
- }
- }
- count++
- }
- return int(n), effects, nil
- }
- func (m *MongoDAO) GetEffect(effectID string) (model.Effect, error) {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectsCollectionName)
- objectID, _ := primitive.ObjectIDFromHex(effectID)
- result := collection.FindOne(ctx, bson.M{"_id": objectID})
- err := result.Err()
- if err == mongo.ErrNoDocuments {
- log.Print(err)
- return model.Effect{}, ErrNoDocument
- }
- if err != nil {
- log.Print(err)
- return model.Effect{}, err
- }
- var effect model.Effect
- if err := result.Decode(&effect); err != nil {
- log.Print(err)
- return model.Effect{}, err
- } else {
- return effect, nil
- }
- }
- // CheckUser checking username and password... returns true if the user is active and the password for this user is correct
- func (m *MongoDAO) CheckUser(username string, password string) bool {
- username = strings.ToLower(username)
- pwd, ok := m.users[username]
- if ok {
- if pwd == password {
- return true
- } else {
- user, ok := m.GetUser(username)
- if ok {
- if user.Password == password {
- return true
- }
- }
- }
- }
- if !ok {
- user, ok := m.GetUser(username)
- if ok {
- if user.Password == password {
- return true
- }
- }
- }
- return false
- }
- // GetUser getting the usermolde
- func (m *MongoDAO) GetUser(username string) (model.User, bool) {
- username = strings.ToLower(username)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- usersCollection := m.database.Collection(usersCollectionName)
- var user model.User
- filter := bson.M{"name": username}
- err := usersCollection.FindOne(ctx, filter).Decode(&user)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return model.User{}, false
- }
- password := user.Password
- hash := BuildPasswordHash(password)
- m.users[username] = hash
- return user, true
- }
- // DropAll dropping all data from the database
- func (m *MongoDAO) DropAll() {
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collectionNames, err := m.database.ListCollectionNames(ctx, bson.D{}, &options.ListCollectionsOptions{})
- if err != nil {
- log.Fatal(err)
- }
- for _, name := range collectionNames {
- if name != usersCollectionName {
- collection := m.database.Collection(name)
- err = collection.Drop(ctx)
- if err != nil {
- log.Fatal(err)
- }
- }
- }
- }
- // Stop stopping the mongodao
- func (m *MongoDAO) Stop() {
- m.ticker.Stop()
- m.done <- true
- }
- // AddUser adding a new user to the system
- func (m *MongoDAO) AddUser(user model.User) error {
- if user.Name == "" {
- return errors.New("username should not be empty")
- }
- user.Name = strings.ToLower(user.Name)
- _, ok := m.users[user.Name]
- if ok {
- return errors.New("username already exists")
- }
- user.Password = BuildPasswordHash(user.Password)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(usersCollectionName)
- _, err := collection.InsertOne(ctx, user)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return err
- }
- m.users[user.Name] = user.Password
- return nil
- }
- // DeleteUser deletes one user from the system
- func (m *MongoDAO) DeleteUser(username string) error {
- if username == "" {
- return errors.New("username should not be empty")
- }
- username = strings.ToLower(username)
- _, ok := m.users[username]
- if !ok {
- return errors.New("username not exists")
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(usersCollectionName)
- filter := bson.M{"name": username}
- _, err := collection.DeleteOne(ctx, filter)
- if err != nil {
- fmt.Printf("error: %s\n", err.Error())
- return err
- }
- delete(m.users, username)
- return nil
- }
- // ChangePWD changes the apssword of a single user
- func (m *MongoDAO) ChangePWD(username string, newpassword string, oldpassword string) error {
- if username == "" {
- return errors.New("username should not be empty")
- }
- username = strings.ToLower(username)
- pwd, ok := m.users[username]
- if !ok {
- return errors.New("username not registered")
- }
- newpassword = BuildPasswordHash(newpassword)
- oldpassword = BuildPasswordHash(oldpassword)
- if pwd != oldpassword {
- return errors.New("actual password incorrect")
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(usersCollectionName)
- filter := bson.M{"name": username}
- update := bson.D{{"$set", bson.D{{"password", newpassword}}}}
- result := collection.FindOneAndUpdate(ctx, filter, update)
- if result.Err() != nil {
- fmt.Printf("error: %s\n", result.Err().Error())
- return result.Err()
- }
- m.users[username] = newpassword
- return nil
- }
- //Backup pinging the mongoDao
- func (m *MongoDAO) Backup(path string) error {
- // writung users
- err := m.backupUsers(path)
- if err != nil {
- return err
- }
- // writing tags
- err = m.backupTags(path)
- if err != nil {
- return err
- }
- // writing manufacturers
- err = m.backupManufacturers(path)
- if err != nil {
- return err
- }
- // writing schematics
- err = m.backupSchematics(path)
- if err != nil {
- return err
- }
- // writing effect categories
- err = m.backupEffectTypes(path)
- if err != nil {
- return err
- }
- // writing effects
- err = m.backupEffects(path)
- if err != nil {
- return err
- }
- return err
- }
- func (m *MongoDAO) backupUsers(path string) error {
- path = path + "/users"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(usersCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup users: ")
- for cursor.Next(ctx) {
- var user model.User
- if err = cursor.Decode(&user); err != nil {
- log.Fatal(err)
- return err
- } else {
- user.Name = strings.ToLower(user.Name)
- user.Password = BuildPasswordHash(user.Password)
- data, err := json.Marshal(user)
- if err != nil {
- return err
- }
- filename := path + "/" + user.ID.Hex() + ".json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- func (m *MongoDAO) backupTags(path string) error {
- path = path + "/tags"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(tagsCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup tags: ")
- for cursor.Next(ctx) {
- var model model.Tag
- if err = cursor.Decode(&model); err != nil {
- log.Fatal(err)
- return err
- } else {
- data, err := json.Marshal(model)
- if err != nil {
- return err
- }
- filename := path + "/" + model.ID.Hex() + ".json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- func (m *MongoDAO) backupManufacturers(path string) error {
- path = path + "/manufacturers"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(manufacturersCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup manufacturers: ")
- for cursor.Next(ctx) {
- var model model.Manufacturer
- if err = cursor.Decode(&model); err != nil {
- log.Fatal(err)
- return err
- } else {
- data, err := json.Marshal(model)
- if err != nil {
- return err
- }
- filename := path + "/" + model.ID.Hex() + ".json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- func (m *MongoDAO) backupSchematics(path string) error {
- path = path + "/schematics"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(schematicsCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup schematics: ")
- for cursor.Next(ctx) {
- var model model.Schematic
- if err = cursor.Decode(&model); err != nil {
- log.Fatal(err)
- return err
- } else {
- folder := path + "/" + model.ID.Hex()
- os.MkdirAll(folder, os.ModePerm)
- for _, fileid := range model.Files {
- if fileid != "" {
- filename, err := m.GetFilename(fileid)
- if err != nil {
- return err
- }
- imagename := folder + "/" + filename
- file, err := os.Create(imagename)
- if err != nil {
- return err
- }
- defer file.Close()
- err = m.GetFile(fileid, file)
- if err != nil {
- return err
- }
- }
- }
- data, err := json.Marshal(model)
- if err != nil {
- return err
- }
- filename := folder + "/schematic.json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- func (m *MongoDAO) backupEffectTypes(path string) error {
- path = path + "/effecttypes"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectTypesCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup effecttypes: ")
- for cursor.Next(ctx) {
- var model model.EffectType
- if err = cursor.Decode(&model); err != nil {
- log.Fatal(err)
- return err
- } else {
- folder := path + "/" + model.ID.Hex()
- os.MkdirAll(folder, os.ModePerm)
- fileid := model.TypeImage
- if fileid != "" {
- filename, err := m.GetFilename(fileid)
- if err != nil {
- return err
- }
- imagename := folder + "/" + filename
- file, err := os.Create(imagename)
- if err != nil {
- return err
- }
- defer file.Close()
- err = m.GetFile(fileid, file)
- if err != nil {
- return err
- }
- model.TypeImage = filename
- }
- data, err := json.Marshal(model)
- if err != nil {
- return err
- }
- filename := folder + "/effecttype.json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- func (m *MongoDAO) backupEffects(path string) error {
- path = path + "/effects"
- os.MkdirAll(path, os.ModePerm)
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- collection := m.database.Collection(effectsCollectionName)
- cursor, err := collection.Find(ctx, bson.M{})
- if err != nil {
- log.Fatal(err)
- }
- defer cursor.Close(ctx)
- count := 0
- fmt.Print("backup effects: ")
- for cursor.Next(ctx) {
- var model model.Effect
- if err = cursor.Decode(&model); err != nil {
- log.Fatal(err)
- return err
- } else {
- folder := path + "/" + model.ID.Hex()
- os.MkdirAll(folder, os.ModePerm)
- fileid := model.Image
- if fileid != "" {
- filename, err := m.GetFilename(fileid)
- if err != nil {
- return err
- }
- imagename := folder + "/" + filename
- file, err := os.Create(imagename)
- if err != nil {
- return err
- }
- defer file.Close()
- err = m.GetFile(fileid, file)
- if err != nil {
- return err
- }
- model.Image = filename
- }
- data, err := json.Marshal(model)
- if err != nil {
- return err
- }
- filename := folder + "/effect.json"
- ioutil.WriteFile(filename, data, os.ModePerm)
- count++
- if count%100 == 0 {
- fmt.Print(".")
- }
- }
- }
- fmt.Println()
- return nil
- }
- // Ping pinging the mongoDao
- func (m *MongoDAO) Ping() error {
- if !m.initialised {
- return errors.New("mongo client not initialised")
- }
- ctx, _ := context.WithTimeout(context.Background(), timeout)
- return m.database.Client().Ping(ctx, nil)
- }
|