Reputation: 3861
Spring has the ability to return a subset of a collection through the ExpressionParser
(Collection Selection).
For example, imagine a simple class:
public Customer {
private String name;
private boolean vip;
private boolean conferenceAttendee;
}
If I had a List<Customer>
collection that was previously set as a StandardEvaluationContext
variable: evalContext.setVariable("customerList", customers);
I could retrieve a subset of all the customers where vip is true through a "selection expression" <variable>.?[<selectionExpression>]
:
List<Customer> vipCustomers =
(List<Customer>)new SpelExpressionParser()
.parseExpression("#customerList.?[vip]")
.getValue(evalContext);
Is it possible to execute this same logic in a selection expression as a union (vip || conferenceAttendee) or intersection (vip && conferenceAttendee), without having to call evalContext.setVariable("customerList", vipCustomers)
on an intermediate list and perform a second parseExpression?
Something similar to this:
// This doesn't work...
List<Customer> vipCustomers =
(List<Customer>)new SpelExpressionParser()
.parseExpression("#customerList.?[vip || conferenceAttendee]")
.getValue(evalContext);
I'm looking specifically to understand what valid selection expressions I can pass in to SpelExpressionParser's parseExpression, as opposed to other comparable solutions.
Upvotes: 1
Views: 781
Reputation: 21401
You are very very close. There is the OR and AND logical operators in the Spring Expression Language that you can use:
customerList.add(new Customer("jim", true, false));
customerList.add(new Customer("bob", false, true));
customerList.add(new Customer("rob", true, true));
List<Customer> vipCustomers =
(List<Customer>)new SpelExpressionParser()
.parseExpression("#customerList.?[vip]")
.getValue(evalContext);
System.out.println(vipCustomers);
//[Customer{name='jim'}, Customer{name='rob'}]
List<Customer> vipANDConfAttendeesCustomers =
(List<Customer>)new SpelExpressionParser()
.parseExpression("#customerList.?[vip and conferenceAttendee]")
.getValue(evalContext);
System.out.println(vipANDConfAttendeesCustomers);
//[Customer{name='rob'}]
List<Customer> vipORConfAttendeesCustomers =
(List<Customer>)new SpelExpressionParser()
.parseExpression("#customerList.?[vip or conferenceAttendee]")
.getValue(evalContext);
System.out.println(vipORConfAttendeesCustomers);
//[Customer{name='jim'}, Customer{name='bob'}, Customer{name='rob'}]
Before Edit - Can be ignored as not really an answer but a suggestion
Allow me to introduce another approach to that without Spring that has a more functional feel into it and scales nicely providing more expressiveness for complex and/or interactions. The following solution is using Guava's Predicates to express the main building blocks of your querying needs:
Predicate<Customer> isVip = new Predicate<Customer>() {
@Override
public boolean apply(Customer customer) {
return customer.isVip();
}
};
Predicate<Customer> isConferenceAttendee = new Predicate<Customer>() {
@Override
public boolean apply(Customer customer) {
return customer.isConferenceAttendee();
}
};
and then combine them into more complex queries involving Predicates.and and Predicates.or, filtering the Collection through Iterables.filter (a functional way of iterating through them):
Iterables.filter(customers, isVip);
Iterables.filter(customers, Predicates.and(isVip,isConferenceAttendee));
Iterables.filter(customers, Predicates.or(isVip,isConferenceAttendee));
The full working example with some autogenerated equals/hashcode/toString methods based on the assumption that the name is what identifies uniquely a Customer:
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
public class GuavaTest {
public static void main(String ...args){
Predicate<Customer> isVip = new Predicate<Customer>() {
@Override
public boolean apply(Customer customer) {
return customer.isVip();
}
};
Predicate<Customer> isConferenceAttendee = new Predicate<Customer>() {
@Override
public boolean apply(Customer customer) {
return customer.isConferenceAttendee();
}
};
List<Customer> customers = Lists.newArrayList();
customers.add(new Customer("jim",true,false));
customers.add(new Customer("bob",false,true));
customers.add(new Customer("rob",true,true));
System.out.println("Vips:\t"+Iterables.filter(customers, isVip));
System.out.println("Vips && ConfAttendees:\t"+Iterables.filter(customers, Predicates.and(isVip,isConferenceAttendee)));
System.out.print("Vips || ConfAttendees:\t"+Iterables.filter(customers, Predicates.or(isVip,isConferenceAttendee)));
}
}
class Customer {
private String name;
private boolean vip;
private boolean conferenceAttendee;
Customer(String name, boolean vip, boolean conferenceAttendee) {
this.name = name;
this.vip = vip;
this.conferenceAttendee = conferenceAttendee;
}
public String getName() {
return name;
}
public boolean isVip() {
return vip;
}
public boolean isConferenceAttendee() {
return conferenceAttendee;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
if (name != null ? !name.equals(customer.name) : customer.name != null) return false;
return true;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
'}';
}
}
Upvotes: 3