public Predicate<InMemoryFilterable> parse(final List<String> filterExpressions,
                                               final List<EntityAttribute> attributes) {
        if (filterExpressions == null || filterExpressions.isEmpty()) {
            return Predicates.alwaysTrue();
        }
        final Map<String, List<Filter>> groupedByField = filterExpressions.stream()
                .map(expr -> singleFilterParser.parseSingleExpression(expr, attributes))
                .collect(groupingBy(Filter::field));

        return groupedByField.values().stream()
                .map(grouped -> grouped.stream()
                        .map(Filter::toPredicate)
                        .collect(Collectors.toList()))
                .map(groupedPredicates -> groupedPredicates.stream().reduce(Predicate::or).orElse(Predicates.alwaysTrue()))
                .reduce(Predicate::and).orElse(Predicates.alwaysTrue());
    }
@Test
    void throwsExceptionOnWrongFilterFormat() {
        final List<EntityAttribute> attributes = List.of(
                EntityAttribute.builder().id("good").title("Good").filterable(true).build(),
                EntityAttribute.builder().id("another").title("Hidden and dangerous").filterable(true).build()
        );
        assertThrows(IllegalArgumentException.class, () -> toTest.parse(List.of("No separator"), attributes));
        assertThrows(IllegalArgumentException.class, () -> toTest.parse(List.of(FIELD_AND_VALUE_SEPARATOR + "no field name"), attributes));
        assertThrows(IllegalArgumentException.class, () -> toTest.parse(List.of("no field value" + FIELD_AND_VALUE_SEPARATOR), attributes));
        assertThrows(IllegalArgumentException.class, () -> toTest.parse(
                List.of("good" + FIELD_AND_VALUE_SEPARATOR + "one",
                        "another" + FIELD_AND_VALUE_SEPARATOR + "good_one",
                        "single wrong one is enough to throw exception"),
                attributes)
        );
    }