All Downloads are FREE. Search and download functionalities are using the official Maven repository.

automately.core.data.predicates.JsonQueryPredicate Maven / Gradle / Ivy

package automately.core.data.predicates;


import com.hazelcast.query.Predicate;
import io.jsync.json.JsonArray;
import io.jsync.json.JsonElement;
import io.jsync.json.JsonObject;
import io.jsync.json.impl.Json;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Map;

/**
 * A simple Hazelcast Predicate that allows you to filter data using
 * queries in the form of a JsonObject.
 */
public class JsonQueryPredicate implements Serializable, Predicate {

    private JsonElement query;

    public JsonQueryPredicate(JsonElement query) {
        if(query == null){
            throw new RuntimeException("Your query cannot be null");
        }
        this.query = query;
    }

    @Override
    public boolean apply(Map.Entry entry) {
        Object obj = entry.getValue();

        if(obj instanceof JsonObject && query.isObject()){
            // Since the entry is a single JsonObject we will match a single
            return processJsonObjectQuery((JsonObject) obj, query.asObject());
        } else if(obj instanceof JsonObject && query.isArray()){
            // We can loop through the query array to check each query against the value
            for(Object q : query.asArray()){
                // Since the query is an actual JsonObject we can treat it as a Json Query
                if(q instanceof JsonObject){
                    JsonObject jsonQuery = (JsonObject) q;
                    if(processJsonObjectQuery((JsonObject) obj, jsonQuery)){
                        return true;
                    }
                }
            }
        } else if(obj instanceof JsonArray){
            // Loop through all the values in this entry
            // If any value matches we will return this entry
            for(Object inObj : (JsonArray) obj){
                // If the value is a json object we will process it with the query array
                if(inObj instanceof JsonObject){
                    JsonObject jsonVal = (JsonObject) inObj;
                    if(query.isObject()){
                        if(processJsonObjectQuery(jsonVal, query.asObject())){
                            return true;
                        }
                    } else {
                        // We can loop through the query array to check each query against the value
                        for(Object q : query.asArray()){
                            // Since the query is an actual JsonObject we can treat it as a Json Query
                            if(q instanceof JsonObject){
                                JsonObject jsonQuery = (JsonObject) q;
                                if(processJsonObjectQuery(jsonVal, jsonQuery)){
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        // If no JsonObjects were found we will return false by default since we are looking for Json Data
        return false;
    }

    private boolean processJsonObjectQuery(JsonObject value, JsonObject query){

        // An empty query is the same as a wildcard but without a value
        if(query.size() == 0){
            return true;
        }

        // We return false for sure because that doesn't even make sense
        if(query.size() > value.size()){
            return false;
        }

        JsonObject valueObj = value;

        boolean success = false;

        for(String field : query.getFieldNames()){

            Object queryValue = query.getValue(field);

            if(field.contains(".")){
                String[] subFields = field.split("\\.");
                JsonObject newValue = null;
                String newField = null;
                for(String sub : subFields){
                    if(newValue == null){
                        if(value.containsField(sub) && value.getValue(sub) instanceof JsonObject){
                            newValue = value.getObject(sub);
                            continue;
                        }
                        break;
                    }
                    newField = sub;
                    if(newValue.containsField(sub) && newValue.getValue(sub) instanceof JsonObject){
                        newValue = newValue.getObject(sub);
                    }
                }
                if(newValue != null && newField != null && field.endsWith("." + newField)){
                    valueObj = newValue;
                    field = newField;
                }
            }

            // If it doesn't contain the field we cannot match it.
            if(!valueObj.containsField(field)){
                return false;
            }

            // This is the object we are checking
            Object value2 = valueObj.getValue(field);

            if(value2 instanceof JsonArray){
                JsonArray array = (JsonArray) value2;
                for(Object val : array.toArray()){
                    if(checkValue(queryValue, val)){
                        success = true;
                    }
                }
                // if it's an array let's loop through it
            } else {
                if(checkValue(queryValue, value2)){
                    success = true;
                } else {
                    return false;
                }
            }
        }

        // An empty query is the same as a wildcard but without a value
        return success;
    }

    private static boolean checkValue(Object queryVal, Object value2){
        if(queryVal.equals(value2)){
            return true;
        } else if(queryVal instanceof String && queryVal.toString().equals("*")){
            return true;
        } else if(queryVal instanceof String && queryVal.toString().matches("^v>\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split(">")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) > 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v<\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split("<")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) < 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v>=\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split(">=")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) >= 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v<=\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split("<=")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) <= 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v=\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split("=")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) == 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v==\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split("==")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) == 0){
                return true;
            }
        } else if(queryVal instanceof String && queryVal.toString().matches("^v!=\\d+$") && value2 instanceof Number){
            Number numb1 = Long.parseLong(queryVal.toString().split("!=")[1]);
            Number numb2 = (Number) value2;
            if(compareTo(numb2, numb1) != 0){
                return true;
            }
        } else if(queryVal instanceof Boolean && value2 instanceof Boolean){
            if(queryVal == value2){
                return true;
            }
        } else if(queryVal instanceof String && value2 instanceof String){
            if(((String) value2).matches((String) queryVal)){
                return true;
            }
        } else if(queryVal instanceof JsonArray){
            JsonArray queryArray = (JsonArray) queryVal;
            // We check the value against an array of query stuff
            for(Object qv : queryArray){
                if(checkValue(qv, value2)){
                    return true;
                }
            }
        } else if(queryVal instanceof JsonObject){
            // JSON objects are more like mongo queries
            JsonObject queryObj = (JsonObject) queryVal;
            // By default an empty query value
            boolean success = false;
            for(String newF : queryObj.getFieldNames()){
                Object obj = queryObj.getValue(newF);
                if(obj != null){
                    // We already know they key exists or we would not be doing this
                    if(newF.equals("$exists") && obj instanceof Boolean){
                        success = (boolean) obj;
                    } else if(newF.equals("$eq")){
                        success = value2.equals(obj);
                    } else if(newF.equals("$gt") && obj instanceof Number && value2 instanceof Number){
                        success = checkValue("v>" + ((Number) obj).longValue(), value2);
                    } else if(newF.equals("$gte") && obj instanceof Number && value2 instanceof Number){
                        success = checkValue("v>=" + ((Number) obj).longValue(), value2);
                    } else if(newF.equals("$lt") && obj instanceof Number && value2 instanceof Number){
                        success = checkValue("v<" + ((Number) obj).longValue(), value2);
                    } else if(newF.equals("$lte") && obj instanceof Number && value2 instanceof Number){
                        success = checkValue("v<=" + ((Number) obj).longValue(), value2);
                    } else if(newF.equals("$ne") && obj instanceof Number && value2 instanceof Number){
                        success = checkValue("v!=" + ((Number) obj).longValue(), value2);
                    } else if(newF.equals("$in") && obj instanceof JsonArray){
                        JsonArray values = (JsonArray) obj;
                        for(Object value : values){
                            if(value2.equals(value)){
                                success = true;
                                break;
                            } else if(value2.toString().matches(value.toString())){
                                success = true;
                                break;
                            }
                        }
                    } else if(newF.equals("$nin") && obj instanceof JsonArray){
                        // None of the values can exist
                        success = true; // Let's say it's successful
                        JsonArray values = (JsonArray) obj;
                        for(Object value : values){
                            if(value2.equals(value)){
                                success = false;
                                break;
                            } else if(value2.toString().matches(value.toString())){
                                success = false;
                                break;
                            }
                        }
                    }
                }
            }
            return success;
        }
        // If it doesn't match that means the query didn't line up
        return false;
    }

    private static int compareTo(Number n1, Number n2) {
        // ignoring null handling
        BigDecimal b1 = new BigDecimal(n1.doubleValue());
        BigDecimal b2 = new BigDecimal(n2.doubleValue());
        return b1.compareTo(b2);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy