/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ package engine import ( "context" "errors" "fmt" "regexp" "strings" "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "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/options" ) func (ms *MongoStorage) GetTpIds(colName string) (tpids []string, err error) { getTpIDs := func(ctx context.Context, col string, tpMap utils.StringSet) (utils.StringSet, error) { if strings.HasPrefix(col, "tp_") { result, err := ms.getCol(col).Distinct(ctx, "tpid", bson.D{}) if err != nil { return tpMap, err } for _, tpid := range result { tpMap.Add(tpid.(string)) } } return tpMap, nil } tpidMap := make(utils.StringSet) if colName == "" { if err := ms.query(func(sctx mongo.SessionContext) error { col, err := ms.DB().ListCollections(sctx, bson.D{}, options.ListCollections().SetNameOnly(true)) if err != nil { return err } for col.Next(sctx) { var elem struct{ Name string } if err := col.Decode(&elem); err != nil { return err } if tpidMap, err = getTpIDs(sctx, elem.Name, tpidMap); err != nil { return err } } return col.Close(sctx) }); err != nil { return nil, err } } else { if err := ms.query(func(sctx mongo.SessionContext) error { tpidMap, err = getTpIDs(sctx, colName, tpidMap) return err }); err != nil { return nil, err } } tpids = tpidMap.AsSlice() return tpids, nil } func (ms *MongoStorage) GetTpTableIds(tpid, table string, distinctIDs utils.TPDistinctIds, filter map[string]string, pag *utils.PaginatorWithSearch) ([]string, error) { findMap := bson.M{} if tpid != "" { findMap["tpid"] = tpid } for k, v := range filter { if k != "" && v != "" { findMap[k] = v } } fop := options.Find() if pag != nil { if pag.Search != "" { var searchItems []bson.M for _, distinctID := range distinctIDs { searchItems = append(searchItems, bson.M{ distinctID: primitive.Regex{ Pattern: ".*" + regexp.QuoteMeta(pag.Search) + ".*", }, }, ) } findMap["$or"] = searchItems } if pag.Paginator != nil { if pag.Limit != nil { fop = fop.SetLimit(int64(*pag.Limit)) } if pag.Offset != nil { fop = fop.SetSkip(int64(*pag.Offset)) } } } selectors := bson.M{"_id": 0} for i, distinctID := range distinctIDs { if distinctID == "tag" { // convert the tag used in SQL into id used here distinctIDs[i] = "id" } selectors[distinctIDs[i]] = 1 } fop.SetProjection(selectors) distinctIds := make(utils.StringMap) err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(table).Find(sctx, findMap, fop) if qryErr != nil { return qryErr } for cur.Next(sctx) { var item bson.M err := cur.Decode(&item) if err != nil { return err } var id string last := len(distinctIDs) - 1 for i, distinctID := range distinctIDs { if distinctValue, ok := item[distinctID]; ok { id += distinctValue.(string) } if i < last { id += utils.ConcatenatedKeySep } } distinctIds[id] = true } return cur.Close(sctx) }) if err != nil { return nil, err } return distinctIds.Slice(), nil } func (ms *MongoStorage) GetTPTimings(tpid, id string) ([]*utils.ApierTPTiming, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var tpTimings []*utils.ApierTPTiming err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPTimings).Find(sctx, filter) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.ApierTPTiming qryErr = cur.Decode(&el) if qryErr != nil { return qryErr } tpTimings = append(tpTimings, &el) } if len(tpTimings) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpTimings, err } func (ms *MongoStorage) GetTPDestinations(tpid, id string) ([]*utils.TPDestination, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var tpDestinations []*utils.TPDestination err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPDestinations).Find(sctx, filter) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.TPDestination qryErr = cur.Decode(&el) if qryErr != nil { return qryErr } tpDestinations = append(tpDestinations, &el) } if len(tpDestinations) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpDestinations, err } func (ms *MongoStorage) GetTPRates(tpid, id string) ([]*utils.TPRateRALs, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var tpRates []*utils.TPRateRALs err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPRates).Find(sctx, filter) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.TPRateRALs err := cur.Decode(&el) if err != nil { return err } for _, rs := range el.RateSlots { rs.SetDurations() } tpRates = append(tpRates, &el) } if len(tpRates) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpRates, err } func (ms *MongoStorage) GetTPDestinationRates(tpid, id string, pag *utils.Paginator) ([]*utils.TPDestinationRate, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var tpDstRates []*utils.TPDestinationRate fop := options.Find() if pag != nil { if pag.Limit != nil { fop = fop.SetLimit(int64(*pag.Limit)) } if pag.Offset != nil { fop = fop.SetSkip(int64(*pag.Offset)) } } err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPDestinationRates).Find(sctx, filter, fop) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.TPDestinationRate qryErr = cur.Decode(&el) if qryErr != nil { return qryErr } tpDstRates = append(tpDstRates, &el) } if len(tpDstRates) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpDstRates, err } func (ms *MongoStorage) GetTPRatingPlans(tpid, id string, pag *utils.Paginator) ([]*utils.TPRatingPlan, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var tpRatingPlans []*utils.TPRatingPlan fop := options.Find() if pag != nil { if pag.Limit != nil { fop = fop.SetLimit(int64(*pag.Limit)) } if pag.Offset != nil { fop = fop.SetSkip(int64(*pag.Offset)) } } err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPRatingPlans).Find(sctx, filter, fop) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.TPRatingPlan qryErr = cur.Decode(&el) if qryErr != nil { return qryErr } tpRatingPlans = append(tpRatingPlans, &el) } if len(tpRatingPlans) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpRatingPlans, err } func (ms *MongoStorage) GetTPRatingProfiles(tp *utils.TPRatingProfile) ([]*utils.TPRatingProfile, error) { filter := bson.M{"tpid": tp.TPid} if tp.Tenant != "" { filter["tenant"] = tp.Tenant } if tp.Category != "" { filter["category"] = tp.Category } if tp.Subject != "" { filter["subject"] = tp.Subject } if tp.LoadId != "" { filter["loadid"] = tp.LoadId } var tpRatingProfiles []*utils.TPRatingProfile err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(utils.TBLTPRatingProfiles).Find(sctx, filter) if qryErr != nil { return qryErr } for cur.Next(sctx) { var el utils.TPRatingProfile qryErr = cur.Decode(&el) if qryErr != nil { return qryErr } tpRatingProfiles = append(tpRatingProfiles, &el) } if len(tpRatingProfiles) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return tpRatingProfiles, err } func (ms *MongoStorage) GetTPSharedGroups(tpid, id string) ([]*utils.TPSharedGroups, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var results []*utils.TPSharedGroups err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPSharedGroups).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPSharedGroups err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPResources(tpid, tenant, id string) ([]*utils.TPResourceProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPResourceProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPResources).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPResourceProfile err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPIPs(tpid, tenant, id string) ([]*utils.TPIPProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPIPProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPIPs).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPIPProfile err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPStats(tpid, tenant, id string) ([]*utils.TPStatProfile, error) { filter := bson.M{ "tpid": tpid, } if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPStatProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPStats).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPStatProfile err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPTrends(tpid string, tenant string, id string) ([]*utils.TPTrendsProfile, error) { filter := bson.M{ "tpid": tpid, } if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPTrendsProfile err := ms.query(func(sctx mongo.SessionContext) error { cur, err := ms.getCol(utils.TBLTPTrends).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPTrendsProfile err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPRankings(tpid string, tenant string, id string) ([]*utils.TPRankingProfile, error) { filter := bson.M{ "tpid": tpid, } if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPRankingProfile err := ms.query(func(sctx mongo.SessionContext) error { cur, err := ms.getCol(utils.TBLTPRankings).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPRankingProfile err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPActions(tpid, id string) ([]*utils.TPActions, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var results []*utils.TPActions err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPActions).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPActions err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPActionPlans(tpid, id string) ([]*utils.TPActionPlan, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } var results []*utils.TPActionPlan err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPActionPlans).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPActionPlan err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPActionTriggers(tpid, id string) ([]*utils.TPActionTriggers, error) { filter := bson.M{ "tpid": tpid, } if id != "" { filter["id"] = id } var results []*utils.TPActionTriggers err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPActionTriggers).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPActionTriggers err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) GetTPAccountActions(tp *utils.TPAccountActions) ([]*utils.TPAccountActions, error) { filter := bson.M{"tpid": tp.TPid} if tp.Tenant != "" { filter["tenant"] = tp.Tenant } if tp.Account != "" { filter["account"] = tp.Account } if tp.LoadId != "" { filter["loadid"] = tp.LoadId } var results []*utils.TPAccountActions err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPAccountActions).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var el utils.TPAccountActions err := cur.Decode(&el) if err != nil { return err } results = append(results, &el) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) RemTpData(table, tpid string, args map[string]string) error { if table == utils.EmptyString { // Remove tpid out of all tables return ms.query(func(sctx mongo.SessionContext) error { col, err := ms.DB().ListCollections(sctx, bson.D{}, options.ListCollections().SetNameOnly(true)) if err != nil { return err } for col.Next(sctx) { var elem struct{ Name string } if err := col.Decode(&elem); err != nil { return err } if strings.HasPrefix(elem.Name, "tp_") { _, err = ms.getCol(elem.Name).DeleteMany(sctx, bson.M{"tpid": tpid}) if err != nil { return err } } } return col.Close(sctx) }) } // Remove from a single table if args == nil { args = make(map[string]string) } if _, has := args["tag"]; has { // API uses tag to be compatible with SQL models, fix it here args["id"] = args["tag"] delete(args, "tag") } if tpid != "" { args["tpid"] = tpid } return ms.query(func(sctx mongo.SessionContext) (err error) { dr, err := ms.getCol(table).DeleteOne(sctx, args) if dr.DeletedCount == 0 { return utils.ErrNotFound } return err }) } func (ms *MongoStorage) SetTPTimings(tps []*utils.ApierTPTiming) error { if len(tps) == 0 { return nil } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { _, err = ms.getCol(utils.TBLTPTimings).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPDestinations(tpDsts []*utils.TPDestination) (err error) { if len(tpDsts) == 0 { return nil } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpDsts { _, err = ms.getCol(utils.TBLTPDestinations).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPRates(tps []*utils.TPRateRALs) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true _, err := ms.getCol(utils.TBLTPRates).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}) if err != nil { return err } } _, err := ms.getCol(utils.TBLTPRates).InsertOne(sctx, tp) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPDestinationRates(tps []*utils.TPDestinationRate) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true _, err := ms.getCol(utils.TBLTPDestinationRates).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}) if err != nil { return err } } _, err := ms.getCol(utils.TBLTPDestinationRates).InsertOne(sctx, tp) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPRatingPlans(tps []*utils.TPRatingPlan) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true _, err := ms.getCol(utils.TBLTPRatingPlans).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}) if err != nil { return err } } _, err := ms.getCol(utils.TBLTPRatingPlans).InsertOne(sctx, tp) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPRatingProfiles(tps []*utils.TPRatingProfile) error { if len(tps) == 0 { return nil } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { _, err = ms.getCol(utils.TBLTPRatingProfiles).UpdateOne(sctx, bson.M{ "tpid": tp.TPid, "loadid": tp.LoadId, "tenant": tp.Tenant, "category": tp.Category, "subject": tp.Subject, }, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPSharedGroups(tps []*utils.TPSharedGroups) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true _, err := ms.getCol(utils.TBLTPSharedGroups).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}) if err != nil { return err } } _, err := ms.getCol(utils.TBLTPSharedGroups).InsertOne(sctx, tp) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPActions(tps []*utils.TPActions) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true if _, err := ms.getCol(utils.TBLTPActions).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}); err != nil { return err } } if _, err := ms.getCol(utils.TBLTPActions).InsertOne(sctx, tp); err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPActionPlans(tps []*utils.TPActionPlan) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true if _, err := ms.getCol(utils.TBLTPActionPlans).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}); err != nil { return err } } if _, err := ms.getCol(utils.TBLTPActionPlans).InsertOne(sctx, tp); err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPActionTriggers(tps []*utils.TPActionTriggers) error { if len(tps) == 0 { return nil } m := make(map[string]bool) return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { if !m[tp.ID] { m[tp.ID] = true if _, err := ms.getCol(utils.TBLTPActionTriggers).DeleteMany(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}); err != nil { return err } } if _, err := ms.getCol(utils.TBLTPActionTriggers).InsertOne(sctx, tp); err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPAccountActions(tps []*utils.TPAccountActions) error { if len(tps) == 0 { return nil } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { _, err = ms.getCol(utils.TBLTPAccountActions).UpdateOne(sctx, bson.M{ "tpid": tp.TPid, "loadid": tp.LoadId, "tenant": tp.Tenant, "account": tp.Account, }, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPResources(tpRLs []*utils.TPResourceProfile) (err error) { if len(tpRLs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpRLs { _, err = ms.getCol(utils.TBLTPResources).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPIPs(tps []*utils.TPIPProfile) (err error) { if len(tps) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { _, err = ms.getCol(utils.TBLTPIPs).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPRStats(tps []*utils.TPStatProfile) (err error) { if len(tps) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tps { _, err = ms.getCol(utils.TBLTPStats).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetSMCost(smc *SMCost) error { if smc.CostDetails == nil { return nil } return ms.query(func(sctx mongo.SessionContext) (err error) { _, err = ms.getCol(utils.SessionCostsTBL).InsertOne(sctx, smc) return err }) } func (ms *MongoStorage) RemoveSMCost(smc *SMCost) error { remParams := bson.M{} if smc != nil { remParams = bson.M{"cgrid": smc.CGRID, "runid": smc.RunID} } return ms.query(func(sctx mongo.SessionContext) (err error) { _, err = ms.getCol(utils.SessionCostsTBL).DeleteMany(sctx, remParams) return err }) } func (ms *MongoStorage) GetSMCosts(cgrid, runid, originHost, originIDPrefix string) (smcs []*SMCost, err error) { filter := bson.M{} if cgrid != "" { filter[CGRIDLow] = cgrid } if runid != "" { filter[RunIDLow] = runid } if originHost != "" { filter[OriginHostLow] = originHost } if originIDPrefix != "" { filter[OriginIDLow] = primitive.Regex{ Pattern: "^" + originIDPrefix, } } err = ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.SessionCostsTBL).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var smCost SMCost err := cur.Decode(&smCost) if err != nil { return err } clone := smCost clone.CostDetails.initCache() smcs = append(smcs, &clone) } if len(smcs) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return smcs, err } func (ms *MongoStorage) RemoveSMCosts(qryFltr *utils.SMCostFilter) error { filters := bson.M{ CGRIDLow: bson.M{"$in": qryFltr.CGRIDs, "$nin": qryFltr.NotCGRIDs}, RunIDLow: bson.M{"$in": qryFltr.RunIDs, "$nin": qryFltr.NotRunIDs}, OriginHostLow: bson.M{"$in": qryFltr.OriginHosts, "$nin": qryFltr.NotOriginHosts}, OriginIDLow: bson.M{"$in": qryFltr.OriginIDs, "$nin": qryFltr.NotOriginIDs}, CostSourceLow: bson.M{"$in": qryFltr.CostSources, "$nin": qryFltr.NotCostSources}, UsageLow: bson.M{"$gte": qryFltr.Usage.Min, "$lt": qryFltr.Usage.Max}, CreatedAtLow: bson.M{"$gte": qryFltr.CreatedAt.Begin, "$lt": qryFltr.CreatedAt.End}, } ms.cleanEmptyFilters(filters) return ms.query(func(sctx mongo.SessionContext) (err error) { _, err = ms.getCol(utils.SessionCostsTBL).DeleteMany(sctx, filters) return err }) } func (ms *MongoStorage) SetCDR(cdr *CDR, allowUpdate bool) error { if cdr.OrderID == 0 { cdr.OrderID = ms.counter.Next() } var savedCDR any = cdr if config.CgrConfig().CdrsCfg().CompressStoredCost && cdr.CostDetails != nil { cdrC, err := cdr.AsCompressedCostCDR(ms.ms) if err != nil { return err } savedCDR = cdrC } return ms.query(func(sctx mongo.SessionContext) (err error) { if allowUpdate { _, err = ms.getCol(ColCDRs).UpdateOne(sctx, bson.M{CGRIDLow: cdr.CGRID, RunIDLow: cdr.RunID}, bson.M{"$set": savedCDR}, options.Update().SetUpsert(true)) return } _, err = ms.getCol(ColCDRs).InsertOne(sctx, savedCDR) if err != nil && strings.Contains(err.Error(), "E11000") { // Mongo returns E11000 when key is duplicated err = utils.ErrExists } return }) } func (ms *MongoStorage) cleanEmptyFilters(filters bson.M) { for k, v := range filters { switch value := v.(type) { case *int64: if value == nil { delete(filters, k) } case *float64: if value == nil { delete(filters, k) } case *time.Time: if value == nil { delete(filters, k) } case *time.Duration: if value == nil { delete(filters, k) } case []string: if len(value) == 0 { delete(filters, k) } case bson.M: ms.cleanEmptyFilters(value) if len(value) == 0 { delete(filters, k) } } } } // _, err := col(ColCDRs).UpdateAll(bson.M{CGRIDLow: bson.M{"$in": cgrIds}}, bson.M{"$set": bson.M{"deleted_at": time.Now()}}) func (ms *MongoStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) (cdrs []*CDR, n int64, err error) { var minUsage, maxUsage *time.Duration if qryFltr.MinUsage != utils.EmptyString { parsedDur, err := utils.ParseDurationWithNanosecs(qryFltr.MinUsage) if err != nil { return nil, 0, err } else { minUsage = &parsedDur } } if qryFltr.MaxUsage != utils.EmptyString { parsedDur, err := utils.ParseDurationWithNanosecs(qryFltr.MaxUsage) if err != nil { return nil, 0, err } else { maxUsage = &parsedDur } } filters := bson.M{ CGRIDLow: bson.M{"$in": qryFltr.CGRIDs, "$nin": qryFltr.NotCGRIDs}, RunIDLow: bson.M{"$in": qryFltr.RunIDs, "$nin": qryFltr.NotRunIDs}, OriginIDLow: bson.M{"$in": qryFltr.OriginIDs, "$nin": qryFltr.NotOriginIDs}, OrderIDLow: bson.M{"$gte": qryFltr.OrderIDStart, "$lt": qryFltr.OrderIDEnd}, ToRLow: bson.M{"$in": qryFltr.ToRs, "$nin": qryFltr.NotToRs}, CDRHostLow: bson.M{"$in": qryFltr.OriginHosts, "$nin": qryFltr.NotOriginHosts}, CDRSourceLow: bson.M{"$in": qryFltr.Sources, "$nin": qryFltr.NotSources}, RequestTypeLow: bson.M{"$in": qryFltr.RequestTypes, "$nin": qryFltr.NotRequestTypes}, TenantLow: bson.M{"$in": qryFltr.Tenants, "$nin": qryFltr.NotTenants}, CategoryLow: bson.M{"$in": qryFltr.Categories, "$nin": qryFltr.NotCategories}, AccountLow: bson.M{"$in": qryFltr.Accounts, "$nin": qryFltr.NotAccounts}, SubjectLow: bson.M{"$in": qryFltr.Subjects, "$nin": qryFltr.NotSubjects}, SetupTimeLow: bson.M{"$gte": qryFltr.SetupTimeStart, "$lt": qryFltr.SetupTimeEnd}, AnswerTimeLow: bson.M{"$gte": qryFltr.AnswerTimeStart, "$lt": qryFltr.AnswerTimeEnd}, CreatedAtLow: bson.M{"$gte": qryFltr.CreatedAtStart, "$lt": qryFltr.CreatedAtEnd}, UpdatedAtLow: bson.M{"$gte": qryFltr.UpdatedAtStart, "$lt": qryFltr.UpdatedAtEnd}, UsageLow: bson.M{"$gte": minUsage, "$lt": maxUsage}, // CostDetailsLow + "." + AccountLow: bson.M{"$in": qryFltr.RatedAccounts, "$nin": qryFltr.NotRatedAccounts}, // CostDetailsLow + "." + SubjectLow: bson.M{"$in": qryFltr.RatedSubjects, "$nin": qryFltr.NotRatedSubjects}, } // file, _ := os.TempFile(os.TempDir(), "debug") // file.WriteString(fmt.Sprintf("FILTER: %v\n", utils.ToIJSON(qryFltr))) // file.WriteString(fmt.Sprintf("BEFORE: %v\n", utils.ToIJSON(filters))) ms.cleanEmptyFilters(filters) if len(qryFltr.DestinationPrefixes) != 0 { var regexpRule string for _, prefix := range qryFltr.DestinationPrefixes { if prefix == utils.EmptyString { continue } if regexpRule != utils.EmptyString { regexpRule += "|" } regexpRule += "^(" + regexp.QuoteMeta(prefix) + ")" } if _, hasIt := filters["$and"]; !hasIt { filters["$and"] = make([]bson.M, 0) } // The "$and" operator is used to include additional query conditions that cannot be // represented at the top level of the query. filters["$and"] = append(filters["$and"].([]bson.M), bson.M{ DestinationLow: primitive.Regex{ Pattern: regexpRule, }, }) } if len(qryFltr.NotDestinationPrefixes) != 0 { if _, hasIt := filters["$and"]; !hasIt { filters["$and"] = make([]bson.M, 0) } for _, prefix := range qryFltr.NotDestinationPrefixes { if prefix == utils.EmptyString { continue } filters["$and"] = append(filters["$and"].([]bson.M), bson.M{ DestinationLow: primitive.Regex{ Pattern: "^(?!" + prefix + ")", }, }, ) } } if len(qryFltr.ExtraFields) != 0 { var extrafields []bson.M for field, value := range qryFltr.ExtraFields { if value == utils.MetaExists { extrafields = append(extrafields, bson.M{"extrafields." + field: bson.M{"$exists": true}}) } else { extrafields = append(extrafields, bson.M{"extrafields." + field: value}) } } filters["$and"] = extrafields } if len(qryFltr.NotExtraFields) != 0 { var extrafields []bson.M for field, value := range qryFltr.NotExtraFields { if value == utils.MetaExists { extrafields = append(extrafields, bson.M{"extrafields." + field: bson.M{"$exists": false}}) } else { extrafields = append(extrafields, bson.M{"extrafields." + field: bson.M{"$ne": value}}) } } filters["$and"] = extrafields } if qryFltr.MinCost != nil { if qryFltr.MaxCost == nil { filters[CostLow] = bson.M{"$gte": *qryFltr.MinCost} } else if *qryFltr.MinCost == 0.0 && *qryFltr.MaxCost == -1.0 { // Special case when we want to skip errors filters["$or"] = []bson.M{ {CostLow: bson.M{"$gte": 0.0}}, } } else { filters[CostLow] = bson.M{"$gte": *qryFltr.MinCost, "$lt": *qryFltr.MaxCost} } } else if qryFltr.MaxCost != nil { if *qryFltr.MaxCost == -1.0 { // Non-rated CDRs filters[CostLow] = 0.0 // Need to include it otherwise all CDRs will be returned } else { // Above limited CDRs, since MinCost is empty, make sure we query also NULL cost filters[CostLow] = bson.M{"$lt": *qryFltr.MaxCost} } } // file.WriteString(fmt.Sprintf("AFTER: %v\n", utils.ToIJSON(filters))) // file.Close() if remove { err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { dr, qryErr := ms.getCol(ColCDRs).DeleteMany(sctx, filters) if qryErr != nil { return qryErr } n = dr.DeletedCount return qryErr }) return nil, n, err } fop := options.Find() cop := options.Count() if qryFltr.Paginator.Limit != nil { fop = fop.SetLimit(int64(*qryFltr.Paginator.Limit)) cop = cop.SetLimit(int64(*qryFltr.Paginator.Limit)) } if qryFltr.Paginator.Offset != nil { fop = fop.SetSkip(int64(*qryFltr.Paginator.Offset)) cop = cop.SetSkip(int64(*qryFltr.Paginator.Offset)) } if qryFltr.OrderBy != "" { var orderVal string separateVals := strings.Split(qryFltr.OrderBy, utils.InfieldSep) ordVal := 1 if len(separateVals) == 2 && separateVals[1] == "desc" { ordVal = -1 // orderVal += "-" } switch separateVals[0] { case utils.OrderID: orderVal += "orderid" case utils.AnswerTime: orderVal += "answertime" case utils.SetupTime: orderVal += "setuptime" case utils.Usage: orderVal += "usage" case utils.Cost: orderVal += "cost" default: return nil, 0, fmt.Errorf("invalid value : %s", separateVals[0]) } fop = fop.SetSort(bson.M{orderVal: ordVal}) } if qryFltr.Count { var cnt int64 err := ms.query(func(sctx mongo.SessionContext) (qryErr error) { cnt, qryErr = ms.getCol(ColCDRs).CountDocuments(sctx, filters, cop) return qryErr }) if err != nil { return nil, 0, err } return nil, cnt, nil } // Execute query err = ms.query(func(sctx mongo.SessionContext) (qryErr error) { cur, qryErr := ms.getCol(ColCDRs).Find(sctx, filters, fop) if qryErr != nil { return qryErr } for cur.Next(sctx) { var err error var cdr CDR if config.CgrConfig().CdrsCfg().CompressStoredCost { cdrC := CompressedCostCDR{} err = cur.Decode(&cdrC) if err != nil { return err } cdrP, err := NewCDRFromCompressedCDR(&cdrC, ms.ms) if err != nil { return } cdr = *cdrP } else { if err = cur.Decode(&cdr); err != nil { return err } } clone := cdr clone.CostDetails.initCache() cdrs = append(cdrs, &clone) } if len(cdrs) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return cdrs, 0, err } func (ms *MongoStorage) SetTPStats(tpSTs []*utils.TPStatProfile) (err error) { if len(tpSTs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpSTs { _, err = ms.getCol(utils.TBLTPStats).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPTrends(tpTrends []*utils.TPTrendsProfile) (err error) { if len(tpTrends) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpTrends { _, err := ms.getCol(utils.TBLTPTrends).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) SetTPRankings(tpRankings []*utils.TPRankingProfile) (err error) { if len(tpRankings) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpRankings { _, err := ms.getCol(utils.TBLTPRankings).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true)) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPThresholds(tpid, tenant, id string) ([]*utils.TPThresholdProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPThresholdProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPThresholds).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPThresholdProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPThresholds(tpTHs []*utils.TPThresholdProfile) (err error) { if len(tpTHs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpTHs { _, err = ms.getCol(utils.TBLTPThresholds).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPFilters(tpid, tenant, id string) ([]*utils.TPFilterProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } results := []*utils.TPFilterProfile{} err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPFilters).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPFilterProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPFilters(tpTHs []*utils.TPFilterProfile) (err error) { if len(tpTHs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpTHs { _, err = ms.getCol(utils.TBLTPFilters).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPRoutes(tpid, tenant, id string) ([]*utils.TPRouteProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPRouteProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPRoutes).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPRouteProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPRoutes(tpRoutes []*utils.TPRouteProfile) (err error) { if len(tpRoutes) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpRoutes { _, err = ms.getCol(utils.TBLTPRoutes).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPAttributes(tpid, tenant, id string) ([]*utils.TPAttributeProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPAttributeProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPAttributes).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPAttributeProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPAttributes(tpRoutes []*utils.TPAttributeProfile) (err error) { if len(tpRoutes) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpRoutes { _, err = ms.getCol(utils.TBLTPAttributes).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPChargers(tpid, tenant, id string) ([]*utils.TPChargerProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPChargerProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPChargers).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPChargerProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPChargers(tpCPP []*utils.TPChargerProfile) (err error) { if len(tpCPP) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpCPP { _, err = ms.getCol(utils.TBLTPChargers).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPDispatcherProfiles(tpid, tenant, id string) ([]*utils.TPDispatcherProfile, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPDispatcherProfile err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPDispatchers).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPDispatcherProfile err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPDispatcherProfiles(tpDPPs []*utils.TPDispatcherProfile) (err error) { if len(tpDPPs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpDPPs { _, err = ms.getCol(utils.TBLTPDispatchers).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetTPDispatcherHosts(tpid, tenant, id string) ([]*utils.TPDispatcherHost, error) { filter := bson.M{"tpid": tpid} if id != "" { filter["id"] = id } if tenant != "" { filter["tenant"] = tenant } var results []*utils.TPDispatcherHost err := ms.query(func(sctx mongo.SessionContext) (err error) { cur, err := ms.getCol(utils.TBLTPDispatcherHosts).Find(sctx, filter) if err != nil { return err } for cur.Next(sctx) { var tp utils.TPDispatcherHost err := cur.Decode(&tp) if err != nil { return err } results = append(results, &tp) } if len(results) == 0 { return utils.ErrNotFound } return cur.Close(sctx) }) return results, err } func (ms *MongoStorage) SetTPDispatcherHosts(tpDPPs []*utils.TPDispatcherHost) (err error) { if len(tpDPPs) == 0 { return } return ms.query(func(sctx mongo.SessionContext) (err error) { for _, tp := range tpDPPs { _, err = ms.getCol(utils.TBLTPDispatcherHosts).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, bson.M{"$set": tp}, options.Update().SetUpsert(true), ) if err != nil { return err } } return nil }) } func (ms *MongoStorage) GetVersions(itm string) (Versions, error) { fop := options.FindOne() if itm != "" { fop.SetProjection(bson.M{itm: 1, "_id": 0}) } else { fop.SetProjection(bson.M{"_id": 0}) } var vrs Versions err := ms.query(func(sctx mongo.SessionContext) (err error) { sr := ms.getCol(ColVer).FindOne(sctx, bson.D{}, fop) decodeErr := sr.Decode(&vrs) if errors.Is(decodeErr, mongo.ErrNoDocuments) { return utils.ErrNotFound } return decodeErr }) if err != nil { return nil, err } if len(vrs) == 0 { return nil, utils.ErrNotFound } return vrs, nil } func (ms *MongoStorage) SetVersions(vrs Versions, overwrite bool) error { if overwrite { err := ms.RemoveVersions(nil) if err != nil && !errors.Is(err, utils.ErrNotFound) { return err } } return ms.query(func(sctx mongo.SessionContext) error { _, err := ms.getCol(ColVer).UpdateOne(sctx, bson.D{}, bson.M{"$set": vrs}, options.Update().SetUpsert(true), ) return err }) // } // return ms.query( func(sctx mongo.SessionContext) error { // _, err := ms.getCol(ColVer).InsertOne(sctx, vrs) // return err // }) // _, err = col.Upsert(bson.M{}, bson.M{"$set": &vrs}) } func (ms *MongoStorage) RemoveVersions(vrs Versions) error { if len(vrs) == 0 { return ms.query(func(sctx mongo.SessionContext) error { dr, err := ms.getCol(ColVer).DeleteOne(sctx, bson.D{}) if err != nil { return err } if dr.DeletedCount == 0 { return utils.ErrNotFound } return nil }) } return ms.query(func(sctx mongo.SessionContext) error { for k := range vrs { if _, err := ms.getCol(ColVer).UpdateOne(sctx, bson.D{}, bson.M{"$unset": bson.M{k: 1}}, options.Update().SetUpsert(true)); err != nil { return err } } return nil }) } func (ms *MongoStorage) GetStorageType() string { return utils.MetaMongo } // RewriteStorDB used only for InternalDB func (ms *MongoStorage) DumpStorDB() (err error) { return utils.ErrNotImplemented } // RewriteStorDB used only for InternalDB func (ms *MongoStorage) RewriteStorDB() (err error) { return utils.ErrNotImplemented } // BackupStorDB used only for InternalDB func (ms *MongoStorage) BackupStorDB(backupFolderPath string, zip bool) (err error) { return utils.ErrNotImplemented }