1

I have a list of sources from a JSON file stored in S3. I need to provide search filters on few fields of the list. I have created a Builder class with all the needed fields on which the data can be filtered.

Now I want to use Java streams and apply the filters on the data based on the parameters the users will provide. Users can provide all the 6 parameters or only pass 2 or 3 parameters based on their need. For example user provides only 2 parameters out of 6 I should be able to filter the data based on the 2 parameters given and ignore the 4 parameters where the value is null.

public class ListOfFields {
    private String country;
    @JsonProperty("product")
    private String product;
    private String transactionId;
    private String sourceBy;
    private String category;
    private String specialTag;
    private String organicTag;
    private String successTag;
    private String speedTag;
    private String foodTag;
    private String costPrice;
    private String fruitType;
    private String fruitVendorId;    
} 

The below is the builder class

public class ProductClass {
    @Builder.Default
    private String country = 'UnitedStates';
    private String sourceBy;
    private Boolean specialTag;
    private String category;
    private Boolean priceTag;
    private String fruitType;
}  

The below method where user can make a call to get the product

public String getProduct(ProductClass productQuery) {      
    List<ListOfFields> result = listOfFields // (the declaration is made before in the class as private List<ListOfFields> listOfFields;)
            .stream().filter(productQuery::match)
            .collect(Collectors.toList());

How do I come up with dynamic predicate as the match function above? I should be able to filter on the parameters provided by the user be it 1 or 2 or 3 or 4 ... and not using the parameters where the user did not pass any value in the filter criteria.

0

2 Answers 2

1

Considering the ProductClass as the input and null values for the absence of the keys. You can define the list of Predicates possible using a method that takes in the actual query parameter.

private static List<Predicate<ListOfFields>> predicates(ProductClass product) {
    return List.of(
            lof -> product.priceTag != null && product.priceTag,
            lof -> product.specialTag != null && product.specialTag,
            lof -> product.country != null && product.country.equals(lof.country),
            lof -> product.sourceBy != null && product.sourceBy.equals(lof.sourceBy),
            lof -> product.category != null && product.category.equals(lof.category),
            lof -> product.fruitType != null && product.fruitType.equals(lof.fruitType)
    );
}

Upon reaching this, the list could be reduced to represent a single Predicate such as:

private static Predicate<ListOfFields> matchAll(List<Predicate<ListOfFields>> queries) {
    return queries.stream()
            .reduce(Predicate::and)
            .orElse(p -> true);
}

Now one can easily make use of this predicate while streaming the objects of type ListOfFields --

List<ListOfFields> result = listOfFields.stream()
        .filter(matchAll(predicates(productQuery)))
        .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

Comments

1

You can use regular if statements for each parameter and apply filter which returns Stream.

Stream< ListOfFields> stream = listOfFields.stream();
if(first parameter exists) {
    stream = stream.filter(predicate for first parameter);
}
if(second parameter exists) {
    stream = stream.filter(predicate for second parameter);
}
...
List< ListOfFields> result = stream.collect(Collectors.toList());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.