10 changed files with 689 additions and 341 deletions
@ -0,0 +1,101 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.dao.sql.query; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.UUIDConverter; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.query.EntityData; |
||||
|
import org.thingsboard.server.common.data.query.EntityDataPageLink; |
||||
|
import org.thingsboard.server.common.data.query.EntityKey; |
||||
|
import org.thingsboard.server.common.data.query.EntityKeyType; |
||||
|
import org.thingsboard.server.common.data.query.TsValue; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
public class EntityDataAdapter { |
||||
|
|
||||
|
public static PageData<EntityData> createEntityData(EntityDataPageLink pageLink, |
||||
|
List<EntityKeyMapping> selectionMapping, |
||||
|
List<Object[]> rows, |
||||
|
int totalElements) { |
||||
|
int totalPages = pageLink.getPageSize() > 0 ? (int)Math.ceil((float)totalElements / pageLink.getPageSize()) : 1; |
||||
|
int startIndex = pageLink.getPageSize() * pageLink.getPage(); |
||||
|
boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size(); |
||||
|
List<EntityData> entitiesData = convertListToEntityData(rows, selectionMapping); |
||||
|
return new PageData<>(entitiesData, totalPages, totalElements, hasNext); |
||||
|
} |
||||
|
|
||||
|
private static List<EntityData> convertListToEntityData(List<Object[]> result, List<EntityKeyMapping> selectionMapping) { |
||||
|
return result.stream().map(row -> toEntityData(row, selectionMapping)).collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
private static EntityData toEntityData(Object[] row, List<EntityKeyMapping> selectionMapping) { |
||||
|
String id = (String)row[0]; |
||||
|
EntityType entityType = EntityType.valueOf((String)row[1]); |
||||
|
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, UUIDConverter.fromString(id)); |
||||
|
Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>(); |
||||
|
Map<String, TsValue[]> timeseries = new HashMap<>(); |
||||
|
EntityData entityData = new EntityData(entityId, latest, timeseries); |
||||
|
for (EntityKeyMapping mapping: selectionMapping) { |
||||
|
EntityKey entityKey = mapping.getEntityKey(); |
||||
|
Object value = row[mapping.getIndex()]; |
||||
|
String strValue; |
||||
|
long ts; |
||||
|
if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) { |
||||
|
strValue = value != null ? value.toString() : null; |
||||
|
ts = System.currentTimeMillis(); |
||||
|
} else { |
||||
|
strValue = convertValue(value); |
||||
|
Object tsObject = row[mapping.getIndex()+1]; |
||||
|
ts = Long.parseLong(tsObject.toString()); |
||||
|
} |
||||
|
TsValue tsValue = new TsValue(ts, strValue); |
||||
|
latest.computeIfAbsent(entityKey.getType(), entityKeyType -> new HashMap<>()).put(entityKey.getKey(), tsValue); |
||||
|
} |
||||
|
return entityData; |
||||
|
} |
||||
|
|
||||
|
private static String convertValue(Object value) { |
||||
|
if (value != null) { |
||||
|
String strVal = value.toString(); |
||||
|
// check number
|
||||
|
if (strVal.length() > 0) { |
||||
|
try { |
||||
|
int intVal = Integer.parseInt(strVal); |
||||
|
return Integer.toString(intVal); |
||||
|
} catch (NumberFormatException ignored) { |
||||
|
} |
||||
|
try { |
||||
|
double dblVal = Double.parseDouble(strVal); |
||||
|
if (!Double.isInfinite(dblVal)) { |
||||
|
return Double.toString(dblVal); |
||||
|
} |
||||
|
} catch (NumberFormatException ignored) { |
||||
|
} |
||||
|
} |
||||
|
return strVal; |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,166 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.dao.service; |
||||
|
|
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.query.DeviceTypeFilter; |
||||
|
import org.thingsboard.server.common.data.query.EntityCountQuery; |
||||
|
import org.thingsboard.server.common.data.query.EntityData; |
||||
|
import org.thingsboard.server.common.data.query.EntityDataPageLink; |
||||
|
import org.thingsboard.server.common.data.query.EntityDataQuery; |
||||
|
import org.thingsboard.server.common.data.query.EntityDataSortOrder; |
||||
|
import org.thingsboard.server.common.data.query.EntityFilter; |
||||
|
import org.thingsboard.server.common.data.query.EntityKey; |
||||
|
import org.thingsboard.server.common.data.query.EntityKeyType; |
||||
|
import org.thingsboard.server.common.data.query.EntityListFilter; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
||||
|
|
||||
|
private TenantId tenantId; |
||||
|
|
||||
|
@Before |
||||
|
public void before() { |
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant"); |
||||
|
Tenant savedTenant = tenantService.saveTenant(tenant); |
||||
|
Assert.assertNotNull(savedTenant); |
||||
|
tenantId = savedTenant.getId(); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void after() { |
||||
|
tenantService.deleteTenant(tenantId); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testCountEntitiesByQuery() { |
||||
|
List<Device> devices = new ArrayList<>(); |
||||
|
for (int i = 0; i < 97; i++) { |
||||
|
Device device = new Device(); |
||||
|
device.setTenantId(tenantId); |
||||
|
device.setName("Device" + i); |
||||
|
device.setType("default"); |
||||
|
device.setLabel("testLabel" + (int) (Math.random() * 1000)); |
||||
|
devices.add(deviceService.saveDevice(device)); |
||||
|
} |
||||
|
|
||||
|
DeviceTypeFilter filter = new DeviceTypeFilter(); |
||||
|
filter.setDeviceType("default"); |
||||
|
filter.setDeviceNameFilter(""); |
||||
|
|
||||
|
EntityCountQuery countQuery = new EntityCountQuery(filter); |
||||
|
|
||||
|
long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
||||
|
Assert.assertEquals(97, count); |
||||
|
|
||||
|
filter.setDeviceType("unknown"); |
||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
||||
|
Assert.assertEquals(0, count); |
||||
|
|
||||
|
filter.setDeviceType("default"); |
||||
|
filter.setDeviceNameFilter("Device1"); |
||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
||||
|
Assert.assertEquals(11, count); |
||||
|
|
||||
|
EntityListFilter entityListFilter = new EntityListFilter(); |
||||
|
entityListFilter.setEntityType(EntityType.DEVICE); |
||||
|
entityListFilter.setEntityList(devices.stream().map(Device::getId).map(DeviceId::toString).collect(Collectors.toList())); |
||||
|
|
||||
|
countQuery = new EntityCountQuery(entityListFilter); |
||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
||||
|
Assert.assertEquals(97, count); |
||||
|
|
||||
|
deviceService.deleteDevicesByTenantId(tenantId); |
||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
||||
|
Assert.assertEquals(0, count); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindEntityDataByQuery() { |
||||
|
List<Device> devices = new ArrayList<>(); |
||||
|
for (int i = 0; i < 97; i++) { |
||||
|
Device device = new Device(); |
||||
|
device.setTenantId(tenantId); |
||||
|
device.setName("Device" + i); |
||||
|
device.setType("default"); |
||||
|
device.setLabel("testLabel" + (int) (Math.random() * 1000)); |
||||
|
devices.add(deviceService.saveDevice(device)); |
||||
|
} |
||||
|
|
||||
|
DeviceTypeFilter filter = new DeviceTypeFilter(); |
||||
|
filter.setDeviceType("default"); |
||||
|
filter.setDeviceNameFilter(""); |
||||
|
|
||||
|
EntityDataSortOrder sortOrder = new EntityDataSortOrder( |
||||
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC |
||||
|
); |
||||
|
EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); |
||||
|
List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name")); |
||||
|
|
||||
|
EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, null, null); |
||||
|
PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); |
||||
|
|
||||
|
Assert.assertEquals(97, data.getTotalElements()); |
||||
|
Assert.assertEquals(10, data.getTotalPages()); |
||||
|
Assert.assertTrue(data.hasNext()); |
||||
|
Assert.assertEquals(10, data.getData().size()); |
||||
|
|
||||
|
List<EntityData> loadedEntities = new ArrayList<>(data.getData()); |
||||
|
while(data.hasNext()) { |
||||
|
query = query.next(); |
||||
|
data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); |
||||
|
loadedEntities.addAll(data.getData()); |
||||
|
} |
||||
|
Assert.assertEquals(97, loadedEntities.size()); |
||||
|
|
||||
|
List<EntityId> loadedIds = loadedEntities.stream().map(EntityData::getEntityId).collect(Collectors.toList()); |
||||
|
List<EntityId> deviceIds = devices.stream().map(Device::getId).collect(Collectors.toList()); |
||||
|
|
||||
|
Assert.assertEquals(deviceIds, loadedIds); |
||||
|
|
||||
|
List<String> loadedNames = loadedEntities.stream().map(entityData -> |
||||
|
entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue()).collect(Collectors.toList()); |
||||
|
List<String> deviceNames = devices.stream().map(Device::getName).collect(Collectors.toList()); |
||||
|
|
||||
|
Assert.assertEquals(deviceNames, loadedNames); |
||||
|
|
||||
|
sortOrder = new EntityDataSortOrder( |
||||
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), EntityDataSortOrder.Direction.DESC |
||||
|
); |
||||
|
|
||||
|
pageLink = new EntityDataPageLink(10, 0, "device1", sortOrder); |
||||
|
query = new EntityDataQuery(filter, pageLink, entityFields, null, null); |
||||
|
data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); |
||||
|
Assert.assertEquals(11, data.getTotalElements()); |
||||
|
Assert.assertEquals("Device19", data.getData().get(0).getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.dao.service.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.BaseEntityServiceTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class EntityServiceSqlTest extends BaseEntityServiceTest { |
||||
|
} |
||||
Loading…
Reference in new issue