
com.spikeify.aerospikeql.parse.fields.SelectField Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aerospike-ql Show documentation
Show all versions of aerospike-ql Show documentation
SQL wrapper for Aerospike database
The newest version!
package com.spikeify.aerospikeql.parse.fields;
import com.spikeify.aerospikeql.Definitions;
import com.spikeify.aerospikeql.parse.ParserException;
import com.spikeify.aerospikeql.parse.fields.statements.AggregationStatement;
import com.spikeify.aerospikeql.parse.fields.statements.BasicStatement;
import com.spikeify.aerospikeql.parse.fields.statements.Statement;
import com.spikeify.aerospikeql.parse.fields.statements.TransformationStatement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by roman on 17/07/15.
*
* Select field data structure is a sub structure of query statements.
*
* Select statements can contain statements, field transformations, field aggregations.
*/
public class SelectField {
private final List statements = new ArrayList<>(); //main data structure with select statements
private final List aliases = new ArrayList<>(); //aliases of statements that are print out
private List selectList = new ArrayList<>(); //helper data structure: it is used to fill statements data structure
private final Set distinctCounters = new HashSet<>();
private boolean aggregations = false; //if query contains aggregations
/**
* Split every select field on AS and set aliases
*
* @throws ParserException - alias has incorrect name
*/
public void setAliases() throws ParserException {
List newSelectList = new ArrayList<>();
for (String field : selectList) {
String[] names = field.split(" AS ");
//without alias
if (names.length == 1) {
if (names[0].length() > 14) {
String message = "Statement " + names[0] + " is too long. Please define an alias.";
throw new ParserException(message);
} else if (names[0].contains("(")) {
String message = "Please define an alias for field " + names[0] + ".";
throw new ParserException(message);
}
aliases.add(names[0]);
//with alias
} else if (names.length == 2) {
if (names[1].length() > 14) {
String message = "Alias " + names[1] + " is too long. Please define shorter alias.";
throw new ParserException(message);
} else if (names[1].endsWith("_")) {
String message = "Aliases should not end with _ character: " + names[1] + ".";
throw new ParserException(message);
}
aliases.add(names[1]);
}
newSelectList.add(names[0]);
}
this.selectList = newSelectList;
}
/**
* classify statements in select statements. We have 3 types of statements: basic, transformation and aggregation.
*
* basic: select timestamp
* transformation: select hour(timestamp) as hour
* aggregation: sum(duration) as sumDuration
*/
public void setFields() {
Pattern patternAggregations = Definitions.getAggregationsPattern();
Pattern patternDistinct = Pattern.compile("DISTINCT (.+)", Pattern.CASE_INSENSITIVE);
Pattern patternIf = Pattern.compile("CASE WHEN (.*) THEN (.*) END", Pattern.CASE_INSENSITIVE);
Pattern patternIfElse = Pattern.compile("CASE WHEN (.*) THEN (.*) ELSE (.*) END", Pattern.CASE_INSENSITIVE);
String match, condition, condTrue, condFalse, operation = "";
int index = 0;
for (String field : selectList) {
Matcher matcherAggregations = patternAggregations.matcher(field);
if (matcherAggregations.find()) {
//aggregation statements
aggregations = true; //query has aggregations
int j = 0;
for (int i = 1; i <= matcherAggregations.groupCount(); i++)
if ((match = matcherAggregations.group(i)) != null) {
if (j % 3 == 0) {
operation = match.toLowerCase(); //operation name: sum, avg, etc.
} else if (j % 3 == 1) {
Matcher matcherDistinct = patternDistinct.matcher(match);
Matcher matcherIf = patternIf.matcher(match);
Matcher matcherIfElse = patternIfElse.matcher(match);
if (matcherDistinct.find()) {
//count distinct
match = matcherDistinct.group(1);
match = evaluateCondition(match, index);
distinctCounters.add(aliases.get(index));
statements.add(new AggregationStatement.AggregationFieldBuilder(aliases.get(index), operation).setField(match).createAggregationField());
} else if (matcherIfElse.find()) {
//if else aggregation
condition = Definitions.replaceCondition(matcherIfElse.group(1));
condition = evaluateCondition(condition, index);
condTrue = matcherIfElse.group(2);
condTrue = evaluateCondition(condTrue, index);
condFalse = matcherIfElse.group(3);
condFalse = evaluateCondition(condFalse, index);
statements.add(new AggregationStatement.AggregationFieldBuilder(aliases.get(index), operation).setCondition(condition).setIsTrue(condTrue).setIsFalse(condFalse).createAggregationField());
} else if (matcherIf.find()) {
//if aggregation
condition = Definitions.replaceCondition(matcherIf.group(1));
condition = evaluateCondition(condition, index);
condTrue = matcherIf.group(2);
condTrue = evaluateCondition(condTrue, index);
statements.add(new AggregationStatement.AggregationFieldBuilder(aliases.get(index), operation).setCondition(condition).setIsTrue(condTrue).createAggregationField());
} else {
//basic aggregation: min(timestamp)
match = evaluateCondition(match, index);
statements.add(new AggregationStatement.AggregationFieldBuilder(aliases.get(index), operation).setField(match).createAggregationField());
}
break; //when aggregation object is set, break from the loop
}
j++;
}
} else if (field.contains("(") || field.contains("+") || field.contains("-") || field.contains("*") || field.contains("/") || field.contains("%")) {
//transformation statements
statements.add(new TransformationStatement.TransformationFieldBuilder().setAlias(aliases.get(index)).setCondition(field).setNested(true).createTransformationField());
} else {
//basic field
statements.add(new BasicStatement.BasicFieldBuilder().setAlias(aliases.get(index)).setField(field).setNested(true).createBasicField());
}
index++;
}
}
/**
* Determine if string is a field or a constant (function name)
*/
private void findFields(String statement, List toAdd) {
String match;
String[] fieldsSplit = statement.replace("(", " ").replace(")", " ").split(" ");
Pattern detectField = Definitions.getDetectFieldPattern();
for (String field : fieldsSplit) {
if (field.contains("\'"))
continue; //omit constants that are marked with quotes. E.g. the second parameter of JSON_EXTRACT
Matcher m = detectField.matcher(field);
if (m.find()) {
match = m.group(0);
if (!selectList.contains(match) &&
!toAdd.contains(match) &&
!Definitions.transformations.contains(match.toUpperCase()) &&
!Definitions.stopWords.contains(match.toUpperCase()) &&
!Definitions.nullValue.equalsIgnoreCase(match))
toAdd.add(match);
}
}
}
/**
* method checks if a condition have statements with transformations or it has just basic statements
*
* @param condition is a part of condition. E.g. hour(timestamp) > 100
* @param index of an alias
* @return condition with new field names (because of transformations).
*/
private String evaluateCondition(String condition, int index) {
if (isTransformation(condition)) {
condition = setTransformation(condition, index);
} else {
List newFields = new ArrayList<>();
findFields(condition, newFields);
for (String subField : newFields)
statements.add(new BasicStatement.BasicFieldBuilder().setAlias(subField).setField(subField).createBasicField());
}
return condition;
}
/**
* @param condition is a part of condition. E.g. hour(timestamp) > 100
* @param index of an alias
* @return condition with new field names (because of transformations).
*/
private String setTransformation(String condition, int index) {
List newFields = new ArrayList<>();
String alias = aliases.get(index);
findFields(condition, newFields); //get all field names from condition
List transformations = getTransformations(condition); //get all transformations from condition
int k = 0;
for (String subField : newFields) {
String newFieldName = alias + "_" + subField; //mark transformed statements with new name
condition = condition.replace(transformations.get(k), newFieldName); //replace the name with new name in condition
statements.add(new TransformationStatement.TransformationFieldBuilder().setAlias(newFieldName).setCondition(transformations.get(k)).createTransformationField());
k++;
}
return condition;
}
/**
* @param match is a part of condition. E.g. hour(timestamp) > 100
* @return true if match contains transformation function
*/
private boolean isTransformation(String match) {
for (String transformation : Definitions.transformations)
if (match.toUpperCase().contains(transformation + "("))
return true;
return false;
}
/**
* @param match is a part of condition. Example hour(timestamp) > 10 or hour(timestamp1) > hour(timestamp2)
* @return all transformations
*/
private List getTransformations(String match) {
List extractedTransformations = new ArrayList<>();
for (String field : match.split("[<>=!]")) { //split match only with operators and not with a space.
for (String transformation : Definitions.transformations)
if (field.toUpperCase().startsWith(transformation + "(")) {
extractedTransformations.add(field);
break;
}
}
return extractedTransformations;
}
public List getAliases() {
return aliases;
}
public List getStatements() {
return statements;
}
public boolean isAggregations() {
return aggregations;
}
public void setAggregations(boolean aggregations) {
this.aggregations = aggregations;
}
public List getSelectList() {
return selectList;
}
public Set getDistinctCounters() {
return distinctCounters;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy