Files
cloud-services/pkg/mongo/fleets.go

456 lines
11 KiB
Go

package mongo
import (
"strings"
"fiskerinc.com/modules/common"
"fiskerinc.com/modules/db/queries"
"go.mongodb.org/mongo-driver/bson/primitive"
"fiskerinc.com/modules/utils/elptr"
"github.com/pkg/errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
// NewFleetsCollection is the constructor for Fleets and utilizes the same mongo connection
func NewFleetsCollection(c CollectionInterface) FleetsCollectionInterface {
return &FleetsCollection{c}
}
type FleetsCollectionInterface interface {
AddFleet(fleet *Fleet) error
FindFleet(filter *Fleet) (*Fleet, error)
UpdateFleet(filter *Fleet, fleet *Fleet) error
DeleteFleet(fleet *Fleet) error
SelectFleets(filter *Fleet, options *queries.PageQueryOptions) ([]Fleet, error)
GetFleetCount(filter *Fleet) (int64, error)
GetVehiclesForFleet(name, search string, options *queries.PageQueryOptions) ([]string, error)
GetVehiclesForFleetCount(name, search string) (int64, error)
AddVehiclesToFleet(name string, vins []string) error
DeleteVehiclesFromFleet(name string, vins []string) error
GetFiltersForFleet(name string, options *queries.PageQueryOptions) ([]common.CANFilter, error)
GetFiltersForFleetCount(name string) (int64, error)
AddFilterToFleet(name string, filter *common.CANFilter) error
UpdateFilterForFleet(name string, id string, filter *common.CANFilter) error
DeleteFilterFromFleet(name string, id string) error
GetCANBusForVehicle(vin string) (*Fleet, error)
CollectionInterface
}
// FleetsCollection is the client for the collection
type FleetsCollection struct {
CollectionInterface
}
// Fleet is an embedded object within Vehicle
type Fleet struct {
Name string `json:"name" bson:"name"`
LogLevel common.LogLevel `json:"log_level" bson:"log_level,omitempty"`
CANBus common.CANBus `json:"canbus" bson:"canbus,omitempty"`
IDPSEnabled bool `json:"idps_enabled" bson:"idps_enabled"`
DebugMask string `json:"debug_mask,omitempty" bson:"debug_mask,omitempty"`
Tags []string `json:"tags" bson:"tags,omitempty"`
Vehicles []string `json:"vehicles" bson:"vehicles,omitempty"`
VehiclesCount int `json:"vehicles_count" bson:"vehicles_count,omitempty"`
search string `json:"-" bson:"-"`
}
func (f *Fleet) SetSearchQuery(search string) {
f.search = search
}
func (f *Fleet) SearchQuery() string {
return f.search
}
func (f *Fleet) AsExpr() bson.M {
if f == nil || len(strings.Trim(f.search, " ")) == 0 {
return bson.M{}
}
return bson.M{"$text": bson.M{"$search": f.search}}
}
func (c *FleetsCollection) AddFleet(fleet *Fleet) error {
_, err := c.InsertOne(fleet)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) FindFleet(filter *Fleet) (*Fleet, error) {
m := bson.M{"name": filter.Name}
projection := bson.D{{"vehicles", 0}}
f := &Fleet{}
err := c.FindOne(m, f, projection)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, nil
}
return nil, err
}
return f, nil
}
func (c *FleetsCollection) UpdateFleet(filter *Fleet, fleet *Fleet) error {
if fleet.CANBus.DTCEnabled == nil {
fleet.CANBus.DTCEnabled = elptr.ElPtr(false)
}
m := bson.M{"name": filter.Name}
u := bson.M{"$set": bson.M{
"name": fleet.Name,
"canbus.enabled": fleet.CANBus.Enabled,
"canbus.data_logger": fleet.CANBus.DataLogger,
"canbus.dtc_enabled": fleet.CANBus.DTCEnabled,
"canbus.mem_buff_size": fleet.CANBus.MemBuffSize,
"canbus.disk_buff_size": fleet.CANBus.DiskBuffSize,
"idps_enabled": fleet.IDPSEnabled,
"log_level": fleet.LogLevel,
"debug_mask": fleet.DebugMask,
}}
_, err := c.UpdateOne(m, u)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) DeleteFleet(fleet *Fleet) error {
m := bson.M{"name": fleet.Name}
err := c.DeleteOne(m)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) SelectFleets(filter *Fleet, options *queries.PageQueryOptions) ([]Fleet, error) {
m, f := filter.AsExpr(), make([]Fleet, 0, 0)
if err := c.Find(m, &f, options); err != nil {
return nil, err
}
for x := range f {
if f[x].CANBus.DTCEnabled == nil {
f[x].CANBus.DTCEnabled = elptr.ElPtr(false)
}
}
return f, nil
}
func (c *FleetsCollection) GetFleetCount(filter *Fleet) (int64, error) {
m := filter.AsExpr()
if len(filter.Tags) > 0 {
m["tags"] = bson.M{"$all": filter.Tags}
}
return c.Count(m)
}
func (c *FleetsCollection) GetVehiclesForFleet(name, search string, options *queries.PageQueryOptions) ([]string, error) {
preProc := mongo.Pipeline{
{{"$match", bson.D{{"name", name}}}},
{{"$project", bson.D{{
"vin", bson.D{{
"$cond", bson.A{bson.D{{"$isArray", "$vehicles"}}, "$vehicles", []string{}},
}},
}}}},
{{"$unwind", "$vin"}},
}
if search != "" {
preProc = append(preProc, bson.D{
{"$match", bson.M{"vin": primitive.Regex{
Pattern: search,
Options: "i",
}}}})
}
preProc = append(preProc, bson.D{{"$sort", bson.D{{"vin", 1}}}})
postProc := mongo.Pipeline{
{{"$group", bson.D{{"_id", "$_id"}, {"vehicles", bson.D{{"$push", "$vin"}}}}}},
}
pipe := mongo.Pipeline{}
pipe = append(pipe, preProc...)
if options.Offset > 0 {
pipe = append(pipe, bson.D{{"$skip", options.Offset}})
}
if options.Limit > 0 {
pipe = append(pipe, bson.D{{"$limit", options.Limit}})
}
pipe = append(pipe, postProc...)
var fleets []Fleet
err := c.Aggregate(pipe, &fleets)
if err != nil {
return nil, err
}
if len(fleets) != 1 {
return []string{}, nil
}
return fleets[0].Vehicles, nil
}
type count struct {
Total int64 `bson:"total"`
}
func (c *FleetsCollection) GetVehiclesForFleetCount(name, search string) (int64, error) {
pipe := mongo.Pipeline{
{{"$match", bson.D{{"name", name}}}},
}
if search == "" {
pipe = append(pipe, bson.D{{"$project", bson.D{{
"total", bson.D{{
"$size", bson.D{{
"$cond", bson.A{
bson.D{{"$isArray", "$vehicles"}}, "$vehicles", []string{}},
}},
}}}},
}})
} else {
pipe = append(pipe,
bson.D{{"$project",
bson.D{{"vin",
bson.D{{"$cond",
bson.A{
bson.D{{"$isArray", "$vehicles"}}, "$vehicles", []string{}}}}}}}},
bson.D{{"$unwind", "$vin"}},
bson.D{{"$match", bson.M{"vin": primitive.Regex{
Pattern: search,
Options: "i",
}}}},
bson.D{{"$count", "total"}},
)
}
var counter []count
err := c.Aggregate(pipe, &counter)
if err != nil {
return 0, err
}
if len(counter) != 1 {
return 0, nil
}
return counter[0].Total, nil
}
// Creates a Mongodb pipeline to accomplish several steps
// 1. Get document by name
// 2. Merge vins as sets
// 3. Calculate an accurate count
// 4. Store result in db
func createPipelineForFleetVehicles(name string, vins []string, mergeStrategy string) mongo.Pipeline {
return mongo.Pipeline{
{{"$match", bson.D{{"name", name}}}},
{{"$addFields", bson.D{
{"vehicles", bson.D{
{mergeStrategy, bson.A{
bson.D{{"$ifNull", bson.A{"$vehicles", bson.A{}}}},
vins,
}},
}},
}}},
{{"$set", bson.D{
{"vehicles_count", bson.D{
{"$size", bson.D{
{"$ifNull", bson.A{"$vehicles", bson.A{}}},
}},
}},
}}},
{{"$merge", bson.D{{"into", "fleets"}}}},
}
}
func (c *FleetsCollection) AddVehiclesToFleet(name string, vins []string) error {
if len(vins) == 0 {
return errors.Errorf("No VINs to add to fleet")
}
pipe := createPipelineForFleetVehicles(name, vins, "$setUnion")
var result []interface{}
err := c.Aggregate(pipe, &result)
return err
}
func (c *FleetsCollection) DeleteVehiclesFromFleet(name string, vins []string) error {
if len(vins) == 0 {
return errors.Errorf("No VINs to remove from fleet")
}
pipe := createPipelineForFleetVehicles(name, vins, "$setDifference")
var result []interface{}
err := c.Aggregate(pipe, &result)
return err
}
func (c *FleetsCollection) GetFiltersForFleet(name string, options *queries.PageQueryOptions) ([]common.CANFilter, error) {
preProc := mongo.Pipeline{
{{"$match", bson.D{{"name", name}}}},
{{"$project", bson.D{{
"canbus.filters", bson.D{{
"$cond", bson.A{bson.D{{"$isArray", "$canbus.filters"}}, "$canbus.filters", []common.CANFilter{}},
}},
}}}},
{{"$unwind", "$canbus.filters"}},
{{"$sort", bson.D{{"canbus.filters.can_id", 1}}}},
}
postProc := mongo.Pipeline{
{{"$group", bson.D{{"_id", "$_id"}, {"filters", bson.D{{"$push", "$canbus.filters"}}}}}},
{{"$project", bson.D{{"canbus.filters", "$filters"}}}},
}
pipe := mongo.Pipeline{}
pipe = append(pipe, preProc...)
if options.Offset > 0 {
pipe = append(pipe, bson.D{{"$skip", options.Offset}})
}
if options.Limit > 0 {
pipe = append(pipe, bson.D{{"$limit", options.Limit}})
}
pipe = append(pipe, postProc...)
var fleets []Fleet
err := c.Aggregate(pipe, &fleets)
if err != nil {
return nil, err
}
if len(fleets) != 1 {
return []common.CANFilter{}, nil
}
return fleets[0].CANBus.Filters, nil
}
func (c *FleetsCollection) GetFiltersForFleetCount(name string) (int64, error) {
pipe := mongo.Pipeline{
{{"$match", bson.D{{"name", name}}}},
{{"$project", bson.D{{
"total", bson.D{{
"$size", bson.D{{
"$cond", bson.A{bson.D{{"$isArray", "$canbus.filters"}}, "$canbus.filters", []common.CANBus{}},
}},
}}}},
}},
}
var counter []count
err := c.Aggregate(pipe, &counter)
if err != nil {
return 0, err
}
if len(counter) != 1 {
return 0, nil
}
return counter[0].Total, nil
}
func (c *FleetsCollection) AddFilterToFleet(name string, filter *common.CANFilter) error {
m := bson.M{"name": name}
u := bson.M{"$addToSet": bson.M{"canbus.filters": filter}}
_, err := c.UpdateOne(m, u)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) UpdateFilterForFleet(name string, id string, filter *common.CANFilter) error {
m := bson.M{
"name": name,
"canbus.filters.can_id": id,
}
u := bson.M{"$set": bson.M{
"canbus.filters.$.interval": filter.Interval,
"canbus.filters.$.edge_mask": filter.EdgeMask}}
_, err := c.UpdateOne(m, u)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) DeleteFilterFromFleet(name string, id string) error {
m := bson.M{"name": name}
u := bson.M{"$pull": bson.M{"canbus.filters": bson.M{"can_id": id}}}
_, err := c.UpdateOne(m, u)
if err != nil {
return err
}
return nil
}
func (c *FleetsCollection) GetCANBusForVehicle(vin string) (*Fleet, error) {
pipe := mongo.Pipeline{
{{"$match", bson.D{{"vehicles", vin}}}},
{{"$unwind", bson.D{{"path", "$canbus.filters"}, {"preserveNullAndEmptyArrays", true}}}},
{{"$group", bson.D{
{"_id", nil},
{"filters", bson.D{{"$push", "$canbus.filters"}}},
{"enabled", bson.D{{"$max", "$canbus.enabled"}}},
{"data_logger", bson.D{{"$max", "$canbus.data_logger"}}},
{"mem_buff_size", bson.D{{"$min", "$canbus.mem_buff_size"}}},
{"disk_buff_size", bson.D{{"$min", "$canbus.disk_buff_size"}}},
}}},
{{"$project", bson.D{
{"canbus.filters", "$filters"},
{"canbus.enabled", "$enabled"},
{"canbus.data_logger", "$data_logger"},
{"canbus.mem_buff_size", "$mem_buff_size"},
{"canbus.disk_buff_size", "$disk_buff_size"},
{"canbus.dtc_enabled", "$dtc_enabled"},
}}},
}
var fleets []Fleet
err := c.Aggregate(pipe, &fleets)
if err != nil {
return nil, err
}
if len(fleets) != 1 {
return nil, nil
}
theFleet := &fleets[0]
if theFleet.CANBus.DTCEnabled == nil {
theFleet.CANBus.DTCEnabled = elptr.ElPtr(false)
}
return theFleet, nil
}