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

io.github.thunderz99.cosmos.condition.SimpleExpression Maven / Gradle / Ivy

There is a newer version: 0.7.11
Show newest version
package io.github.thunderz99.cosmos.condition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.github.thunderz99.cosmos.condition.Condition.OperatorType;
import io.github.thunderz99.cosmos.dto.CosmosSqlParameter;
import io.github.thunderz99.cosmos.dto.CosmosSqlQuerySpec;
import io.github.thunderz99.cosmos.util.Checker;
import io.github.thunderz99.cosmos.util.JsonUtil;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * A class representing simple expression
 * 

* {@code * c.id = "001", c.age > 15, c.skills CONTAINS "java", and other simple filter * } */ public class SimpleExpression implements Expression { public static final Pattern binaryOperatorPattern = Pattern.compile("^\\s*(LIKE|IN|=|!=|<|<=|>|>=)\\s*$"); public static final Pattern alphaNumericPattern = Pattern.compile("^[\\w\\d]+$"); public String key; public Object value; public OperatorType type = OperatorType.BINARY_OPERATOR; /** * Default is empty, which means the default operator based on filter's key and value * * The difference between "=" and the default operator "": *

e.g.
*
    *
  • {@code {"status": ["A", "B"]} means status is either A or B }
  • *
  • {@code {"status =": ["A", "B"]} means status equals ["A", "B"] }
  • *
*/ public String operator = ""; public SimpleExpression() { } public SimpleExpression(String key, Object value) { this.key = key; this.value = value; } public SimpleExpression(String key, Object value, String operator) { this.key = key; this.value = value; this.operator = operator; this.type = binaryOperatorPattern.asPredicate().test(operator) ? OperatorType.BINARY_OPERATOR : OperatorType.BINARY_FUNCTION; } @Override public CosmosSqlQuerySpec toQuerySpec(AtomicInteger paramIndex, String selectAlias) { var ret = new CosmosSqlQuerySpec(); var params = new ArrayList(); // fullName.last -> @param001_fullName__last // or // "829cc727-2d49-4d60-8f91-b30f50560af7.name" -> @param001_wg31gsa.name var paramName = getParamNameFromKey(this.key, paramIndex.getAndIncrement()); var paramValue = this.value; // Robust process for operator IN and paramValue is not Collection if ("IN".equals(this.operator) && !(paramValue instanceof Collection)) { paramValue = List.of(paramValue); } if (paramValue instanceof Collection) { // collection param value // e.g ( c.parentId IN (@parentId__0, @parentId__1, @parentId__2) ) var coll = (Collection) paramValue; // array equals or not if (Set.of("=", "!=").contains(this.operator)) { // use = or != ret.setQueryText(String.format(" (%s %s %s)", Condition.getFormattedKey(this.key, selectAlias), this.operator, paramName)); params.add(Condition.createSqlParameter(paramName, paramValue)); } else { //the default operator for collection //ARRAY_CONTAINS if (coll.isEmpty()) { //if paramValue is empty, return a FALSE queryText. ret.setQueryText(" (1=0)"); } else { // use ARRAY_CONTAINS by default to minimize the sql length ret.setQueryText(buildArrayContains(this.key, paramName, coll, params)); } } } else { // single param value if (StringUtils.isEmpty(this.operator)) { // set the default operator for scalar value this.operator = "="; } // paramName or fieldName var valuePart = ""; if (paramValue instanceof FieldKey) { // valuePart should be "mail2" for "c.mail != c.mail2" valuePart = Condition.getFormattedKey(((FieldKey) paramValue).keyName, selectAlias); } else { // valuePart should be "@param001_wg31gsa" valuePart = paramName; params.add(Condition.createSqlParameter(paramName, paramValue)); } // other types var formattedKey = Condition.getFormattedKey(this.key, selectAlias); if (this.type == OperatorType.BINARY_OPERATOR) { // operators, e.g. =, !=, <, >, LIKE //use c["key"] for cosmosdb reserved words ret.setQueryText(String.format(" (%s %s %s)", formattedKey, this.operator, valuePart)); } else if (Condition.typeCheckFunctionPattern.asMatchPredicate().test(this.operator)) { // type check funcs: IS_DEFINED|IS_NUMBER|IS_PRIMITIVE, etc ret.setQueryText(String.format(" (%s(%s) = %s)", this.operator, formattedKey, valuePart)); } else { // other binary funcs. e.g. STARTSWITH, CONTAINS, ARRAY_CONTAINS ret.setQueryText(String.format(" (%s(%s, %s))", this.operator, formattedKey, valuePart)); } } ret.setParameters(params); return ret; } /** * generate a valid param name from a key. *

* {@code * // e.g * // fullName.last -> @param001_fullName__last * // or * // if the key contains a non alphanumeric part, it will be replaced by a random str. * // "829cc727-2d49-4d60-8f91-b30f50560af7.name" -> @param001_wg31gsa.name * // "family.テスト.age" -> @param001_family.ab135dx.age * } * * @param key * @param index */ static String getParamNameFromKey(String key, int index) { Checker.checkNotBlank(key, "key"); var sanitizedKey = Stream.of(key.split("\\.")).map(part -> { if (alphaNumericPattern.asMatchPredicate().test(part)) { return part; } else { return RandomStringUtils.randomAlphanumeric(7); } }).collect(Collectors.joining("__")); return String.format("@param%03d_%s", index, sanitizedKey); } @Override public String toString() { return JsonUtil.toJson(this); } /** * A helper function to generate c.foo IN (...) queryText *

* INPUT: "parentId", "@parentId", ["id001", "id002", "id005"], params OUTPUT: * "( c.parentId IN (@parentId__0, @parentId__1, @parentId__2) )", and add * paramsValue into params */ static String buildArray(String key, String paramName, Collection paramValue, List params) { var ret = new StringBuilder(String.format(" (%s IN (", Condition.getFormattedKey(key))); int index = 0; var paramNameList = new ArrayList(); for (var v : paramValue) { var paramNameIdx = String.format("%s__%d", paramName, index); paramNameList.add(paramNameIdx); params.add(Condition.createSqlParameter(paramNameIdx, v)); index++; } ret.append(paramNameList.stream().collect(Collectors.joining(", "))); ret.append("))"); return ret.toString(); } /** * A helper function to generate c.foo ARRAY_CONTAINS (...) queryText *

* INPUT: "parentId", "@parentId", ["id001", "id002", "id005"], params OUTPUT: * "(ARRAY_CONTAINS("@parentId", c["parentId"]))", and add * paramsValue into params */ static String buildArrayContains(String key, String paramName, Collection paramValue, List params) { var ret = String.format(" (ARRAY_CONTAINS(%s, %s))", paramName, Condition.getFormattedKey(key)); params.add(Condition.createSqlParameter(paramName, paramValue)); return ret; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy