From 803138eab89292506fff25623a654a487eec4213 Mon Sep 17 00:00:00 2001 From: Ginny Guan Date: Mon, 21 Oct 2024 18:03:47 +0800 Subject: [PATCH] refactor: separate count and query functions in DB interface Signed-off-by: Ginny Guan --- internal/core/data/application/reading.go | 9 +++- .../core/data/controller/http/reading_test.go | 3 +- .../core/data/infrastructure/interfaces/db.go | 3 +- .../interfaces/mocks/DBClient.go | 53 +++++++++++++------ .../metadata/application/deviceprofile.go | 11 +++- .../controller/http/deviceprofile_test.go | 5 +- .../metadata/infrastructure/interfaces/db.go | 3 +- .../interfaces/mocks/DBClient.go | 51 +++++++++++++----- .../infrastructure/postgres/device_profile.go | 22 ++++---- .../pkg/infrastructure/postgres/reading.go | 39 +++++--------- internal/pkg/infrastructure/redis/client.go | 44 ++++++++++++--- .../infrastructure/redis/device_profile.go | 46 +++------------- internal/pkg/infrastructure/redis/queries.go | 17 +++--- internal/pkg/infrastructure/redis/reading.go | 8 +-- 14 files changed, 179 insertions(+), 135 deletions(-) diff --git a/internal/core/data/application/reading.go b/internal/core/data/application/reading.go index 6e179cdfad..af64a418e3 100644 --- a/internal/core/data/application/reading.go +++ b/internal/core/data/application/reading.go @@ -231,7 +231,14 @@ func ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourc dbClient := container.DBClientFrom(dic.Get) var readingModels []models.Reading if len(resourceNames) > 0 { - readingModels, totalCount, err = dbClient.ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName, resourceNames, start, end, offset, limit) + totalCount, err = dbClient.ReadingCountByDeviceNameAndResourceNamesAndTimeRange(deviceName, resourceNames, start, end) + if err != nil { + return readings, totalCount, errors.NewCommonEdgeXWrapper(err) + } + if cont, err := utils.CheckCountRange(totalCount, offset, limit); !cont { + return []dtos.BaseReading{}, totalCount, err + } + readingModels, err = dbClient.ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName, resourceNames, start, end, offset, limit) } else { totalCount, err = dbClient.ReadingCountByDeviceNameAndTimeRange(deviceName, start, end) if err != nil { diff --git a/internal/core/data/controller/http/reading_test.go b/internal/core/data/controller/http/reading_test.go index f842e5d6f4..8dacfb0a95 100644 --- a/internal/core/data/controller/http/reading_test.go +++ b/internal/core/data/controller/http/reading_test.go @@ -628,7 +628,8 @@ func TestReadingsByDeviceNameAndResourceNamesAndTimeRange(t *testing.T) { dbClientMock := &dbMock.DBClient{} dbClientMock.On("ReadingCountByDeviceNameAndTimeRange", TestDeviceName, int64(0), int64(100)).Return(totalCount, nil) dbClientMock.On("ReadingsByDeviceNameAndTimeRange", TestDeviceName, int64(0), int64(100), 0, 10).Return([]models.Reading{}, nil) - dbClientMock.On("ReadingsByDeviceNameAndResourceNamesAndTimeRange", TestDeviceName, testResourceNames, int64(0), int64(100), 0, 10).Return([]models.Reading{}, totalCount, nil) + dbClientMock.On("ReadingCountByDeviceNameAndResourceNamesAndTimeRange", TestDeviceName, testResourceNames, int64(0), int64(100)).Return(totalCount, nil) + dbClientMock.On("ReadingsByDeviceNameAndResourceNamesAndTimeRange", TestDeviceName, testResourceNames, int64(0), int64(100), 0, 10).Return([]models.Reading{}, nil) dic.Update(di.ServiceConstructorMap{ container.DBClientInterfaceName: func(get di.Get) interface{} { return dbClientMock diff --git a/internal/core/data/infrastructure/interfaces/db.go b/internal/core/data/infrastructure/interfaces/db.go index 11fa359ffe..3e73666805 100644 --- a/internal/core/data/infrastructure/interfaces/db.go +++ b/internal/core/data/infrastructure/interfaces/db.go @@ -38,7 +38,8 @@ type DBClient interface { ReadingCountByDeviceNameAndResourceNameAndTimeRange(deviceName string, resourceName string, start int64, end int64) (uint32, errors.EdgeX) ReadingCountByTimeRange(start int64, end int64) (uint32, errors.EdgeX) ReadingsByResourceNameAndTimeRange(resourceName string, start int64, end int64, offset int, limit int) ([]model.Reading, errors.EdgeX) - ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset, limit int) ([]model.Reading, uint32, errors.EdgeX) + ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset, limit int) ([]model.Reading, errors.EdgeX) + ReadingCountByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceName []string, start int64, end int64) (uint32, errors.EdgeX) ReadingsByDeviceNameAndTimeRange(deviceName string, start int64, end int64, offset int, limit int) ([]model.Reading, errors.EdgeX) ReadingCountByDeviceNameAndTimeRange(deviceName string, start int64, end int64) (uint32, errors.EdgeX) LatestReadingByOffset(offset uint32) (model.Reading, errors.EdgeX) diff --git a/internal/core/data/infrastructure/interfaces/mocks/DBClient.go b/internal/core/data/infrastructure/interfaces/mocks/DBClient.go index 627fd86b2a..bf74eb357c 100644 --- a/internal/core/data/infrastructure/interfaces/mocks/DBClient.go +++ b/internal/core/data/infrastructure/interfaces/mocks/DBClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.43.0. DO NOT EDIT. +// Code generated by mockery v2.45.0. DO NOT EDIT. package mocks @@ -480,6 +480,36 @@ func (_m *DBClient) ReadingCountByDeviceNameAndResourceNameAndTimeRange(deviceNa return r0, r1 } +// ReadingCountByDeviceNameAndResourceNamesAndTimeRange provides a mock function with given fields: deviceName, resourceName, start, end +func (_m *DBClient) ReadingCountByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceName []string, start int64, end int64) (uint32, errors.EdgeX) { + ret := _m.Called(deviceName, resourceName, start, end) + + if len(ret) == 0 { + panic("no return value specified for ReadingCountByDeviceNameAndResourceNamesAndTimeRange") + } + + var r0 uint32 + var r1 errors.EdgeX + if rf, ok := ret.Get(0).(func(string, []string, int64, int64) (uint32, errors.EdgeX)); ok { + return rf(deviceName, resourceName, start, end) + } + if rf, ok := ret.Get(0).(func(string, []string, int64, int64) uint32); ok { + r0 = rf(deviceName, resourceName, start, end) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(string, []string, int64, int64) errors.EdgeX); ok { + r1 = rf(deviceName, resourceName, start, end) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(errors.EdgeX) + } + } + + return r0, r1 +} + // ReadingCountByDeviceNameAndTimeRange provides a mock function with given fields: deviceName, start, end func (_m *DBClient) ReadingCountByDeviceNameAndTimeRange(deviceName string, start int64, end int64) (uint32, errors.EdgeX) { ret := _m.Called(deviceName, start, end) @@ -727,7 +757,7 @@ func (_m *DBClient) ReadingsByDeviceNameAndResourceNameAndTimeRange(deviceName s } // ReadingsByDeviceNameAndResourceNamesAndTimeRange provides a mock function with given fields: deviceName, resourceNames, start, end, offset, limit -func (_m *DBClient) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset int, limit int) ([]models.Reading, uint32, errors.EdgeX) { +func (_m *DBClient) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset int, limit int) ([]models.Reading, errors.EdgeX) { ret := _m.Called(deviceName, resourceNames, start, end, offset, limit) if len(ret) == 0 { @@ -735,9 +765,8 @@ func (_m *DBClient) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName } var r0 []models.Reading - var r1 uint32 - var r2 errors.EdgeX - if rf, ok := ret.Get(0).(func(string, []string, int64, int64, int, int) ([]models.Reading, uint32, errors.EdgeX)); ok { + var r1 errors.EdgeX + if rf, ok := ret.Get(0).(func(string, []string, int64, int64, int, int) ([]models.Reading, errors.EdgeX)); ok { return rf(deviceName, resourceNames, start, end, offset, limit) } if rf, ok := ret.Get(0).(func(string, []string, int64, int64, int, int) []models.Reading); ok { @@ -748,21 +777,15 @@ func (_m *DBClient) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName } } - if rf, ok := ret.Get(1).(func(string, []string, int64, int64, int, int) uint32); ok { + if rf, ok := ret.Get(1).(func(string, []string, int64, int64, int, int) errors.EdgeX); ok { r1 = rf(deviceName, resourceNames, start, end, offset, limit) } else { - r1 = ret.Get(1).(uint32) - } - - if rf, ok := ret.Get(2).(func(string, []string, int64, int64, int, int) errors.EdgeX); ok { - r2 = rf(deviceName, resourceNames, start, end, offset, limit) - } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).(errors.EdgeX) + if ret.Get(1) != nil { + r1 = ret.Get(1).(errors.EdgeX) } } - return r0, r1, r2 + return r0, r1 } // ReadingsByDeviceNameAndTimeRange provides a mock function with given fields: deviceName, start, end, offset, limit diff --git a/internal/core/metadata/application/deviceprofile.go b/internal/core/metadata/application/deviceprofile.go index 8126f73bea..982d6e231c 100644 --- a/internal/core/metadata/application/deviceprofile.go +++ b/internal/core/metadata/application/deviceprofile.go @@ -227,7 +227,16 @@ func DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer st return deviceProfiles, totalCount, errors.NewCommonEdgeX(errors.KindContractInvalid, "model is empty", nil) } dbClient := container.DBClientFrom(dic.Get) - dps, totalCount, err := dbClient.DeviceProfilesByManufacturerAndModel(offset, limit, manufacturer, model) + totalCount, err = dbClient.DeviceProfileCountByManufacturerAndModel(manufacturer, model) + if err != nil { + return deviceProfiles, totalCount, errors.NewCommonEdgeXWrapper(err) + } + cont, err := utils.CheckCountRange(totalCount, offset, limit) + if !cont { + return []dtos.DeviceProfile{}, totalCount, err + } + + dps, err := dbClient.DeviceProfilesByManufacturerAndModel(offset, limit, manufacturer, model) if err != nil { return deviceProfiles, totalCount, errors.NewCommonEdgeXWrapper(err) } diff --git a/internal/core/metadata/controller/http/deviceprofile_test.go b/internal/core/metadata/controller/http/deviceprofile_test.go index 3e2c85fd6e..9fcea1df69 100644 --- a/internal/core/metadata/controller/http/deviceprofile_test.go +++ b/internal/core/metadata/controller/http/deviceprofile_test.go @@ -1454,8 +1454,9 @@ func TestDeviceProfilesByManufacturerAndModel(t *testing.T) { dic := mockDic() dbClientMock := &mocks.DBClient{} - dbClientMock.On("DeviceProfilesByManufacturerAndModel", 0, 10, TestManufacturer, TestModel).Return(deviceProfiles, expectedTotalProfileCount, nil) - dbClientMock.On("DeviceProfilesByManufacturerAndModel", 1, 2, TestManufacturer, TestModel).Return([]models.DeviceProfile{deviceProfiles[1], deviceProfiles[2]}, expectedTotalProfileCount, nil) + dbClientMock.On("DeviceProfileCountByManufacturerAndModel", TestManufacturer, TestModel).Return(expectedTotalProfileCount, nil) + dbClientMock.On("DeviceProfilesByManufacturerAndModel", 0, 10, TestManufacturer, TestModel).Return(deviceProfiles, nil) + dbClientMock.On("DeviceProfilesByManufacturerAndModel", 1, 2, TestManufacturer, TestModel).Return([]models.DeviceProfile{deviceProfiles[1], deviceProfiles[2]}, nil) dbClientMock.On("DeviceProfilesByManufacturerAndModel", 4, 1, TestManufacturer, TestModel).Return([]models.DeviceProfile{}, expectedTotalProfileCount, errors.NewCommonEdgeX(errors.KindRangeNotSatisfiable, "query objects bounds out of range.", nil)) dic.Update(di.ServiceConstructorMap{ container.DBClientInterfaceName: func(get di.Get) interface{} { diff --git a/internal/core/metadata/infrastructure/interfaces/db.go b/internal/core/metadata/infrastructure/interfaces/db.go index 98f3fd6324..e26fe5ed06 100644 --- a/internal/core/metadata/infrastructure/interfaces/db.go +++ b/internal/core/metadata/infrastructure/interfaces/db.go @@ -23,10 +23,11 @@ type DBClient interface { AllDeviceProfiles(offset int, limit int, labels []string) ([]model.DeviceProfile, errors.EdgeX) DeviceProfilesByModel(offset int, limit int, model string) ([]model.DeviceProfile, errors.EdgeX) DeviceProfilesByManufacturer(offset int, limit int, manufacturer string) ([]model.DeviceProfile, errors.EdgeX) - DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]model.DeviceProfile, uint32, errors.EdgeX) + DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]model.DeviceProfile, errors.EdgeX) DeviceProfileCountByLabels(labels []string) (uint32, errors.EdgeX) DeviceProfileCountByManufacturer(manufacturer string) (uint32, errors.EdgeX) DeviceProfileCountByModel(model string) (uint32, errors.EdgeX) + DeviceProfileCountByManufacturerAndModel(manufacturer string, model string) (uint32, errors.EdgeX) AddDeviceService(ds model.DeviceService) (model.DeviceService, errors.EdgeX) DeviceServiceById(id string) (model.DeviceService, errors.EdgeX) diff --git a/internal/core/metadata/infrastructure/interfaces/mocks/DBClient.go b/internal/core/metadata/infrastructure/interfaces/mocks/DBClient.go index 18ba0fcc84..eb0549dc73 100644 --- a/internal/core/metadata/infrastructure/interfaces/mocks/DBClient.go +++ b/internal/core/metadata/infrastructure/interfaces/mocks/DBClient.go @@ -738,6 +738,36 @@ func (_m *DBClient) DeviceProfileCountByManufacturer(manufacturer string) (uint3 return r0, r1 } +// DeviceProfileCountByManufacturerAndModel provides a mock function with given fields: manufacturer, model +func (_m *DBClient) DeviceProfileCountByManufacturerAndModel(manufacturer string, model string) (uint32, errors.EdgeX) { + ret := _m.Called(manufacturer, model) + + if len(ret) == 0 { + panic("no return value specified for DeviceProfileCountByManufacturerAndModel") + } + + var r0 uint32 + var r1 errors.EdgeX + if rf, ok := ret.Get(0).(func(string, string) (uint32, errors.EdgeX)); ok { + return rf(manufacturer, model) + } + if rf, ok := ret.Get(0).(func(string, string) uint32); ok { + r0 = rf(manufacturer, model) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(string, string) errors.EdgeX); ok { + r1 = rf(manufacturer, model) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(errors.EdgeX) + } + } + + return r0, r1 +} + // DeviceProfileCountByModel provides a mock function with given fields: model func (_m *DBClient) DeviceProfileCountByModel(model string) (uint32, errors.EdgeX) { ret := _m.Called(model) @@ -831,7 +861,7 @@ func (_m *DBClient) DeviceProfilesByManufacturer(offset int, limit int, manufact } // DeviceProfilesByManufacturerAndModel provides a mock function with given fields: offset, limit, manufacturer, model -func (_m *DBClient) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]models.DeviceProfile, uint32, errors.EdgeX) { +func (_m *DBClient) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]models.DeviceProfile, errors.EdgeX) { ret := _m.Called(offset, limit, manufacturer, model) if len(ret) == 0 { @@ -839,9 +869,8 @@ func (_m *DBClient) DeviceProfilesByManufacturerAndModel(offset int, limit int, } var r0 []models.DeviceProfile - var r1 uint32 - var r2 errors.EdgeX - if rf, ok := ret.Get(0).(func(int, int, string, string) ([]models.DeviceProfile, uint32, errors.EdgeX)); ok { + var r1 errors.EdgeX + if rf, ok := ret.Get(0).(func(int, int, string, string) ([]models.DeviceProfile, errors.EdgeX)); ok { return rf(offset, limit, manufacturer, model) } if rf, ok := ret.Get(0).(func(int, int, string, string) []models.DeviceProfile); ok { @@ -852,21 +881,15 @@ func (_m *DBClient) DeviceProfilesByManufacturerAndModel(offset int, limit int, } } - if rf, ok := ret.Get(1).(func(int, int, string, string) uint32); ok { + if rf, ok := ret.Get(1).(func(int, int, string, string) errors.EdgeX); ok { r1 = rf(offset, limit, manufacturer, model) } else { - r1 = ret.Get(1).(uint32) - } - - if rf, ok := ret.Get(2).(func(int, int, string, string) errors.EdgeX); ok { - r2 = rf(offset, limit, manufacturer, model) - } else { - if ret.Get(2) != nil { - r2 = ret.Get(2).(errors.EdgeX) + if ret.Get(1) != nil { + r1 = ret.Get(1).(errors.EdgeX) } } - return r0, r1, r2 + return r0, r1 } // DeviceProfilesByModel provides a mock function with given fields: offset, limit, model diff --git a/internal/pkg/infrastructure/postgres/device_profile.go b/internal/pkg/infrastructure/postgres/device_profile.go index 68dbc18f17..a4f38474cc 100644 --- a/internal/pkg/infrastructure/postgres/device_profile.go +++ b/internal/pkg/infrastructure/postgres/device_profile.go @@ -17,7 +17,6 @@ import ( pkgCommon "github.com/edgexfoundry/edgex-go/internal/pkg/common" pgClient "github.com/edgexfoundry/edgex-go/internal/pkg/db/postgres" - "github.com/edgexfoundry/edgex-go/internal/pkg/utils" "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" model "github.com/edgexfoundry/go-mod-core-contracts/v3/models" ) @@ -180,21 +179,11 @@ func (c *Client) DeviceProfilesByManufacturer(offset int, limit int, manufacture } // DeviceProfilesByManufacturerAndModel query device profiles with offset, limit, manufacturer and model -func (c *Client) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) (profiles []model.DeviceProfile, totalCount uint32, err errors.EdgeX) { +func (c *Client) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) (profiles []model.DeviceProfile, err errors.EdgeX) { ctx := context.Background() offset, validLimit := getValidOffsetAndLimit(offset, limit) queryObj := map[string]any{modelField: model, manufacturerField: manufacturer} - totalCount, err = getTotalRowsCount(ctx, c.ConnPool, sqlQueryCountByJSONField(deviceProfileTableName), queryObj) - if err != nil { - return profiles, totalCount, err - } - cont, err := utils.CheckCountRange(totalCount, offset, limit) - if !cont { - return profiles, totalCount, err - } - profiles, err = queryDeviceProfiles(ctx, c.ConnPool, sqlQueryContentByJSONFieldWithPagination(deviceProfileTableName), queryObj, offset, validLimit) - - return profiles, totalCount, err + return queryDeviceProfiles(ctx, c.ConnPool, sqlQueryContentByJSONFieldWithPagination(deviceProfileTableName), queryObj, offset, validLimit) } // DeviceProfileCountByLabels returns the total count of Device Profiles with labels specified. If no label is specified, the total count of all device profiles will be returned. @@ -222,6 +211,13 @@ func (c *Client) DeviceProfileCountByModel(model string) (uint32, errors.EdgeX) return getTotalRowsCount(ctx, c.ConnPool, sqlQueryCountByJSONField(deviceProfileTableName), queryObj) } +// DeviceProfileCountByManufacturerAndModel returns the count of Device Profiles associated with specified manufacturer and model +func (c *Client) DeviceProfileCountByManufacturerAndModel(manufacturer, model string) (uint32, errors.EdgeX) { + ctx := context.Background() + queryObj := map[string]any{manufacturerField: manufacturer, modelField: model} + return getTotalRowsCount(ctx, c.ConnPool, sqlQueryCountByJSONField(deviceProfileTableName), queryObj) +} + func deviceProfileNameExists(ctx context.Context, connPool *pgxpool.Pool, name string) (bool, errors.EdgeX) { var exists bool queryObj := map[string]any{nameField: name} diff --git a/internal/pkg/infrastructure/postgres/reading.go b/internal/pkg/infrastructure/postgres/reading.go index 4509ce55c9..b9aa84ace1 100644 --- a/internal/pkg/infrastructure/postgres/reading.go +++ b/internal/pkg/infrastructure/postgres/reading.go @@ -13,8 +13,6 @@ import ( pgClient "github.com/edgexfoundry/edgex-go/internal/pkg/db/postgres" dbModels "github.com/edgexfoundry/edgex-go/internal/pkg/infrastructure/postgres/models" - "github.com/edgexfoundry/edgex-go/internal/pkg/utils" - "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" model "github.com/edgexfoundry/go-mod-core-contracts/v3/models" @@ -130,39 +128,21 @@ func (c *Client) ReadingsByDeviceNameAndResourceNameAndTimeRange(deviceName stri } // ReadingsByDeviceNameAndResourceNamesAndTimeRange query readings by the specified device and resourceName slice, origin within the time range, offset and limit -func (c *Client) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset, limit int) ([]model.Reading, uint32, errors.EdgeX) { +func (c *Client) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64, offset, limit int) ([]model.Reading, errors.EdgeX) { ctx := context.Background() sqlStatement := sqlQueryAllWithPaginationAndTimeRangeDescByCol(readingTableName, originCol, originCol, []string{resourceNameCol}, deviceNameCol, resourceNameCol) - // build the query args for the where condition using in querying readings and reading count - queryArgs := []any{start, end, deviceName, resourceNames} - // make a copy for query count args as we don't need offset and limit while querying total count - queryCountArgs := append([]any{}, queryArgs...) - // add offset and limit for query args - queryArgs = append(queryArgs, offset, limit) - + // build the query args for the where condition using in querying readings + queryArgs := []any{start, end, deviceName, resourceNames, offset, limit} // query readings readings, err := queryReadings(ctx, c.ConnPool, sqlStatement, queryArgs...) if err != nil { - return nil, 0, errors.NewCommonEdgeXWrapper(err) - } - - // get the total count of readings based on the condition column names and query count args - totalCount, err := getTotalRowsCount(context.Background(), - c.ConnPool, - sqlQueryCountByTimeRangeCol(readingTableName, originCol, []string{resourceNameCol}, deviceNameCol, resourceNameCol), - queryCountArgs...) - if err != nil { - return nil, 0, errors.NewCommonEdgeXWrapper(err) - } - cont, err := utils.CheckCountRange(totalCount, offset, limit) - if !cont { - return readings, totalCount, err + return nil, errors.NewCommonEdgeXWrapper(err) } - return readings, totalCount, nil + return readings, nil } // ReadingCountByDeviceName returns the count of Readings associated a specific Device from db @@ -213,6 +193,15 @@ func (c *Client) ReadingCountByDeviceNameAndResourceNameAndTimeRange(deviceName resourceName) } +// ReadingCountByDeviceNameAndResourceNamesAndTimeRange returns the count of readings by origin within the time range +// associated with the specified device and resourceName slice from db +func (c *Client) ReadingCountByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64) (uint32, errors.EdgeX) { + return getTotalRowsCount(context.Background(), + c.ConnPool, + sqlQueryCountByTimeRangeCol(readingTableName, originCol, []string{resourceNameCol}, deviceNameCol, resourceNameCol), + start, end, deviceName, resourceNames) +} + func (c *Client) LatestReadingByOffset(offset uint32) (model.Reading, errors.EdgeX) { ctx := context.Background() diff --git a/internal/pkg/infrastructure/redis/client.go b/internal/pkg/infrastructure/redis/client.go index ce57479e50..7859c6ee99 100644 --- a/internal/pkg/infrastructure/redis/client.go +++ b/internal/pkg/infrastructure/redis/client.go @@ -274,15 +274,15 @@ func (c *Client) DeviceProfilesByManufacturer(offset int, limit int, manufacture } // DeviceProfilesByManufacturerAndModel query device profiles with offset, limit, manufacturer and model -func (c *Client) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]model.DeviceProfile, uint32, errors.EdgeX) { +func (c *Client) DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]model.DeviceProfile, errors.EdgeX) { conn := c.Pool.Get() defer conn.Close() - deviceProfiles, totalCount, edgeXerr := deviceProfilesByManufacturerAndModel(conn, offset, limit, manufacturer, model) + deviceProfiles, edgeXerr := deviceProfilesByManufacturerAndModel(conn, offset, limit, manufacturer, model) if edgeXerr != nil { - return deviceProfiles, totalCount, errors.NewCommonEdgeXWrapper(edgeXerr) + return deviceProfiles, errors.NewCommonEdgeXWrapper(edgeXerr) } - return deviceProfiles, totalCount, nil + return deviceProfiles, nil } // EventTotalCount returns the total count of Event from the database @@ -640,6 +640,21 @@ func (c *Client) ReadingCountByDeviceNameAndResourceNameAndTimeRange(deviceName return count, nil } +// ReadingCountByDeviceNameAndResourceNamesAndTimeRange returns the count of readings by origin within the time range +// associated with the specified device and resourceName slice from db +func (c *Client) ReadingCountByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start int64, end int64) (uint32, errors.EdgeX) { + conn := c.Pool.Get() + defer conn.Close() + + readings, err := readingsByDeviceNameAndResourceNamesAndTimeRange(conn, deviceName, resourceNames, start, end, 0, -1) + if err != nil { + return 0, errors.NewCommonEdgeX(errors.Kind(err), + fmt.Sprintf("fail to query readings by deviceName %s, resourceNames %v and time range %v ~ %v", deviceName, resourceNames, start, end), err) + } + + return uint32(len(readings)), nil +} + // ReadingCountByTimeRange returns the count of Readings from the database within specified time range func (c *Client) ReadingCountByTimeRange(start int64, end int64) (uint32, errors.EdgeX) { conn := c.Pool.Get() @@ -692,17 +707,17 @@ func (c *Client) ReadingsByDeviceNameAndResourceNameAndTimeRange(deviceName stri return readings, nil } -func (c *Client) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start, end int64, offset, limit int) (readings []model.Reading, totalCount uint32, err errors.EdgeX) { +func (c *Client) ReadingsByDeviceNameAndResourceNamesAndTimeRange(deviceName string, resourceNames []string, start, end int64, offset, limit int) (readings []model.Reading, err errors.EdgeX) { conn := c.Pool.Get() defer conn.Close() - readings, totalCount, err = readingsByDeviceNameAndResourceNamesAndTimeRange(conn, deviceName, resourceNames, start, end, offset, limit) + readings, err = readingsByDeviceNameAndResourceNamesAndTimeRange(conn, deviceName, resourceNames, start, end, offset, limit) if err != nil { - return readings, totalCount, errors.NewCommonEdgeX(errors.Kind(err), + return readings, errors.NewCommonEdgeX(errors.Kind(err), fmt.Sprintf("fail to query readings by deviceName %s, resourceNames %v and time range %v ~ %v", deviceName, resourceNames, start, end), err) } - return readings, totalCount, nil + return readings, nil } func (c *Client) ReadingsByDeviceNameAndTimeRange(deviceName string, start int64, end int64, offset int, limit int) (readings []model.Reading, err errors.EdgeX) { @@ -869,6 +884,19 @@ func (c *Client) DeviceProfileCountByModel(model string) (uint32, errors.EdgeX) return count, nil } +// DeviceProfileCountByManufacturerAndModel returns the count of Device Profiles associated with specified manufacturer and model +func (c *Client) DeviceProfileCountByManufacturerAndModel(manufacturer, model string) (uint32, errors.EdgeX) { + conn := c.Pool.Get() + defer conn.Close() + + profiles, edgeXerr := deviceProfilesByManufacturerAndModel(conn, 0, -1, manufacturer, model) + if edgeXerr != nil { + return 0, errors.NewCommonEdgeXWrapper(edgeXerr) + } + + return uint32(len(profiles)), nil +} + // DeviceServiceCountByLabels returns the total count of Device Services with labels specified. If no label is specified, the total count of all device services will be returned. func (c *Client) DeviceServiceCountByLabels(labels []string) (uint32, errors.EdgeX) { conn := c.Pool.Get() diff --git a/internal/pkg/infrastructure/redis/device_profile.go b/internal/pkg/infrastructure/redis/device_profile.go index 5fb8fc3156..9b01557e01 100644 --- a/internal/pkg/infrastructure/redis/device_profile.go +++ b/internal/pkg/infrastructure/redis/device_profile.go @@ -281,44 +281,14 @@ func deviceProfilesByManufacturer(conn redis.Conn, offset int, limit int, manufa } // deviceProfilesByManufacturerAndModel query device profiles by offset, limit, manufacturer and model -func deviceProfilesByManufacturerAndModel(conn redis.Conn, offset int, limit int, manufacturer string, model string) (deviceProfiles []models.DeviceProfile, totalCount uint32, edgeXerr errors.EdgeX) { - if limit == 0 { - return - } - end := offset + limit - 1 - if limit == -1 { //-1 limit means that clients want to retrieve all remaining records after offset from DB, so specifying -1 for end - end = limit - } +func deviceProfilesByManufacturerAndModel(conn redis.Conn, offset int, limit int, manufacturer string, model string) (deviceProfiles []models.DeviceProfile, edgeXerr errors.EdgeX) { + var redisKeys []string + redisKeys = append(redisKeys, CreateKey(DeviceProfileCollectionManufacturer, manufacturer)) + redisKeys = append(redisKeys, CreateKey(DeviceProfileCollectionModel, model)) - idsSlice := make([][]string, 2) - // query ids by manufacturer - idsWithManufacturer, err := redis.Strings(conn.Do(ZREVRANGE, CreateKey(DeviceProfileCollectionManufacturer, manufacturer), 0, -1)) - if err != nil { - return nil, totalCount, errors.NewCommonEdgeX(errors.KindDatabaseError, fmt.Sprintf("query object ids by manufacturer %s from database failed", manufacturer), err) - } - idsSlice[0] = idsWithManufacturer - // query ids by model - idsWithModel, err := redis.Strings(conn.Do(ZREVRANGE, CreateKey(DeviceProfileCollectionModel, model), 0, -1)) + objects, err := intersectionObjectsByKeys(conn, offset, limit, redisKeys...) if err != nil { - return nil, totalCount, errors.NewCommonEdgeX(errors.KindDatabaseError, fmt.Sprintf("query object ids by model %s from database failed", manufacturer), err) - } - idsSlice[1] = idsWithModel - - //find common Ids among two-dimension Ids slice - commonIds := pkgCommon.FindCommonStrings(idsSlice...) - totalCount = uint32(len(commonIds)) - if offset > len(commonIds) { - return nil, totalCount, errors.NewCommonEdgeX(errors.KindRangeNotSatisfiable, fmt.Sprintf("query objects bounds out of range. length:%v", len(commonIds)), nil) - } - if end >= len(commonIds) || end == -1 { - commonIds = commonIds[offset:] - } else { // as end index in golang re-slice is exclusive, increment the end index to ensure the end could be inclusive - commonIds = commonIds[offset : end+1] - } - - objects, edgeXerr := getObjectsByIds(conn, pkgCommon.ConvertStringsToInterfaces(commonIds)) - if edgeXerr != nil { - return deviceProfiles, totalCount, errors.NewCommonEdgeXWrapper(edgeXerr) + return deviceProfiles, errors.NewCommonEdgeXWrapper(err) } deviceProfiles = make([]models.DeviceProfile, len(objects)) @@ -326,9 +296,9 @@ func deviceProfilesByManufacturerAndModel(conn redis.Conn, offset int, limit int dp := models.DeviceProfile{} err := json.Unmarshal(in, &dp) if err != nil { - return deviceProfiles, totalCount, errors.NewCommonEdgeX(errors.KindContractInvalid, "device profile parsing failed", err) + return deviceProfiles, errors.NewCommonEdgeX(errors.KindContractInvalid, "device profile parsing failed", err) } deviceProfiles[i] = dp } - return deviceProfiles, totalCount, nil + return deviceProfiles, nil } diff --git a/internal/pkg/infrastructure/redis/queries.go b/internal/pkg/infrastructure/redis/queries.go index d5cd7c8982..3527358690 100644 --- a/internal/pkg/infrastructure/redis/queries.go +++ b/internal/pkg/infrastructure/redis/queries.go @@ -282,12 +282,12 @@ func objectsByKeys(conn redis.Conn, setMethod string, offset int, limit int, red } // unionObjectsByKeysAndScoreRange returns objects resulting from the union of all the given sets with specified score range, offset, and limit -func unionObjectsByKeysAndScoreRange(conn redis.Conn, start int64, end int64, offset, limit int, redisKeys ...string) ([][]byte, uint32, errors.EdgeX) { +func unionObjectsByKeysAndScoreRange(conn redis.Conn, start int64, end int64, offset, limit int, redisKeys ...string) ([][]byte, errors.EdgeX) { return objectsByKeysAndScoreRange(conn, ZUNIONSTORE, start, end, offset, limit, redisKeys...) } // objectsByKeysAndScoreRange returns objects resulting from the set method of all the given sets with specified score range, offset, and limit. The data set method could be either ZINTERSTORE or ZUNIONSTORE -func objectsByKeysAndScoreRange(conn redis.Conn, setMethod string, start int64, end int64, offset, limit int, redisKeys ...string) (objects [][]byte, totalCount uint32, edgeXerr errors.EdgeX) { +func objectsByKeysAndScoreRange(conn redis.Conn, setMethod string, start int64, end int64, offset, limit int, redisKeys ...string) (objects [][]byte, edgeXerr errors.EdgeX) { // build up the redis command arguments args := redis.Args{} cacheSet := uuid.New().String() @@ -300,26 +300,21 @@ func objectsByKeysAndScoreRange(conn redis.Conn, setMethod string, start int64, // create a temporary sorted set, cacheSet, resulting from the specified setMethod _, err := conn.Do(setMethod, args...) if err != nil { - return nil, totalCount, errors.NewCommonEdgeX(errors.KindDatabaseError, fmt.Sprintf("failed to execute %s command with args %v", setMethod, args), err) - } - - // get the total count of the temporary sorted set - if totalCount, edgeXerr = getMemberCountByScoreRange(conn, cacheSet, start, end); edgeXerr != nil { - return nil, totalCount, edgeXerr + return nil, errors.NewCommonEdgeX(errors.KindDatabaseError, fmt.Sprintf("failed to execute %s command with args %v", setMethod, args), err) } // get objects from the temporary sorted set if objects, edgeXerr = getObjectsByScoreRange(conn, cacheSet, start, end, offset, limit); edgeXerr != nil { - return nil, totalCount, edgeXerr + return nil, edgeXerr } // clean up unused temporary sorted set _, err = redis.Int(conn.Do(DEL, cacheSet)) if err != nil { - return nil, totalCount, errors.NewCommonEdgeX(errors.KindDatabaseError, "cache set deletion failed", err) + return nil, errors.NewCommonEdgeX(errors.KindDatabaseError, "cache set deletion failed", err) } - return objects, totalCount, nil + return objects, nil } // idFromStoredKey extracts Id from the store key diff --git a/internal/pkg/infrastructure/redis/reading.go b/internal/pkg/infrastructure/redis/reading.go index 9d3067ba6d..0395a778c0 100644 --- a/internal/pkg/infrastructure/redis/reading.go +++ b/internal/pkg/infrastructure/redis/reading.go @@ -242,18 +242,18 @@ func readingsByDeviceNameAndResourceNameAndTimeRange(conn redis.Conn, deviceName return convertObjectsToReadings(objects) } -func readingsByDeviceNameAndResourceNamesAndTimeRange(conn redis.Conn, deviceName string, resourceNames []string, startTime int64, endTime int64, offset int, limit int) (readings []models.Reading, totalCount uint32, err errors.EdgeX) { +func readingsByDeviceNameAndResourceNamesAndTimeRange(conn redis.Conn, deviceName string, resourceNames []string, startTime int64, endTime int64, offset int, limit int) (readings []models.Reading, err errors.EdgeX) { var redisKeys []string for _, resourceName := range resourceNames { redisKeys = append(redisKeys, CreateKey(ReadingsCollectionDeviceNameResourceName, deviceName, resourceName)) } - objects, totalCount, err := unionObjectsByKeysAndScoreRange(conn, startTime, endTime, offset, limit, redisKeys...) + objects, err := unionObjectsByKeysAndScoreRange(conn, startTime, endTime, offset, limit, redisKeys...) if err != nil { - return readings, totalCount, err + return readings, err } readings, err = convertObjectsToReadings(objects) - return readings, totalCount, err + return readings, err } // readingsByTimeRange query readings by time range, offset, and limit