2017-03-08 134 views
1

由多個字段篩選的最佳方式我有10場的實體:什麼是Java中

Class Details{ 
    String item; 
    String name; 
    String type; 
    String origin; 
    String color; 
    String quality; 
    String country; 
    String quantity; 
    Boolean availability; 
    String price; 
} 

我有一個寧靜的端點提供一個列表。我希望用戶能夠爲每個字段提供搜索過濾器。 目前我有QueryParam爲每個字段。然後,我用java8流過濾:

List<Detail> details; 
details.stream().filter(detail-> detail.getItem()==item).filter(detail-> detail.getName()==name).....collect(Collectors.toList()); 

如果我還有其他50個教學班,我想篩選多個領域,是有推廣這種方式?

+3

這可能是反射可能派上用場的罕見情況之一:所以不要像在你的例子中那樣手動寫下所有的屬性,你可以用反射來「按名稱」。但我想知道有多少設計進入了一個模型,導致50個班級看起來像這樣。 – GhostCat

+1

不要將字符串與'=='進行比較。使用equals方法或使用[Objects.equals](http://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#equals-java.lang.Object-java。 lang.Object-)。請參閱http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java。 – VGR

回答

1

您可以撰寫這樣的謂詞與.and().or(),使您可以定義應用於所有你想,而不是試圖鏈ň.filter()呼叫檢查一個聚合謂語。這使得可以在運行時構建任意複雜的謂詞。

// Note that you shouldn't normally use == on objects 
Predicate<Detail> itemPredicate = d-> item.equals(d.getItem()); 
Predicate<Detail> namePredicate = d-> name.equals(d.getName()); 

details.stream() 
    .filter(itemPredicate.and(namePredicate)) 
    .collect(Collectors.toList()); 
1

如果你想避免反思這樣的事情呢?

static enum DetailQueryParams { 

    ITEM("item", d -> d.item), 
    NAME("name", d -> d.name), 
    TYPE("type", d -> d.type), 
    ORIGIN("origin", d -> d.origin), 
    COLOR("color", d -> d.color), 
    QUALITY("quality", d -> d.quality), 
    COUNTRY("country", d -> d.country), 
    QUANTITY("quantity", d -> d.quantity), 
    AVAILABILITY("availability", d -> d.availability), 
    PRICE("price", d -> d.price); 

    private String name; 
    private Function<Detail, Object> valueExtractor; 

    private DetailQueryParams(String name, 
      Function<Detail, Object> valueExtractor) { 
     this.name = name; 
     this.valueExtractor = valueExtractor; 
    } 

    public static Predicate<Detail> mustMatchDetailValues(
      Function<String, Optional<String>> queryParamGetter) { 
     return Arrays.asList(values()).stream() 
       .map(p -> queryParamGetter.apply(p.name) 
         .map(q -> (Predicate<Detail>) 
           d -> String.valueOf(p.valueExtractor.apply(d)).equals(q)) 
         .orElse(d -> true)) 
       .reduce(Predicate::and) 
       .orElse(d -> true); 
    } 

} 

然後,假設你可以訪問查詢參數,例如, request.getQueryParam(String name)它返回一個String值或null,通過調用使用以下代碼:

details.stream() 
    .filter(DetailQueryParams.mustMatchDetailValues(
      name -> Optional.ofNullable(request.getQueryParam(name)))) 
    .collect(Collectors.toList()); 

什麼方法基本上沒有是:

- for each possible query param 
    - get its value from the request 
    - if value is present build predicate which 
     - gets field value from detail object and convert to string 
     - check that both strings (queried and extracted) matches 
    - if value is not present return predicate that always returns true 
- combine resulting predicates using and 
- use always true as fallback (which here never actually happens) 

當然,這也可以擴展取決於謂詞以產生實際值類型,而不是比較字符串,例如範圍可以通過「priceMin」和/或「priceMax」來請求和處理。

+0

您也可以使用Arrays.stream(values())而不是Arrays.asList(values())。stream()。您還可以將.reduce(Predicate :: and).orElse(d - > true)簡化爲.reduce(d - > true,Predicate ::和)。 – srborlongan