
com.balajeetm.mystique.util.gson.lever.JsonQuery Maven / Gradle / Ivy
/*
* Copyright (c) Balajee TM 2016.
* All rights reserved.
* License - @see
*/
/*
* Created on 12 Oct, 2016 by balajeetm
* http://www.balajeetm.com
*/
package com.balajeetm.mystique.util.gson.lever;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import org.apache.commons.lang3.StringUtils;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* The Class JsonQuery.
*
* @author balajeetm
*/
public class JsonQuery {
/** The json lever. */
private JsonLever jsonLever;
/** The query types. */
private Map> queryTypes;
/**
* Gets the single instance of JsonQuery.
*
* @return single instance of JsonQuery
*/
public static JsonQuery getInstance() {
return Creator.INSTANCE;
}
// Efficient Thread safe Lazy Initialization
// works only if the singleton constructor is non parameterized
/** The Class Creator. */
private static class Creator {
/** The instance. */
private static JsonQuery INSTANCE = new JsonQuery();
}
/** Instantiates a new json query. */
private JsonQuery() {
jsonLever = JsonLever.getInstance();
queryTypes = new HashMap<>();
queryTypes.put("=", this::equalsFilter);
queryTypes.put("!=", this::notEqualsFilter);
queryTypes.put("in", this::inFilter);
queryTypes.put("notin", this::notinFilter);
}
/**
* Query.
*
* @param query the query
* @return the json element
*/
public JsonElement query(JsonObject query) {
JsonElement result = null;
try {
Mono reduce =
queryAsync(query)
.reduce(
new JsonArray(),
(resultarray, json) -> {
resultarray.add(json);
return resultarray;
});
result = reduce.block();
} catch (RuntimeException e) {
result = new JsonPrimitive(e.getMessage());
}
return result;
}
public Flux queryAsync(JsonObject query) {
return Flux.just(query)
.map(q -> validate(q))
.flatMap(
s -> {
Flux obs = null;
if ("valid".equals(s)) {
JsonElement from = query.get("from");
obs =
jsonLever.isArray(from)
? queryAsync(from.getAsJsonArray(), query)
: queryAsync(from.getAsJsonObject(), query);
} else {
Exceptions.propagate(new Throwable(s));
}
return obs;
});
}
/**
* Query async.
*
* @param array the array
* @param query the query
* @return the observable
*/
private Flux queryAsync(JsonArray array, JsonObject query) {
return Flux.fromIterable(array)
.publishOn(Schedulers.elastic())
.filter(json -> filter(json, query.get("where")))
.compose(
obs -> {
Long limit = jsonLever.asLong(query.get("limit"), 0L);
return limit > 0 ? obs.take(limit.intValue()) : obs;
})
.publishOn(Schedulers.elastic())
.compose(
obs ->
StringUtils.equals(jsonLever.getString(query, "select"), "count")
? obs.count().map(count -> new JsonPrimitive(count))
: obs.map(json -> select(json, query.get("select"))));
}
/**
* Query async.
*
* @param obj the obj
* @param query the query
* @return the observable
*/
private Flux queryAsync(JsonElement obj, JsonObject query) {
return Flux.just(obj)
.publishOn(Schedulers.elastic())
.filter(json -> filter(json, query.get("where")))
.compose(
obs -> {
Long limit = jsonLever.asLong(query.get("limit"), 0L);
return limit > 0 ? obs.take(limit.intValue()) : obs;
})
.publishOn(Schedulers.elastic())
.compose(
obs ->
StringUtils.equals(jsonLever.getString(query, "select"), "count")
? obs.count().map(count -> new JsonPrimitive(count))
: obs.map(json -> select(json, query.get("select"))));
}
/**
* Select.
*
* @param json the json
* @param selectElement the select element
* @return the json element
*/
private JsonElement select(JsonElement json, JsonElement selectElement) {
JsonElement result = null;
String selectStr = jsonLever.asString(selectElement);
if (null != selectStr) {
if (equalsAny(selectStr, "*")) result = json;
} else {
if (jsonLever.isArray(selectElement)) {
result = new JsonArray();
for (JsonElement ele : jsonLever.asJsonArray(selectElement)) {
jsonLever.asJsonArray(result).add(jsonLever.get(json, ele));
}
if (jsonLever.asJsonArray(result).size() == 1) {
result = jsonLever.getFirst(jsonLever.asJsonArray(result));
}
}
}
return result;
}
/**
* Filter.
*
* @param json the json
* @param whereElement the where element
* @return the boolean
*/
private Boolean filter(JsonElement json, JsonElement whereElement) {
Boolean filter = Boolean.TRUE;
JsonArray where = jsonLever.asJsonArray(whereElement, new JsonArray());
for (JsonElement condition : where) {
if (jsonLever.isString(condition)) {
String operator = StringUtils.lowerCase(jsonLever.asString(condition));
if (equalsAny(operator, "&", "&&", "and")) {
if (filter) {
continue;
} else {
return filter;
}
}
if (equalsAny(operator, "|", "||", "or")) {
if (!filter) {
continue;
} else {
return filter;
}
}
} else if (jsonLever.isObject(condition)) {
String type = jsonLever.getString(condition, "type");
type = null != type && queryTypes.containsKey(type) ? type : "=";
filter = queryTypes.get(type).apply(json, condition.getAsJsonObject());
}
}
return filter;
}
/**
* Equals filter.
*
* @param json the json
* @param condition the condition
* @return the boolean
*/
private Boolean equalsFilter(JsonElement json, JsonObject condition) {
return jsonLever
.asJsonElement(condition.get("value"), JsonNull.INSTANCE)
.equals(
jsonLever.get(json, jsonLever.asJsonArray(condition.get("field"), new JsonArray())));
}
/**
* Not equals filter.
*
* @param json the json
* @param condition the condition
* @return the boolean
*/
private Boolean notEqualsFilter(JsonElement json, JsonObject condition) {
return !equalsFilter(json, condition);
}
/**
* In filter.
*
* @param json the json
* @param condition the condition
* @return the boolean
*/
private Boolean inFilter(JsonElement json, JsonObject condition) {
return jsonLever
.asJsonArray(condition.get("value"), new JsonArray())
.contains(
jsonLever.get(json, jsonLever.asJsonArray(condition.get("field"), new JsonArray())));
}
/**
* Notin filter.
*
* @param json the json
* @param condition the condition
* @return the boolean
*/
private Boolean notinFilter(JsonElement json, JsonObject condition) {
return !inFilter(json, condition);
}
private Boolean equalsAll(String str, String... matches) {
Boolean equals = Boolean.TRUE;
for (String string : matches) {
equals = equals && StringUtils.equals(str, string);
if (!equals) {
break;
}
}
return equals;
}
private Boolean equalsAny(String str, String... matches) {
Boolean equals = Boolean.FALSE;
for (String string : matches) {
equals = equals || StringUtils.equals(str, string);
if (equals) {
break;
}
}
return equals;
}
/**
* Validate.
*
* @param query the query
* @return the string
*/
private String validate(JsonObject query) {
if (jsonLever.isNull(jsonLever.get(query, "select"))) {
return "select field of the query cannot be null";
}
JsonElement from = query.get("from");
if (!(jsonLever.isObject(from) || jsonLever.isArray(from))) {
return "from field must be a valid json object or array";
}
JsonElement where = jsonLever.get(query, "where");
if (jsonLever.isNotNull(where) && !jsonLever.isArray(where)) {
return "where field must be a valid json array";
}
JsonElement limit = jsonLever.get(query, "limit");
if (jsonLever.isNotNull(limit) && !jsonLever.isNumber(limit)) {
return "limit must be a valid number";
}
return "valid";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy