Commit 3420eeb9d71e6dc542004d4dc5c7f75fc2d0fe0b

Authored by Viacheslav Klimov
Committed by GitHub
1 parent bc6efa5e

[3.3] [PROD-800] Fix empty search result when query is empty (#4209)

* Fix empty search result when query is empty

* Refactor
... ... @@ -519,6 +519,9 @@ public class EntityKeyMapping {
519 519 String operationField = field;
520 520 String paramName = getNextParameterName(field);
521 521 String value = stringFilterPredicate.getValue().getValue();
  522 + if (value.isEmpty()) {
  523 + return null;
  524 + }
522 525 String stringOperationQuery = "";
523 526 if (stringFilterPredicate.isIgnoreCase()) {
524 527 value = value.toLowerCase();
... ... @@ -526,30 +529,26 @@ public class EntityKeyMapping {
526 529 }
527 530 switch (stringFilterPredicate.getOperation()) {
528 531 case EQUAL:
529   - stringOperationQuery = String.format("%s = :%s) or (%s is null and :%s = '')", operationField, paramName, operationField, paramName);
  532 + stringOperationQuery = String.format("%s = :%s)", operationField, paramName);
530 533 break;
531 534 case NOT_EQUAL:
532   - stringOperationQuery = String.format("%s != :%s) or (%s is null and :%s != '')", operationField, paramName, operationField, paramName);
  535 + stringOperationQuery = String.format("%s != :%s or %s is null)", operationField, paramName, operationField);
533 536 break;
534 537 case STARTS_WITH:
535 538 value += "%";
536   - stringOperationQuery = String.format("%s like :%s) or (%s is null and :%s = '%%')", operationField, paramName, operationField, paramName);
  539 + stringOperationQuery = String.format("%s like :%s)", operationField, paramName);
537 540 break;
538 541 case ENDS_WITH:
539 542 value = "%" + value;
540   - stringOperationQuery = String.format("%s like :%s) or (%s is null and :%s = '%%')", operationField, paramName, operationField, paramName);
  543 + stringOperationQuery = String.format("%s like :%s)", operationField, paramName);
541 544 break;
542 545 case CONTAINS:
543   - if (value.length() > 0) {
544   - value = "%" + value + "%";
545   - }
546   - stringOperationQuery = String.format("%s like :%s) or (%s is null and :%s = '')", operationField, paramName, operationField, paramName);
  546 + value = "%" + value + "%";
  547 + stringOperationQuery = String.format("%s like :%s)", operationField, paramName);
547 548 break;
548 549 case NOT_CONTAINS:
549   - if (value.length() > 0) {
550   - value = "%" + value + "%";
551   - }
552   - stringOperationQuery = String.format("%s not like :%s) or (%s is null and :%s != '')", operationField, paramName, operationField, paramName);
  550 + value = "%" + value + "%";
  551 + stringOperationQuery = String.format("%s not like :%s or %s is null)", operationField, paramName, operationField);
553 552 break;
554 553 }
555 554 ctx.addStringParameter(paramName, value);
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service;
17 17
18 18 import com.google.common.util.concurrent.Futures;
19 19 import com.google.common.util.concurrent.ListenableFuture;
  20 +import org.apache.commons.lang3.RandomUtils;
20 21 import org.junit.After;
21 22 import org.junit.Assert;
22 23 import org.junit.Before;
... ... @@ -56,6 +57,7 @@ import org.thingsboard.server.common.data.query.KeyFilter;
56 57 import org.thingsboard.server.common.data.query.NumericFilterPredicate;
57 58 import org.thingsboard.server.common.data.query.RelationsQueryFilter;
58 59 import org.thingsboard.server.common.data.query.StringFilterPredicate;
  60 +import org.thingsboard.server.common.data.query.StringFilterPredicate.StringOperation;
59 61 import org.thingsboard.server.common.data.relation.EntityRelation;
60 62 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
61 63 import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter;
... ... @@ -72,6 +74,9 @@ import java.util.List;
72 74 import java.util.Random;
73 75 import java.util.concurrent.ExecutionException;
74 76 import java.util.stream.Collectors;
  77 +import java.util.stream.Stream;
  78 +
  79 +import static org.junit.Assert.assertEquals;
75 80
76 81 public abstract class BaseEntityServiceTest extends AbstractServiceTest {
77 82
... ... @@ -542,6 +547,155 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
542 547 }
543 548
544 549 @Test
  550 + public void testFindEntityDataByQuery_operationEqual_emptySearchQuery() {
  551 + List<Device> devices = createMockDevices(10);
  552 + devices.get(0).setLabel("");
  553 + devices.get(1).setLabel(null);
  554 + devices.forEach(deviceService::saveDevice);
  555 +
  556 + String searchQuery = "";
  557 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.EQUAL, searchQuery);
  558 +
  559 + PageData<EntityData> result = searchEntities(query);
  560 + assertEquals(devices.size(), result.getTotalElements());
  561 + }
  562 +
  563 + @Test
  564 + public void testFindEntityDataByQuery_operationNotEqual() {
  565 + List<Device> devices = createMockDevices(10);
  566 + devices.get(0).setLabel("");
  567 + devices.get(1).setLabel(null);
  568 + devices.forEach(deviceService::saveDevice);
  569 +
  570 + String searchQuery = devices.get(2).getLabel();
  571 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.NOT_EQUAL, searchQuery);
  572 +
  573 + PageData<EntityData> result = searchEntities(query);
  574 + assertEquals(devices.size() - 1, result.getTotalElements());
  575 + }
  576 +
  577 + @Test
  578 + public void testFindEntityDataByQuery_operationNotEqual_emptySearchQuery() {
  579 + List<Device> devices = createMockDevices(10);
  580 + devices.get(0).setLabel("");
  581 + devices.get(1).setLabel(null);
  582 + devices.forEach(deviceService::saveDevice);
  583 +
  584 + String searchQuery = "";
  585 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.NOT_EQUAL, searchQuery);
  586 +
  587 + PageData<EntityData> result = searchEntities(query);
  588 + assertEquals(devices.size(), result.getTotalElements());
  589 + }
  590 +
  591 + @Test
  592 + public void testFindEntityDataByQuery_operationStartsWith_emptySearchQuery() {
  593 + List<Device> devices = createMockDevices(10);
  594 + devices.get(0).setLabel("");
  595 + devices.get(1).setLabel(null);
  596 + devices.forEach(deviceService::saveDevice);
  597 +
  598 + String searchQuery = "";
  599 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.STARTS_WITH, searchQuery);
  600 +
  601 + PageData<EntityData> result = searchEntities(query);
  602 + assertEquals(devices.size(), result.getTotalElements());
  603 + }
  604 +
  605 + @Test
  606 + public void testFindEntityDataByQuery_operationEndsWith_emptySearchQuery() {
  607 + List<Device> devices = createMockDevices(10);
  608 + devices.get(0).setLabel("");
  609 + devices.get(1).setLabel(null);
  610 + devices.forEach(deviceService::saveDevice);
  611 +
  612 + String searchQuery = "";
  613 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.ENDS_WITH, searchQuery);
  614 +
  615 + PageData<EntityData> result = searchEntities(query);
  616 + assertEquals(devices.size(), result.getTotalElements());
  617 + }
  618 +
  619 + @Test
  620 + public void testFindEntityDataByQuery_operationContains_emptySearchQuery() {
  621 + List<Device> devices = createMockDevices(10);
  622 + devices.get(0).setLabel("");
  623 + devices.get(1).setLabel(null);
  624 + devices.forEach(deviceService::saveDevice);
  625 +
  626 + String searchQuery = "";
  627 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.CONTAINS, searchQuery);
  628 +
  629 + PageData<EntityData> result = searchEntities(query);
  630 + assertEquals(devices.size(), result.getTotalElements());
  631 + }
  632 +
  633 + @Test
  634 + public void testFindEntityDataByQuery_operationNotContains() {
  635 + List<Device> devices = createMockDevices(10);
  636 + devices.get(0).setLabel("");
  637 + devices.get(1).setLabel(null);
  638 + devices.forEach(deviceService::saveDevice);
  639 +
  640 + String searchQuery = "label-";
  641 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.NOT_CONTAINS, searchQuery);
  642 +
  643 + PageData<EntityData> result = searchEntities(query);
  644 + assertEquals(2, result.getTotalElements());
  645 + }
  646 +
  647 + @Test
  648 + public void testFindEntityDataByQuery_operationNotContains_emptySearchQuery() {
  649 + List<Device> devices = createMockDevices(10);
  650 + devices.get(0).setLabel("");
  651 + devices.get(1).setLabel(null);
  652 + devices.forEach(deviceService::saveDevice);
  653 +
  654 + String searchQuery = "";
  655 + EntityDataQuery query = createDeviceSearchQuery("label", StringOperation.NOT_CONTAINS, searchQuery);
  656 +
  657 + PageData<EntityData> result = searchEntities(query);
  658 + assertEquals(devices.size(), result.getTotalElements());
  659 + }
  660 +
  661 + private PageData<EntityData> searchEntities(EntityDataQuery query) {
  662 + return entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  663 + }
  664 +
  665 + private EntityDataQuery createDeviceSearchQuery(String deviceField, StringOperation operation, String searchQuery) {
  666 + DeviceTypeFilter deviceTypeFilter = new DeviceTypeFilter();
  667 + deviceTypeFilter.setDeviceType("default");
  668 + deviceTypeFilter.setDeviceNameFilter("");
  669 +
  670 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  671 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  672 + );
  673 + EntityDataPageLink pageLink = new EntityDataPageLink(1000, 0, null, sortOrder);
  674 + List<EntityKey> entityFields = Arrays.asList(
  675 + new EntityKey(EntityKeyType.ENTITY_FIELD, "name"),
  676 + new EntityKey(EntityKeyType.ENTITY_FIELD, "label")
  677 + );
  678 +
  679 + List<KeyFilter> keyFilters = createStringKeyFilters(deviceField, EntityKeyType.ENTITY_FIELD, operation, searchQuery);
  680 +
  681 + return new EntityDataQuery(deviceTypeFilter, pageLink, entityFields, null, keyFilters);
  682 + }
  683 +
  684 + private List<Device> createMockDevices(int count) {
  685 + return Stream.iterate(1, i -> i + 1)
  686 + .map(i -> {
  687 + Device device = new Device();
  688 + device.setTenantId(tenantId);
  689 + device.setName("Device " + i);
  690 + device.setType("default");
  691 + device.setLabel("label-" + RandomUtils.nextInt(100, 10000));
  692 + return device;
  693 + })
  694 + .limit(count)
  695 + .collect(Collectors.toList());
  696 + }
  697 +
  698 + @Test
545 699 public void testFindEntityDataByQueryWithAttributes() throws ExecutionException, InterruptedException {
546 700
547 701 List<EntityKeyType> attributesEntityTypes = new ArrayList<>(Arrays.asList(EntityKeyType.CLIENT_ATTRIBUTE, EntityKeyType.SHARED_ATTRIBUTE, EntityKeyType.SERVER_ATTRIBUTE));
... ...