我正在尝试对 EmployeeDTO 列表实施过滤。我有一个 SearchCritera 列表,我想将其强加到列表中进行过滤。这是类结构。
public class EmployeeDTO {
private String id;
private String employeeId;
private String email;
private String phone;
private String firstName;
private String lastName;
private String middleName;
private LocalDate dob;
private Map<String, Object> details; // for storing dynamic nested data
}
public class SearchCriteria {
private String key;
private String value;
private SearchCondition condition;
private SearchOperation operation;
}
public enum SearchCondition {
EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUALS, LESS_THAN_OR_EQUALS
}
public enum SearchOperation {
AND, OR, NOT
}
您可以预期 SearchCriteria 列表如下所示:
[
{
"key": "education.degree",
// would reside in details.education.degree where education is an array of {degree, city}
"value": "BBA",
"condition": "EQUALS"
},
{
"key": "education.degree",
"value": "MBAA",
"condition": "EQUALS",
"operation": "AND"
},
{
"key": "education.degree",
"value": "BA",
"condition": "EQUALS",
"operation": "OR"
},
{
"key": "middle_name",
"value": "Anne",
"condition": "EQUALS",
"operation": "NOT"
}
]
这是我用来过滤员工列表的方法。但基于这种过滤技术,我得到的结果不准确。我一定是哪里出错了。
public class EmployeeFilter {
private static final Logger log = LoggerFactory.getLogger(EmployeeFilter.class);
private static final String OUTPUT_1 = "output_1";
private static final String OUTPUT_2 = "output_2";
public Map<String, JSONObject> processNode(List<EmployeeDTO> employees, List<SearchCriteria> searchCriteria) {
Map<String, List<EmployeeDTO>> filteredEmployees = filterEmployees(employees, searchCriteria);
JSONObject validEmployeesJson = new JSONObject();
validEmployeesJson.put(Constants.EMPLOYEES, filteredEmployees.get("valid"));
JSONObject invalidEmployeesJson = new JSONObject();
invalidEmployeesJson.put(Constants.EMPLOYEES, filteredEmployees.get("invalid"));
return Map.of(
OUTPUT_1, validEmployeesJson,
OUTPUT_2, invalidEmployeesJson
);
}
private Map<String, List<EmployeeDTO>> filterEmployees(List<EmployeeDTO> employees, List<SearchCriteria> searchCriteria) {
Map<Boolean, List<EmployeeDTO>> partitionedEmployees = employees.stream()
.collect(Collectors.partitioningBy(e -> isEmployeeMatchingSearchCriteria(e, searchCriteria)));
return Map.of(
"valid", partitionedEmployees.getOrDefault(true, Collections.emptyList()),
"invalid", partitionedEmployees.getOrDefault(false, Collections.emptyList())
);
}
private boolean isEmployeeMatchingSearchCriteria(EmployeeDTO employee, List<SearchCriteria> searchCriteria) {
boolean result = true;
for (SearchCriteria criterion : searchCriteria) {
boolean match = evaluateCondition(employee, criterion);
SearchOperation currentOperation = criterion.getOperation();
if (currentOperation == null) currentOperation = SearchOperation.AND;
result = switch(currentOperation) {
case AND -> result && match;
case OR -> result || match;
case NOT -> !match;
};
}
return result;
}
private boolean evaluateCondition(EmployeeDTO employee, SearchCriteria criterion) {
String key = criterion.getKey();
String val = criterion.getValue();
SearchCondition condition = criterion.getCondition();
Object employeeValue = getValue(employee, key);
if(employeeValue == null) return false;
switch (condition) {
case EQUALS:
if (employeeValue instanceof List) {
List<?> list = (List<?>) employeeValue;
return list.stream().anyMatch(item -> item.equals(val));
}
return employeeValue.equals(val);
case CONTAINS:
return employeeValue.toString().contains(val);
case STARTS_WITH:
return employeeValue.toString().startsWith(val);
case ENDS_WITH:
return employeeValue.toString().endsWith(val);
case GREATER_THAN:
return compareValues(employeeValue, val) > 0;
case LESS_THAN:
return compareValues(employeeValue, val) < 0;
case GREATER_THAN_OR_EQUALS:
return compareValues(employeeValue, val) >= 0;
case LESS_THAN_OR_EQUALS:
return compareValues(employeeValue, val) <= 0;
default:
return false;
}
}
private int compareValues(Object employeeValue, String val) {
try {
if (employeeValue instanceof Number) {
double employeeDouble = ((Number) employeeValue).doubleValue();
double valDouble = Double.parseDouble(val);
return Double.compare(employeeDouble, valDouble);
} else if (employeeValue instanceof String) {
return ((String) employeeValue).compareTo(val);
} else if (employeeValue instanceof LocalDate) {
LocalDate employeeDate = (LocalDate) employeeValue;
LocalDate valDate = LocalDate.parse(val, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
return employeeDate.compareTo(valDate);
} else {
return 0;
}
} catch(Exception e) {
return 0;
}
}
private Object getValue(EmployeeDTO employee, String key) {
if (employee == null || key == null) { // Added null check for employee
return null;
}
switch (key) {
case "employee_id": return employee.getEmployeeId();
case "email": return employee.getEmail();
case "first_name": return employee.getFirstName();
case "middle_name": return employee.getMiddleName();
case "last_name": return employee.getLastName();
case "phone": return employee.getPhone();
case "dob": return employee.getDob();
default:
// Handle dot notation (for nested objects), including arrays
Map<String, Object> details = employee.getDetails();
String[] keys = key.split("\\.");
Object value = details;
for (String part: keys) {
if (value instanceof Map) {
value = ((Map<String, Object>) value).get(part); // navigating the map
} else if (value instanceof List) {
List<?> list = (List<?>) value;
List<Object> matchedValues = new ArrayList<>();
for (Object item: list) {
if(item instanceof Map) {
Object nestedValue = ((Map<String, Object>) item).get(part);
if (nestedValue != null) {
matchedValues.add(nestedValue);
}
}
}
value = matchedValues;
} else {
return null; // neither a map nor a list
}
// If it is nested further
if (value != null && !part.equals(keys[keys.length - 1])) {
continue;
} else {
// Found deepest value
return value;
}
}
return null;
}
}
}
请帮助我纠正逻辑,如果您能建议我可以进行一些代码改进,这也会很有帮助。预先感谢!
由于您在评论中澄清了您的
NOT
运算符是“与非”的意思,所以所提供的代码错误地处理了该运算符。 这个...
case NOT -> !match;
...仅考虑当前项的值,忽略该点之前的运行结果。 这并不是“与非”的忠实执行。 你似乎想要...
case NOT -> result && !match;
...相反。