456 lines
11 KiB
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
|
|
}
|