Commit 3420eeb9d71e6dc542004d4dc5c7f75fc2d0fe0b
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
Showing
2 changed files
with
165 additions
and
12 deletions
... | ... | @@ -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)); | ... | ... |