package io.robe.hibernate.criteria.query;
import io.robe.common.service.search.model.SearchModel;
import io.robe.common.utils.Validations;
import io.robe.common.utils.reflection.Fields;
import io.robe.hibernate.criteria.api.*;
import io.robe.hibernate.criteria.api.criterion.Restriction;
import io.robe.hibernate.criteria.api.criterion.Restrictions;
import io.robe.hibernate.criteria.api.projection.Projection;
import io.robe.hibernate.criteria.api.projection.ProjectionList;
import io.robe.hibernate.criteria.api.cache.EntityMeta;
import io.robe.hibernate.criteria.api.cache.FieldMeta;
import io.robe.hibernate.criteria.api.projection.Projections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.*;
/**
* Created by kamilbukum on 17/01/2017.
*/
public class QueryUtility {
private static final Logger LOGGER = LoggerFactory.getLogger(QueryUtility.class);
/**
*
* @param criteria
* @param queries
*/
static <E> void configureCriteriaByQ(CriteriaParent<E> criteria, String[] queries){
Set<String> joinedClassNames = new HashSet<>();
configureCriteriaByQ(criteria, queries, criteria.getMeta().getFieldMap().keySet(), joinedClassNames);
}
static <E> void configureCriteriaByQ(CriteriaParent<E> criteria, String[] queries, Collection<String> fieldSet, Set<String> joinedClassNames) {
List<Restriction> restrictions = new LinkedList<>();
for(String fieldName: fieldSet) {
FieldMeta fieldMeta = criteria.getMeta().getFieldMap().get(fieldName);
if(fieldMeta == null) {
LOGGER.warn("Not found defined field name in the filtering section in the " + criteria.getEntityClass().getName() + " class ! ");
}
if(!fieldMeta.isSearchIgnore() && !fieldMeta.isTransient() && fieldMeta.getField().getType().equals(String.class)) {
configureQForField(fieldName, fieldMeta, queries, restrictions);
}
if(fieldMeta.getReference() != null) {
if(fieldMeta.getReference().getFilters() != null && fieldMeta.getReference().getFilters().length > 0) {
String className = fieldMeta.getReference().getTargetEntity().getName();
if(joinedClassNames.contains(className)) {
return;
}
joinedClassNames.add(className);
CriteriaJoin<E> criteriaJoin = addOrGetJoin(fieldName, fieldMeta, criteria);
List<String> joinFieldSet = Arrays.asList(fieldMeta.getReference().getFilters());
configureCriteriaByQ(criteriaJoin, queries, joinFieldSet, joinedClassNames);
}
}
}
if(restrictions.size() > 0) {
criteria.add(restrictions.size() == 1 ? restrictions.get(0): Restrictions.or(restrictions));
}
}
private static void configureQForField(String fieldName, FieldMeta fieldMeta, String[] queries, List<Restriction> restrictions) {
// create filter
for(int i = 0 ; i < queries.length; i++) {
Operator op = Operator.Q;
String rawValue = queries[i];
Object value = getValue(op, rawValue, fieldMeta.getField());
Restriction restriction = Restrictions.filter(fieldName, op, value);
restriction.setValueAlias( "$_query_" + i);
restrictions.add(restriction);
}
}
/**
*
* @param criteria
* @param filters
* @param <E>
*/
static <E> void configureFilters(CriteriaParent<E> criteria, String[][] filters, Integer restrictionOrder) {
if(filters == null || filters.length == 0) return;
Map<String, List<Restriction>> restrictionMap = new LinkedHashMap<>();
Map<String, CriteriaParent<E>> criteriaMap = new LinkedHashMap<>();
for(String[] filter: filters) {
String name = filter[0];
if(Validations.isEmptyOrNull(name) && Validations.isEmptyOrNull(filter[1])) continue;
Operator operator = Operator.value(filter[1]);
String rawValue = filter[2];
Parent<E> parent = new Parent<>(criteria, name);
if(!createCriteriaByGivenName(parent)) continue;
FieldMeta fieldMeta = parent.criteria.getMeta().getFieldMap().get(parent.name);
Object value = getValue(operator, rawValue, fieldMeta.getField());
Restriction restriction = Restrictions.filter(parent.name, operator, value);
if(restriction == null) continue;
restriction.setValueAlias(parent.criteria.getAlias() + "_" + parent.name + "_" + restrictionOrder++);
List<Restriction> restrictions = restrictionMap.get(parent.criteria.getAlias());
if(restrictions == null) {
restrictions = new LinkedList<>();
restrictionMap.put(parent.criteria.getAlias(), restrictions);
criteriaMap.put(parent.criteria.getAlias(), parent.criteria);
}
restrictions.add(restriction);
parent.criteria = criteria;
}
for(Map.Entry<String, List<Restriction>> entry: restrictionMap.entrySet()) {
criteriaMap.get(entry.getKey()).add(entry.getValue().size() == 1 ? entry.getValue().get(0): Restrictions.and(entry.getValue()));
}
}
/**
*
* @param criteria
* @param sorts
* @param <E>
*/
public static <E> void configureSorts(CriteriaParent<E> criteria, String[] sorts){
if( sorts == null || sorts.length == 0) return;
for(String sort: sorts) {
if(Validations.isEmptyOrNull(sort)) continue;
sort = sort.trim();
if(sort.length() < 2) continue;
Order.Type type;
try {
type = Order.Type.value(sort.substring(0, 1));
sort = sort.substring(1);
} catch (Exception e) {
type = Order.Type.ASC;
}
Parent<E> parent = new Parent<>(criteria, sort);
if(!createCriteriaByGivenName(parent)) continue;
Order order = type == Order.Type.ASC ? Order.asc(parent.name): Order.desc(parent.name);
parent.criteria.addOrder(order);
}
}
public static <E> void configureSelectFields(Criteria<E> criteria, SearchModel search){
switch (criteria.getTransformer().getTransformType()) {
case ENTITY:
return;
case DTO:
QueryUtility.configureDtoSelects(criteria);
break;
case MAP:
if(search.getFields() != null && search.getFields().length > 0) {
QueryUtility.configureFields(criteria, search.getFields());
}
break;
}
}
public static <E> void configureDtoSelects(Criteria<E> criteria){
EntityMeta transformerMeta = criteria.getTransformer().getMeta();
for(Map.Entry<String, FieldMeta> entry: transformerMeta.getFieldMap().entrySet()) {
FieldMeta fieldMeta = entry.getValue();
String fieldName = fieldMeta.hasRelation() ? fieldMeta.getRelationName() : entry.getKey();
Parent<E> parent = new Parent<>(criteria, fieldName);
ProjectionList projectionList = configureField(parent);
if(projectionList != null) {
projectionList.add(Projections.alias(fieldMeta.isCollection() ? Projections.elements(parent.name): Projections.property(parent.name), entry.getKey()));
}
}
}
/**
*
* @param criteria
* @param fields
* @param <E>
*/
static <E> void configureFields(CriteriaParent<E> criteria, String[] fields){
if( fields == null || fields.length == 0) return;
for(String field: fields) {
if(Validations.isEmptyOrNull(field)) continue;
field = field.trim();
Parent<E> parent = new Parent<>(criteria, field);
ProjectionList projectionList = configureField(parent);
if(projectionList != null) {
FieldMeta fieldMeta = parent.criteria.getMeta().getFieldMap().get(parent.name);
projectionList.add(fieldMeta.isCollection() ? Projections.elements(parent.name): Projections.property(parent.name));
}
}
}
static <E> ProjectionList configureField(Parent<E> parent) {
if(!createCriteriaByGivenName(parent)) return null;
ProjectionList projection = (ProjectionList) parent.criteria.getProjection();
if(parent.criteria.getProjection() == null) {
projection = Projections.projectionList();
parent.criteria.setProjection(projection);
}
return projection;
}
/**
*
*/
private static class Parent<E> {
CriteriaParent<E> criteria;
String name;
public Parent(CriteriaParent<E> criteria, String name){
this.criteria = criteria;
this.name = name;
}
}
/**
*
* @param parent
* @param <E>
* @return
*/
private static <E> boolean createCriteriaByGivenName(Parent<E> parent){
EntityMeta meta = parent.criteria.getMeta();
String[] names;
if(meta.getFieldMap().get(parent.name) != null ) {
names = new String[] {parent.name};
} else {
names = parent.name.split("\\.");
}
int lastParentIndex = names.length -1;
int step = 0;
FieldMeta fieldMeta;
if(lastParentIndex <= step) {
String fieldName = names[0];
fieldMeta = meta.getFieldMap().get(fieldName);
if(fieldMeta == null || fieldMeta.isTransient() ) {
return false;
}
} else {
while (lastParentIndex > step) {
String fieldName = names[step];
fieldMeta = meta.getFieldMap().get(fieldName);
if(fieldMeta == null) return false;
if(fieldMeta.getReference() == null) {
throw new RuntimeException("Field is parent field. @SearchFrom is not defined on field ! ");
}
Class<?> joinClass = fieldMeta.getReference().getTargetEntity();
EntityMeta joinMeta = parent.criteria.getTransformer().getMeta(joinClass);
parent.criteria = addOrGetJoin(names[step], fieldMeta, parent.criteria);
meta = joinMeta;
step++;
}
}
parent.name = names[step];
return true;
}
/**
*
* @param name
* @param meta
* @param criteriaParent
* @return
*/
private static <E> CriteriaJoin<E> addOrGetJoin(String name, FieldMeta meta, CriteriaParent<E> criteriaParent){
Class<?> joinClass = meta.getReference().getTargetEntity();
CriteriaJoin<E> join = criteriaParent.getJoin("$" + name);
if(join == null) {
join = criteriaParent.createJoin(name, joinClass, name);
}
return join;
}
/**
*
* @param operator
* @param rawValue
* @param field
* @return
*/
public static Object getValue(String operator, String rawValue, Field field){
return getValue(Operator.value(operator), rawValue, field);
}
/**
*
* @param operator
* @param rawValue
* @param field
* @return
*/
public static Object getValue(Operator operator, String rawValue, Field field){
if(Operator.IN.equals(operator)) {
if(!field.getType().isAssignableFrom(Collection.class)) {
String[] svalues = rawValue.split("\\|");
List<Object> lvalues = new LinkedList<>();
for (String svalue : svalues) {
lvalues.add(Fields.castValue(field.getType(), svalue));
}
return lvalues;
} else {
return Fields.castValue(Fields.getTypeOfList(field), rawValue);
}
}
return Fields.castValue(field.getType(), rawValue);
}
}