diff --git a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java index 0e4b5623cb267e8b6d3b7c13b780bd6e44fa9672..d35d7ceacb0a1d7ac9463e4062a1f6bce4c31e72 100644 --- a/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java +++ b/BudgetMasterServer/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionSearchSpecifications.java @@ -11,6 +11,7 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class TransactionSearchSpecifications @@ -22,30 +23,63 @@ public class TransactionSearchSpecifications public static Specification<Transaction> withDynamicQuery(final Search search) { return (transaction, query, builder) -> { - final String pattern = "%" + search.getSearchText().toLowerCase() + "%"; + final String searchText = search.getSearchText().toLowerCase(); + final List<String> searchTextParts = Arrays.stream(searchText.split(" ")) + .map(part -> "%" + part + "%") + .toList(); List<Predicate> predicates = new ArrayList<>(); if(search.isSearchName()) { - predicates.add(builder.like(builder.lower(transaction.get(Transaction_.name)), pattern)); + final List<Predicate> predicatesName = new ArrayList<>(); + for(String part : searchTextParts) + { + predicatesName.add(builder.like(builder.lower(transaction.get(Transaction_.name)), part)); + } + + final Predicate[] predicatesArray = new Predicate[predicatesName.size()]; + predicates.add(builder.and(predicatesName.toArray(predicatesArray))); } if(search.isSearchDescription()) { - predicates.add(builder.like(builder.lower(transaction.get(Transaction_.description)), pattern)); + final List<Predicate> predicatesDescription = new ArrayList<>(); + for(String part : searchTextParts) + { + predicatesDescription.add(builder.like(builder.lower(transaction.get(Transaction_.description)), part)); + } + + final Predicate[] predicatesArray = new Predicate[predicatesDescription.size()]; + predicates.add(builder.and(predicatesDescription.toArray(predicatesArray))); } if(search.isSearchCategory()) { + final List<Predicate> predicatesCategories = new ArrayList<>(); + Join<Transaction, Category> categoryJoin = transaction.join(Transaction_.category, JoinType.INNER); - predicates.add(builder.like(builder.lower(categoryJoin.get("name").as(String.class)), pattern)); + for(String part : searchTextParts) + { + predicatesCategories.add(builder.like(builder.lower(categoryJoin.get("name").as(String.class)), part)); + } + + final Predicate[] predicatesArray = new Predicate[predicatesCategories.size()]; + predicates.add(builder.and(predicatesCategories.toArray(predicatesArray))); } if(search.isSearchTags()) { + final List<Predicate> predicatesTags = new ArrayList<>(); Join<Transaction, Tag> tagJoin = transaction.join(Transaction_.tags, JoinType.LEFT); - predicates.add(builder.like(builder.lower(tagJoin.get(Tag_.name).as(String.class)), pattern)); + + for(String part : searchTextParts) + { + predicatesTags.add(builder.like(builder.lower(tagJoin.get(Tag_.name).as(String.class)), part)); + } + + final Predicate[] predicatesArray = new Predicate[predicatesTags.size()]; + predicates.add(builder.and(predicatesTags.toArray(predicatesArray))); } Predicate[] predicatesArray = new Predicate[predicates.size()]; diff --git a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java index 2cf8e50f05983bd402d3ff36eb35d77f5e788eec..125494f27dd605e1d770f0c51c9b70fc1c34aec0 100644 --- a/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java +++ b/BudgetMasterServer/src/test/java/de/deadlocker8/budgetmaster/unit/TransactionSearchSpecificationsTest.java @@ -324,4 +324,15 @@ class TransactionSearchSpecificationsTest List<Transaction> results = transactionRepository.findAll(spec); assertThat(results).containsExactly(transactionWithMultipleTags, repeatingTransaction); } + + @Test + void getMatches_IgnoreOrderOfSearchWords() + { + Search search = new Search("TagMaster I the am", true, false, false, false, false, 0); + Specification spec = TransactionSearchSpecifications.withDynamicQuery(search); + + List<Transaction> results = transactionRepository.findAll(spec); + assertThat(results).hasSize(1) + .contains(transactionWithMultipleTags); + } } \ No newline at end of file