46 changed files with 1745 additions and 20 deletions
@ -0,0 +1,65 @@ |
|||
/** |
|||
* 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.controller; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.query.EntityQueryService; |
|||
|
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequestMapping("/api") |
|||
public class EntityQueryController extends BaseController { |
|||
|
|||
@Autowired |
|||
private EntityQueryService entityQueryService; |
|||
|
|||
|
|||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
|||
@RequestMapping(value = "/entitiesQuery/count", method = RequestMethod.POST) |
|||
@ResponseBody |
|||
public long countEntitiesByQuery(@RequestBody EntityCountQuery query) throws ThingsboardException { |
|||
checkNotNull(query); |
|||
try { |
|||
return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
|||
@RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST) |
|||
@ResponseBody |
|||
public PageData<EntityData> findEntityDataByQuery(@RequestBody EntityDataQuery query) throws ThingsboardException { |
|||
checkNotNull(query); |
|||
try { |
|||
return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* 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.service.query; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
import org.thingsboard.server.dao.entity.EntityService; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
@TbCoreComponent |
|||
public class DefaultEntityQueryService implements EntityQueryService { |
|||
|
|||
@Autowired |
|||
private EntityService entityService; |
|||
|
|||
@Override |
|||
public long countEntitiesByQuery(SecurityUser securityUser, EntityCountQuery query) { |
|||
return entityService.countEntitiesByQuery(securityUser.getTenantId(), securityUser.getCustomerId(), query); |
|||
} |
|||
|
|||
@Override |
|||
public PageData<EntityData> findEntityDataByQuery(SecurityUser securityUser, EntityDataQuery query) { |
|||
return entityService.findEntityDataByQuery(securityUser.getTenantId(), securityUser.getCustomerId(), query); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* 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.service.query; |
|||
|
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
public interface EntityQueryService { |
|||
|
|||
long countEntitiesByQuery(SecurityUser securityUser, EntityCountQuery query); |
|||
|
|||
PageData<EntityData> findEntityDataByQuery(SecurityUser securityUser, EntityDataQuery query); |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class AssetSearchQueryFilter extends EntitySearchQueryFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ASSET_SEARCH_QUERY; |
|||
} |
|||
|
|||
private List<String> assetTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class AssetTypeFilter implements EntityFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ASSET_TYPE; |
|||
} |
|||
|
|||
private String assetType; |
|||
|
|||
private String assetNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class BooleanFilterPredicate implements KeyFilterPredicate { |
|||
|
|||
private BooleanOperation operation; |
|||
private boolean value; |
|||
|
|||
@Override |
|||
public FilterPredicateType getType() { |
|||
return FilterPredicateType.BOOLEAN; |
|||
} |
|||
|
|||
public enum BooleanOperation { |
|||
EQUAL, |
|||
NOT_EQUAL |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class ComplexFilterPredicate implements KeyFilterPredicate { |
|||
|
|||
private ComplexOperation operation; |
|||
private List<KeyFilterPredicate> predicates; |
|||
|
|||
@Override |
|||
public FilterPredicateType getType() { |
|||
return FilterPredicateType.COMPLEX; |
|||
} |
|||
|
|||
public enum ComplexOperation { |
|||
AND, |
|||
OR |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class DeviceSearchQueryFilter extends EntitySearchQueryFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.DEVICE_SEARCH_QUERY; |
|||
} |
|||
|
|||
private List<String> deviceTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class DeviceTypeFilter implements EntityFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.DEVICE_TYPE; |
|||
} |
|||
|
|||
private String deviceType; |
|||
|
|||
private String deviceNameFilter; |
|||
|
|||
} |
|||
@ -1,13 +1,27 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.page.SortOrder; |
|||
|
|||
@Data |
|||
public class EntityDataPageLink { |
|||
|
|||
private final int pageSize; |
|||
private final int page; |
|||
private final SortOrder sortOrder; |
|||
private final EntityDataSortOrder sortOrder; |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class EntityDataSortOrder { |
|||
|
|||
private final EntityKey key; |
|||
private final Direction direction; |
|||
|
|||
public EntityDataSortOrder(EntityKey key) { |
|||
this(key, Direction.ASC); |
|||
} |
|||
|
|||
public EntityDataSortOrder(EntityKey key, Direction direction) { |
|||
this.key = key; |
|||
this.direction = direction; |
|||
} |
|||
|
|||
public enum Direction { |
|||
ASC, DESC |
|||
} |
|||
|
|||
} |
|||
@ -1,9 +1,25 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
public enum EntityKeyType { |
|||
ATTRIBUTE, |
|||
CLIENT_ATTRIBUTE, |
|||
SHARED_ATTRIBUTE, |
|||
SERVER_ATTRIBUTE, |
|||
TIMESERIES, |
|||
TIME_SERIES, |
|||
ENTITY_FIELD; |
|||
} |
|||
|
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class EntityListFilter implements EntityFilter { |
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ENTITY_LIST; |
|||
} |
|||
|
|||
private EntityType entityType; |
|||
|
|||
private List<String> entityList; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
public class EntityNameFilter implements EntityFilter { |
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ENTITY_NAME; |
|||
} |
|||
|
|||
private EntityType entityType; |
|||
|
|||
private String entityNameFilter; |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
|||
|
|||
@Data |
|||
public abstract class EntitySearchQueryFilter implements EntityFilter { |
|||
|
|||
private EntityId rootEntity; |
|||
private String relationType; |
|||
private EntitySearchDirection direction; |
|||
private int maxLevel; |
|||
private boolean fetchLastLevelOnly; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class EntityViewSearchQueryFilter extends EntitySearchQueryFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ENTITY_VIEW_SEARCH_QUERY; |
|||
} |
|||
|
|||
private List<String> entityViewTypes; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class EntityViewTypeFilter implements EntityFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.ENTITY_VIEW_TYPE; |
|||
} |
|||
|
|||
private String entityViewType; |
|||
|
|||
private String entityViewNameFilter; |
|||
|
|||
} |
|||
@ -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.common.data.query; |
|||
|
|||
public enum FilterPredicateType { |
|||
STRING, |
|||
NUMERIC, |
|||
BOOLEAN, |
|||
COMPLEX |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class KeyFilter { |
|||
|
|||
private EntityKey key; |
|||
private KeyFilterPredicate predicate; |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
public interface KeyFilterPredicate { |
|||
|
|||
FilterPredicateType getType(); |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class NumericFilterPredicate implements KeyFilterPredicate { |
|||
|
|||
private NumericOperation operation; |
|||
private double value; |
|||
|
|||
@Override |
|||
public FilterPredicateType getType() { |
|||
return FilterPredicateType.NUMERIC; |
|||
} |
|||
|
|||
public enum NumericOperation { |
|||
EQUAL, |
|||
NOT_EQUAL, |
|||
GREATER, |
|||
LESS, |
|||
GREATER_OR_EQUAL, |
|||
LESS_OR_EQUAL |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
|||
import org.thingsboard.server.common.data.relation.EntityTypeFilter; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class RelationsQueryFilter implements EntityFilter { |
|||
|
|||
@Override |
|||
public EntityFilterType getType() { |
|||
return EntityFilterType.RELATIONS_QUERY; |
|||
} |
|||
|
|||
private EntityId rootEntity; |
|||
private EntitySearchDirection direction; |
|||
private List<EntityTypeFilter> filters; |
|||
private int maxLevel; |
|||
private boolean fetchLastLevelOnly; |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* 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.common.data.query; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class StringFilterPredicate implements KeyFilterPredicate { |
|||
|
|||
private StringOperation operation; |
|||
private String value; |
|||
private boolean ignoreCase; |
|||
|
|||
@Override |
|||
public FilterPredicateType getType() { |
|||
return FilterPredicateType.STRING; |
|||
} |
|||
|
|||
public enum StringOperation { |
|||
EQUAL, |
|||
NOT_EQUAL, |
|||
STARTS_WITH, |
|||
ENDS_WITH, |
|||
CONTAINS, |
|||
NOT_CONTAINS |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* 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.entity; |
|||
|
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
|
|||
public interface EntityQueryDao { |
|||
|
|||
long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query); |
|||
|
|||
PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query); |
|||
|
|||
} |
|||
@ -0,0 +1,551 @@ |
|||
/** |
|||
* 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 lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.stereotype.Repository; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.UUIDConverter; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.AssetTypeFilter; |
|||
import org.thingsboard.server.common.data.query.BooleanFilterPredicate; |
|||
import org.thingsboard.server.common.data.query.ComplexFilterPredicate; |
|||
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.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 org.thingsboard.server.common.data.query.EntityNameFilter; |
|||
import org.thingsboard.server.common.data.query.EntityViewTypeFilter; |
|||
import org.thingsboard.server.common.data.query.FilterPredicateType; |
|||
import org.thingsboard.server.common.data.query.KeyFilter; |
|||
import org.thingsboard.server.common.data.query.KeyFilterPredicate; |
|||
import org.thingsboard.server.common.data.query.NumericFilterPredicate; |
|||
import org.thingsboard.server.common.data.query.SingleEntityFilter; |
|||
import org.thingsboard.server.common.data.query.StringFilterPredicate; |
|||
import org.thingsboard.server.common.data.query.TsValue; |
|||
import org.thingsboard.server.dao.util.SqlDao; |
|||
|
|||
import javax.persistence.EntityManager; |
|||
import javax.persistence.PersistenceContext; |
|||
import java.math.BigInteger; |
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@SqlDao |
|||
@Repository |
|||
@Slf4j |
|||
public class DefaultEntityQueryRepository implements EntityQueryRepository { |
|||
|
|||
private static final Map<String, String> entityFieldColumnMap = new HashMap<>(); |
|||
static { |
|||
entityFieldColumnMap.put("createdTime", "id"); |
|||
entityFieldColumnMap.put("name", "name"); |
|||
entityFieldColumnMap.put("type", "type"); |
|||
entityFieldColumnMap.put("label", "label"); |
|||
entityFieldColumnMap.put("firstName", "first_name"); |
|||
entityFieldColumnMap.put("lastName", "last_name"); |
|||
entityFieldColumnMap.put("email", "email"); |
|||
entityFieldColumnMap.put("title", "title"); |
|||
entityFieldColumnMap.put("country", "country"); |
|||
entityFieldColumnMap.put("state", "state"); |
|||
entityFieldColumnMap.put("city", "city"); |
|||
entityFieldColumnMap.put("address", "address"); |
|||
entityFieldColumnMap.put("address2", "address2"); |
|||
entityFieldColumnMap.put("zip", "zip"); |
|||
entityFieldColumnMap.put("phone", "phone"); |
|||
} |
|||
|
|||
private static final Map<EntityType, String> entityTableMap = new HashMap<>(); |
|||
static { |
|||
entityTableMap.put(EntityType.ASSET, "asset"); |
|||
entityTableMap.put(EntityType.DEVICE, "device"); |
|||
entityTableMap.put(EntityType.ENTITY_VIEW, "entity_view"); |
|||
entityTableMap.put(EntityType.DASHBOARD, "dashboard"); |
|||
entityTableMap.put(EntityType.CUSTOMER, "customer"); |
|||
entityTableMap.put(EntityType.USER, "tb_user"); |
|||
entityTableMap.put(EntityType.TENANT, "tenant"); |
|||
} |
|||
|
|||
@PersistenceContext |
|||
private EntityManager entityManager; |
|||
|
|||
@Override |
|||
public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
|||
EntityType entityType = resolveEntityType(query.getEntityFilter()); |
|||
String countQuery = String.format("select count(e.id) from %s e where %s", |
|||
entityTableMap.get(entityType), this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), |
|||
Collections.emptyList(), entityType)); |
|||
return ((BigInteger)entityManager.createNativeQuery(countQuery) |
|||
.getSingleResult()).longValue(); |
|||
} |
|||
|
|||
@Override |
|||
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { |
|||
EntityType entityType = resolveEntityType(query.getEntityFilter()); |
|||
|
|||
List<EntityKeyMapping> mappings = prepareKeyMapping(query); |
|||
|
|||
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(mapping -> mapping.isSelection()) |
|||
.collect(Collectors.toList()); |
|||
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) |
|||
.collect(Collectors.toList()); |
|||
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(mapping -> mapping.isLatest()) |
|||
.collect(Collectors.toList()); |
|||
|
|||
List<EntityKeyMapping> filterMapping = mappings.stream().filter(mapping -> mapping.hasFilter()) |
|||
.collect(Collectors.toList()); |
|||
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) |
|||
.collect(Collectors.toList()); |
|||
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(mapping -> mapping.isLatest()) |
|||
.collect(Collectors.toList()); |
|||
|
|||
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(mapping -> mapping.isLatest()) |
|||
.collect(Collectors.toList()); |
|||
|
|||
|
|||
String entityWhereClause = this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType); |
|||
String latestJoins = this.buildLatestJoins(entityType, allLatestMappings); |
|||
String latestFilters = this.buildLatestQuery(latestFiltersMapping); |
|||
|
|||
String countQuery = String.format("select count(*) from (select e.id from %s e where %s) entities %s", |
|||
entityTableMap.get(entityType), entityWhereClause, latestJoins); |
|||
if (!StringUtils.isEmpty(latestFilters)) { |
|||
countQuery = String.format("%s where %s", countQuery, latestFilters); |
|||
} |
|||
int totalElements = ((BigInteger)entityManager.createNativeQuery(countQuery) |
|||
.getSingleResult()).intValue(); |
|||
|
|||
String entityFieldsSelection = this.buildEntityFieldsSelection(entityFieldsSelectionMapping); |
|||
if (!StringUtils.isEmpty(entityFieldsSelection)) { |
|||
entityFieldsSelection = String.format("e.id, %s", entityFieldsSelection); |
|||
} else { |
|||
entityFieldsSelection = "e.id"; |
|||
} |
|||
String latestSelection = this.buildLatestSelections(latestSelectionMapping); |
|||
String topSelection = "entities.*"; |
|||
if (!StringUtils.isEmpty(latestSelection)) { |
|||
topSelection = topSelection + ", " + latestSelection; |
|||
} |
|||
|
|||
String dataQuery = String.format("select %s from (select %s from %s e where %s) entities %s", topSelection, |
|||
entityFieldsSelection, |
|||
entityTableMap.get(entityType), |
|||
entityWhereClause, |
|||
latestJoins); |
|||
|
|||
if (!StringUtils.isEmpty(latestFilters)) { |
|||
dataQuery = String.format("%s where %s", dataQuery, latestFilters); |
|||
} |
|||
|
|||
EntityDataPageLink pageLink = query.getPageLink(); |
|||
|
|||
// TODO: order by
|
|||
|
|||
int startIndex = pageLink.getPageSize() * pageLink.getPage(); |
|||
if (pageLink.getPageSize() > 0) { |
|||
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); |
|||
} |
|||
List result = entityManager.createNativeQuery(dataQuery).getResultList(); |
|||
int totalPages = pageLink.getPageSize() > 0 ? (int)Math.ceil((float)totalElements / pageLink.getPageSize()) : 1; |
|||
boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + result.size(); |
|||
List<EntityData> entitiesData = convertListToEntityData(result, entityType, selectionMapping); |
|||
return new PageData<>(entitiesData, totalPages, totalElements, hasNext); |
|||
} |
|||
|
|||
private List<EntityData> convertListToEntityData(List<Object> result, EntityType entityType, List<EntityKeyMapping> selectionMapping) { |
|||
return result.stream().map(obj -> this.toEntityData(obj, entityType, selectionMapping)).collect(Collectors.toList()); |
|||
} |
|||
|
|||
private EntityData toEntityData(Object obj, EntityType entityType, List<EntityKeyMapping> selectionMapping) { |
|||
String id = obj instanceof String ? (String)obj : (String)((Object[]) obj)[0]; |
|||
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) { |
|||
Object value = ((Object[]) obj)[mapping.getIndex()]; |
|||
// TODO:
|
|||
} |
|||
return entityData; |
|||
} |
|||
|
|||
private List<EntityKeyMapping> prepareKeyMapping(EntityDataQuery query) { |
|||
List<EntityKey> entityFields = query.getEntityFields() != null ? query.getEntityFields() : Collections.emptyList(); |
|||
List<EntityKey> latestValues = query.getLatestValues() != null ? query.getLatestValues() : Collections.emptyList(); |
|||
Map<EntityKey, List<KeyFilter>> filters = |
|||
query.getKeyFilters() != null ? |
|||
query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap(); |
|||
int index = 1; |
|||
List<EntityKeyMapping> mappings = new ArrayList<>(); |
|||
for (EntityKey entityField : entityFields) { |
|||
EntityKeyMapping mapping = new EntityKeyMapping(); |
|||
mapping.setIndex(index); |
|||
mapping.setAlias(String.format("alias%s", index)); |
|||
mapping.setKeyFilters(filters.remove(entityField)); |
|||
mapping.setLatest(false); |
|||
mapping.setSelection(true); |
|||
mapping.setEntityKey(entityField); |
|||
mappings.add(mapping); |
|||
index++; |
|||
} |
|||
for (EntityKey latestField : latestValues) { |
|||
EntityKeyMapping mapping = new EntityKeyMapping(); |
|||
mapping.setIndex(index); |
|||
mapping.setAlias(String.format("alias%s", index)); |
|||
mapping.setKeyFilters(filters.remove(latestField)); |
|||
mapping.setLatest(true); |
|||
mapping.setSelection(true); |
|||
mapping.setEntityKey(latestField); |
|||
mappings.add(mapping); |
|||
index +=2; |
|||
} |
|||
if (!filters.isEmpty()) { |
|||
for (EntityKey filterField : filters.keySet()) { |
|||
EntityKeyMapping mapping = new EntityKeyMapping(); |
|||
mapping.setIndex(index); |
|||
mapping.setAlias(String.format("alias%s", index)); |
|||
mapping.setKeyFilters(filters.get(filterField)); |
|||
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD)); |
|||
mapping.setSelection(false); |
|||
mapping.setEntityKey(filterField); |
|||
mappings.add(mapping); |
|||
index +=1; |
|||
} |
|||
} |
|||
return mappings; |
|||
} |
|||
|
|||
private String buildEntityFieldsSelection(List<EntityKeyMapping> entityFieldsSelectionMapping) { |
|||
return entityFieldsSelectionMapping.stream().map(mapping -> { |
|||
String column = entityFieldColumnMap.get(mapping.getEntityKey().getKey()); |
|||
return String.format("e.%s as %s", column, mapping.getAlias()); |
|||
}).collect( |
|||
Collectors.joining(", ")); |
|||
} |
|||
|
|||
private String buildLatestSelections(List<EntityKeyMapping> latestSelectionMapping) { |
|||
return latestSelectionMapping.stream().map(mapping -> this.buildLatestSelection(mapping)) |
|||
.collect( |
|||
Collectors.joining(", ")); |
|||
} |
|||
|
|||
private String buildLatestSelection(EntityKeyMapping mapping) { |
|||
if (mapping.getEntityKey().getType().equals(EntityKeyType.TIME_SERIES)) { |
|||
return buildTimeseriesSelection(mapping); |
|||
} else { |
|||
return buildAttributeSelection(mapping); |
|||
} |
|||
} |
|||
|
|||
private String buildAttributeSelection(EntityKeyMapping mapping) { |
|||
String alias = mapping.getAlias(); |
|||
String attrValAlias = alias + "_value"; |
|||
String attrTsAlias = alias + "_ts"; |
|||
String attrTsSelection = String.format("%s.last_update_ts as %s", alias, attrTsAlias); |
|||
String attrValSelection = |
|||
String.format("coalesce(cast(%s.bool_v as varchar), '') || " + |
|||
"coalesce(%s.str_v, '') || " + |
|||
"coalesce(cast(%s.long_v as varchar), '') || " + |
|||
"coalesce(cast(%s.dbl_v as varchar), '') || " + |
|||
"coalesce(cast(%s.json_v as varchar), '')) as %s", alias, alias, alias, alias, alias, attrValAlias); |
|||
return String.join(", ", attrTsSelection, attrValSelection); |
|||
} |
|||
|
|||
private String buildTimeseriesSelection(EntityKeyMapping mapping) { |
|||
// TODO:
|
|||
String alias = mapping.getAlias(); |
|||
String attrValAlias = alias + "_value"; |
|||
String attrTsAlias = alias + "_ts"; |
|||
return String.format("(select 1) as %s, (select '') as %s", attrTsAlias, attrValAlias); |
|||
} |
|||
|
|||
private String buildLatestJoins(EntityType entityType, List<EntityKeyMapping> latestMappings) { |
|||
return latestMappings.stream().map(mapping -> this.buildLatestJoin(entityType, mapping)).collect( |
|||
Collectors.joining(" ")); |
|||
} |
|||
|
|||
private String buildLatestJoin(EntityType entityType, EntityKeyMapping mapping) { |
|||
String join = mapping.hasFilter() ? "left join" : "left outer join"; |
|||
if (mapping.getEntityKey().getType().equals(EntityKeyType.TIME_SERIES)) { |
|||
// TODO:
|
|||
throw new RuntimeException("Not implemented!"); |
|||
} else { |
|||
String alias = mapping.getAlias(); |
|||
String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type='%s' AND %s.attribute_key='%s'", |
|||
join, alias, alias, alias, entityType.name(), alias, mapping.getEntityKey().getKey()); |
|||
if (!mapping.getEntityKey().getType().equals(EntityKeyType.ATTRIBUTE)) { |
|||
String scope; |
|||
if (mapping.getEntityKey().getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) { |
|||
scope = DataConstants.CLIENT_SCOPE; |
|||
} else if (mapping.getEntityKey().getType().equals(EntityKeyType.SHARED_ATTRIBUTE)) { |
|||
scope = DataConstants.SHARED_SCOPE; |
|||
} else { |
|||
scope = DataConstants.SERVER_SCOPE; |
|||
} |
|||
query = String.format("%s AND %s.attribute_type=%s", query, alias, scope); |
|||
} |
|||
return query; |
|||
} |
|||
} |
|||
|
|||
private String buildEntityWhere(TenantId tenantId, |
|||
CustomerId customerId, |
|||
EntityFilter entityFilter, |
|||
List<EntityKeyMapping> entityFieldsFilters, |
|||
EntityType entityType) { |
|||
String permissionQuery = this.buildPermissionQuery(tenantId, customerId, entityType); |
|||
String entityFilterQuery = this.buildEntityFilterQuery(entityFilter); |
|||
if (!entityFieldsFilters.isEmpty()) { |
|||
String entityFieldsQuery = this.buildEntityFieldsQuery(entityFieldsFilters); |
|||
return String.join(" and ", permissionQuery, entityFilterQuery, entityFieldsQuery); |
|||
} else { |
|||
return String.join(" and ", permissionQuery, entityFilterQuery); |
|||
} |
|||
} |
|||
|
|||
private String buildPermissionQuery(TenantId tenantId, CustomerId customerId, EntityType entityType) { |
|||
String permissionQuery = String.format("e.tenant_id=%s", UUIDConverter.fromTimeUUID(tenantId.getId())); |
|||
if (entityType != EntityType.TENANT && entityType != EntityType.CUSTOMER) { |
|||
permissionQuery = String.format("%s and e.customerId=%s", permissionQuery, UUIDConverter.fromTimeUUID(customerId.getId())); |
|||
} |
|||
return permissionQuery; |
|||
} |
|||
|
|||
private String buildEntityFilterQuery(EntityFilter entityFilter) { |
|||
switch (entityFilter.getType()) { |
|||
case SINGLE_ENTITY: |
|||
return this.singleEntityQuery((SingleEntityFilter) entityFilter); |
|||
case ENTITY_LIST: |
|||
return this.entityListQuery((EntityListFilter) entityFilter); |
|||
case ENTITY_NAME: |
|||
return this.entityNameQuery((EntityNameFilter) entityFilter); |
|||
case ASSET_TYPE: |
|||
case DEVICE_TYPE: |
|||
case ENTITY_VIEW_TYPE: |
|||
return this.typeQuery(entityFilter); |
|||
default: |
|||
throw new RuntimeException("Not implemented!"); |
|||
} |
|||
} |
|||
|
|||
private String buildLatestQuery(List<EntityKeyMapping> latestFilters) { |
|||
List<String> latestQueries = new ArrayList<>(); |
|||
for (EntityKeyMapping mapping: latestFilters) { |
|||
latestQueries.addAll(mapping.getKeyFilters().stream().map(keyFilter -> |
|||
this.buildKeyQuery(mapping.getAlias(), keyFilter)) |
|||
.collect(Collectors.toList())); |
|||
} |
|||
return latestQueries.stream().collect(Collectors.joining(" AND ")); |
|||
} |
|||
|
|||
private String buildEntityFieldsQuery(List<EntityKeyMapping> entityFieldsFilters) { |
|||
return entityFieldsFilters.stream().flatMap(mapping -> mapping.getKeyFilters().stream()) |
|||
.map(keyFilter -> this.buildKeyQuery("e", keyFilter)).collect( |
|||
Collectors.joining(" AND ") |
|||
); |
|||
} |
|||
|
|||
private String buildKeyQuery(String alias, KeyFilter keyFilter) { |
|||
return this.buildPredicateQuery(alias, keyFilter.getKey(), keyFilter.getPredicate()); |
|||
} |
|||
|
|||
private String buildPredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) { |
|||
if (predicate.getType().equals(FilterPredicateType.COMPLEX)) { |
|||
return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate)predicate); |
|||
} else { |
|||
return this.buildSimplePredicateQuery(alias, key, predicate); |
|||
} |
|||
} |
|||
|
|||
private String buildComplexPredicateQuery(String alias, EntityKey key, ComplexFilterPredicate predicate) { |
|||
return predicate.getPredicates().stream() |
|||
.map(keyFilterPredicate -> this.buildPredicateQuery(alias, key, keyFilterPredicate)).collect(Collectors.joining( |
|||
" " + predicate.getOperation().name() + " " |
|||
)); |
|||
} |
|||
|
|||
private String buildSimplePredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) { |
|||
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) { |
|||
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) { |
|||
String column = entityFieldColumnMap.get(key.getKey()); |
|||
return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate)predicate); |
|||
} else { |
|||
String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate)predicate); |
|||
String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate)predicate); |
|||
return String.format("(%s or %s)", longQuery, doubleQuery); |
|||
} |
|||
} else { |
|||
String column; |
|||
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) { |
|||
column = entityFieldColumnMap.get(key.getKey()); |
|||
} else { |
|||
column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v"; |
|||
} |
|||
String field = alias + "." + column; |
|||
if (predicate.getType().equals(FilterPredicateType.STRING)) { |
|||
return this.buildStringPredicateQuery(field, (StringFilterPredicate)predicate); |
|||
} else { |
|||
return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate)predicate); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private String buildStringPredicateQuery(String field, StringFilterPredicate stringFilterPredicate) { |
|||
String operationField = field; |
|||
String value = stringFilterPredicate.getValue(); |
|||
String stringOperationQuery = ""; |
|||
if (stringFilterPredicate.isIgnoreCase()) { |
|||
value.toLowerCase(); |
|||
operationField = String.format("lower(%s)", operationField); |
|||
} |
|||
switch (stringFilterPredicate.getOperation()) { |
|||
case EQUAL: |
|||
stringOperationQuery = String.format("%s = '%s'", operationField, value); |
|||
break; |
|||
case NOT_EQUAL: |
|||
stringOperationQuery = String.format("%s != '%s'", operationField, value); |
|||
break; |
|||
case STARTS_WITH: |
|||
stringOperationQuery = String.format("%s like '%s%'", operationField, value); |
|||
break; |
|||
case ENDS_WITH: |
|||
stringOperationQuery = String.format("%s like '%%s'", operationField, value); |
|||
break; |
|||
case CONTAINS: |
|||
stringOperationQuery = String.format("%s like '%%s%'", operationField, value); |
|||
break; |
|||
case NOT_CONTAINS: |
|||
stringOperationQuery = String.format("%s not like '%%s%'", operationField, value); |
|||
break; |
|||
} |
|||
return String.format("(%s is not null and %s)", field, stringOperationQuery); |
|||
} |
|||
|
|||
private String buildNumericPredicateQuery(String field, NumericFilterPredicate numericFilterPredicate) { |
|||
double value = numericFilterPredicate.getValue(); |
|||
String numericOperationQuery = ""; |
|||
switch (numericFilterPredicate.getOperation()) { |
|||
case EQUAL: |
|||
numericOperationQuery = String.format("%s = %s", field, value); |
|||
break; |
|||
case NOT_EQUAL: |
|||
numericOperationQuery = String.format("%s != '%s'", field, value); |
|||
break; |
|||
case GREATER: |
|||
numericOperationQuery = String.format("%s > %s", field, value); |
|||
break; |
|||
case GREATER_OR_EQUAL: |
|||
numericOperationQuery = String.format("%s >= %s", field, value); |
|||
break; |
|||
case LESS: |
|||
numericOperationQuery = String.format("%s < %s", field, value); |
|||
break; |
|||
case LESS_OR_EQUAL: |
|||
numericOperationQuery = String.format("%s <= %s", field, value); |
|||
break; |
|||
} |
|||
return String.format("(%s is not null and %s)", field, numericOperationQuery); |
|||
} |
|||
|
|||
private String buildBooleanPredicateQuery(String field, |
|||
BooleanFilterPredicate booleanFilterPredicate) { |
|||
boolean value = booleanFilterPredicate.isValue(); |
|||
String booleanOperationQuery = ""; |
|||
switch (booleanFilterPredicate.getOperation()) { |
|||
case EQUAL: |
|||
booleanOperationQuery = String.format("%s = %s", field, value); |
|||
break; |
|||
case NOT_EQUAL: |
|||
booleanOperationQuery = String.format("%s != %s", field, value); |
|||
break; |
|||
} |
|||
return String.format("(%s is not null and %s)", field, booleanOperationQuery); |
|||
} |
|||
|
|||
private String singleEntityQuery(SingleEntityFilter filter) { |
|||
return String.format("e.id=%s", UUIDConverter.fromTimeUUID(filter.getSingleEntity().getId())); |
|||
} |
|||
|
|||
private String entityListQuery(EntityListFilter filter) { |
|||
return String.format("e.id in (%s)", |
|||
filter.getEntityList().stream().map(UUID::fromString).map(UUIDConverter::fromTimeUUID).collect(Collectors.joining(","))); |
|||
} |
|||
|
|||
private String entityNameQuery(EntityNameFilter filter) { |
|||
return String.format("lower(e.searchText) like lower(concat(%s, '%'))", filter.getEntityNameFilter()); |
|||
} |
|||
|
|||
private String typeQuery(EntityFilter filter) { |
|||
String type; |
|||
String name; |
|||
switch (filter.getType()) { |
|||
case ASSET_TYPE: |
|||
type = ((AssetTypeFilter)filter).getAssetType(); |
|||
name = ((AssetTypeFilter)filter).getAssetNameFilter(); |
|||
break; |
|||
case DEVICE_TYPE: |
|||
type = ((DeviceTypeFilter)filter).getDeviceType(); |
|||
name = ((DeviceTypeFilter)filter).getDeviceNameFilter(); |
|||
break; |
|||
case ENTITY_VIEW_TYPE: |
|||
type = ((EntityViewTypeFilter)filter).getEntityViewType(); |
|||
name = ((EntityViewTypeFilter)filter).getEntityViewNameFilter(); |
|||
break; |
|||
default: |
|||
throw new RuntimeException("Not supported!"); |
|||
} |
|||
return String.format("e.type = %s and lower(e.searchText) like lower(concat(%s, '%'))", type, name); |
|||
} |
|||
|
|||
private EntityType resolveEntityType(EntityFilter entityFilter) { |
|||
switch (entityFilter.getType()) { |
|||
case SINGLE_ENTITY: |
|||
return ((SingleEntityFilter)entityFilter).getSingleEntity().getEntityType(); |
|||
case ENTITY_LIST: |
|||
return ((EntityListFilter)entityFilter).getEntityType(); |
|||
case ENTITY_NAME: |
|||
return ((EntityNameFilter)entityFilter).getEntityType(); |
|||
case ASSET_TYPE: |
|||
case ASSET_SEARCH_QUERY: |
|||
return EntityType.ASSET; |
|||
case DEVICE_TYPE: |
|||
case DEVICE_SEARCH_QUERY: |
|||
return EntityType.DEVICE; |
|||
case ENTITY_VIEW_TYPE: |
|||
case ENTITY_VIEW_SEARCH_QUERY: |
|||
return EntityType.ENTITY_VIEW; |
|||
default: |
|||
throw new RuntimeException("Not implemented!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* 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 lombok.Data; |
|||
import org.thingsboard.server.common.data.query.EntityKey; |
|||
import org.thingsboard.server.common.data.query.KeyFilter; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class EntityKeyMapping { |
|||
private int index; |
|||
private String alias; |
|||
private boolean isLatest; |
|||
private boolean isSelection; |
|||
private List<KeyFilter> keyFilters; |
|||
private EntityKey entityKey; |
|||
|
|||
public boolean hasFilter() { |
|||
return keyFilters != null && !keyFilters.isEmpty(); |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* 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.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
|
|||
public interface EntityQueryRepository { |
|||
|
|||
long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query); |
|||
|
|||
PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query); |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* 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.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageData; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityData; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
import org.thingsboard.server.dao.entity.EntityQueryDao; |
|||
import org.thingsboard.server.dao.util.SqlDao; |
|||
|
|||
@Component |
|||
@SqlDao |
|||
public class JpaEntityQueryDao implements EntityQueryDao { |
|||
|
|||
@Autowired |
|||
private EntityQueryRepository entityQueryRepository; |
|||
|
|||
@Override |
|||
public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
|||
return entityQueryRepository.countEntitiesByQuery(tenantId, customerId, query); |
|||
} |
|||
|
|||
@Override |
|||
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { |
|||
return entityQueryRepository.findEntityDataByQuery(tenantId, customerId, query); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue