
com.spikeify.aerospikeql.generate.functions.Function 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.generate.functions;
import com.spikeify.aerospikeql.Definitions;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by roman on 16/07/15.
*
* abstract class defines common fields and methods for other lua functions.
*/
class Function {
final String luaSep = ".."; //LUA string concatenation
String code; //code for a function body
String functionName;
String nameArg1; //first argument for a function
String nameArg2;
String nameArg3;
String streamOperation; //filter, map, aggregate or reduce
int level; //code indentation
private String header; // local function functionName(arg1)
private String footer; // end
private String streamArg; //argument for a stream operation
public String getFunction() {
return header + code + footer;
}
public String getStreamOperation() {
if (this.streamArg == null) {
return getStreamOperation1Arg();
} else {
return getStreamOperation2Arg();
}
}
private String getStreamOperation1Arg() {
return " : " + streamOperation + "(" + functionName + ")";
}
private String getStreamOperation2Arg() {
return " : " + streamOperation + "(" + streamArg + ", " + functionName + ")";
}
void setSignature0Arg() {
String tabs = getTabs(level);
String code = "";
if (level == 0) {
code += tabs + "function ";
} else {
code += tabs + "local function ";
}
code += functionName + "()\n";
setHeader(code);
setFooter(tabs + "end\n\n");
}
void setSignature1Arg() {
String tabs = getTabs(level);
String code = "";
if (level == 0) {
code += tabs + "function ";
} else {
code += tabs + "local function ";
}
code += functionName + "(" + nameArg1 + ")\n";
setHeader(code);
setFooter(tabs + "end\n\n");
}
void setSignature2Arg() {
String tabs = getTabs(level);
String code = "";
if (level == 0) {
code += tabs + "function ";
} else {
code += tabs + "local function ";
}
code += functionName + "(" + nameArg1 + ", " + nameArg2 + ")\n";
setHeader(code);
setFooter(tabs + "end\n\n");
}
void setSignature3Arg() {
String tabs = getTabs(level);
String code = "";
if (level == 0) {
code += tabs + "function ";
} else {
code += tabs + "local function ";
}
code += functionName + "(" + nameArg1 + ", " + nameArg2 + ", " + nameArg3 + ")\n";
setHeader(code);
setFooter(tabs + "end\n\n");
}
public String getHeader() {
return header;
}
private void setHeader(String header) {
this.header = header;
}
public String getFooter() {
return footer;
}
private void setFooter(String footer) {
this.footer = footer;
}
void setStreamArg(String streamArg) {
this.streamArg = streamArg;
}
/**
* Create an indentation with tabs
*
* @param level - level of intendation
* @return - string with tab intendations
*/
String getTabs(int level) {
String tabs = "";
for (int i = 0; i < level; i++)
tabs += "\t";
return tabs;
}
/**
* Parse statements (examples statements is 1 + 2 + value)
*
* @param statement - transformation in select statements, e.g. (1+2)/3 + hour(timestamp) + 400
* @param functions - detected functions in statements, e.g. HOUR, DAY
* @return - LUA code for a statements.
*/
public String parseStatement(String statement, HashSet functions) {
String[] fields = statement.split(Definitions.transformationOperatorsRegex(true)); //split a statements with positive lookahead. This means: "a, b, c".split(",") => ["a,", "b,", "c"]
String item = fields[0].trim();
if (item.equalsIgnoreCase("condition()") || item.equalsIgnoreCase("true") || item.equalsIgnoreCase("false")) {
if (fields.length > 1) {
return item + parseStatement(arrayToString(fields, 1), functions);
} else {
return item;
}
}
if (item.equals("") || item.matches(Definitions.defaultQuotesReplacementName + "\\d+")) {
if (fields.length > 1) {
item = fields[1];
fields[1] = "";
} else {
return item;
}
}
if (Definitions.transformationsOperators.contains(item.toUpperCase())) {
return " " + item.toLowerCase() + " "; //operator +, - etc.
}
if (Definitions.transformations.contains(item.toUpperCase())) {
functions.add(item.toLowerCase()); //add LUA function code for transformation
return item.toLowerCase();
}
//split: + field1 etc.
String[] itemSplit = item.split(Definitions.transformationOperatorsRegex(false));
if (itemSplit.length > 1)
return parseStatement(itemSplit[0], functions) + //first part of item
parseStatement(arrayToString(itemSplit, 1), functions) + //second part of item
parseStatement(arrayToString(fields, 1), functions); //all others
//process parentheses
Pattern detectLeftParenthesis = Pattern.compile("(.*)\\((.+)"); //value before right parenthesis and after
Matcher matchLeftParenthesis = detectLeftParenthesis.matcher(item);
if (matchLeftParenthesis.find()) {
return parseStatement(matchLeftParenthesis.group(1), functions) + "(" +
parseStatement(matchLeftParenthesis.group(2), functions) +
parseStatement(arrayToString(fields, 1), functions);
}
Pattern detectRightParenthesis = Pattern.compile("(.+)\\)");
Matcher matchRightParenthesis = detectRightParenthesis.matcher(item);
if (matchRightParenthesis.find()) {
return parseStatement(matchRightParenthesis.group(1), functions) + ")";
}
Pattern detectFieldPattern = Definitions.getDetectFieldPattern();
Matcher detectFieldMatcher = detectFieldPattern.matcher(item);
if (detectFieldMatcher.find()) {
if (item.contains("like_")) {
return processLikeStatement(item) + parseStatement(arrayToString(fields, 1), functions); //item is a field in AS;
} else {
return "topRec[\"" + item + "\"]" + parseStatement(arrayToString(fields, 1), functions); //item is a field in AS
}
}
return statement.trim();
}
/**
* Generate LUA code for SQL like and not like
*
* @param item is field1_like_field2
* @return Lua code
*/
private String processLikeStatement(String item) {
Pattern detectFieldPattern = Definitions.getDetectFieldPattern();
String operation = item.contains("_notlike_") ? "not " : "";
operation += "string.match(";
String[] itemSplit = item.split("_like_|_notlike_");
boolean addComma = false;
for (String subItem : itemSplit) {
Matcher detectFieldMatcher = detectFieldPattern.matcher(subItem);
if (detectFieldMatcher.find()) {
if (subItem.matches(Definitions.defaultQuotesReplacementName + "\\d+"))
operation += subItem;
else
operation += "topRec[\"" + subItem + "\"]";
if (!addComma) {
operation += ", ";
addComma = true;
}
} else
operation += subItem;
}
return operation + ") ";
}
/**
* join strings in array to a string and return it
*
* @param stringArray ["val1", "val2"]
* @param first start index for joining string
* @return string
*/
private String arrayToString(String[] stringArray, int first) {
String newString = "";
for (int i = first; i < stringArray.length; i++) {
if (stringArray[i].length() > 0) {
newString += stringArray[i];
}
}
return newString;
}
/**
* @param replaceQuotesMapping - is empty on input and it stores {MATCH1: 'event/start ...}
* @param statement - example: field1 is gt field2
* @return - processed statements
*/
public String preProcessStatementQuotes(Map replaceQuotesMapping, String statement) {
statement = statement.replaceAll("([^!><])=", "$1 == ") //replace: = with ==, but not if !=.
.replaceAll(" (?i)NOT (?i)LIKE ", "_notlike_") //replace: field1 not like 'start' with field1_notlike_start (ignore case)
.replaceAll(" (?i)LIKE ", "_like_") //replace: field1 like 'start' with field1like_start (ignore case)
.replaceAll(" (?i)and ", " AND ") //replace and with AND (ignore case), to be able to use contains method on arrayList.
.replaceAll(" (?i)or ", " OR ");
Pattern pattern = Pattern.compile("('.*?')"); //matches all values between ' '
Matcher matcher = pattern.matcher(statement);
//replace all values between quotes with MATCH%d
int i = 0;
while (matcher.find()) {
String match = matcher.group(1);
String newName = Definitions.defaultQuotesReplacementName + String.valueOf(i++);
replaceQuotesMapping.put(newName, match);
statement = statement.replace(match, newName);
}
return statement;
}
/**
* replace MATCH1 with 'value1' etc.
* replace (topRec[timestamp] or 0) == (topRec[localtimestamp] or 0) with topRec[timestamp] == topRec[localtimestamp]
*
* @param replaceQuotesMapping it stores {MATCH1: 'event/start ...}
* @param statement example: field1 is gt field2
* @return postprocessed statements
*/
public String postProcessStatementQuotes(Map replaceQuotesMapping, String statement) {
statement = statement.replaceAll("(?i)CONDITION\\(\\)", "\\(load\\('return '.. runTimeCondition, '', 't', \\{topRec=topRec, string=string\\}\\)\\)\\(\\)"); //replace condition(): with code to run runtime condition.
if (replaceQuotesMapping.size() > 0)
for (Map.Entry entry : replaceQuotesMapping.entrySet())
statement = statement.replace(entry.getKey(), entry.getValue());
statement = statement.replaceAll("!=", "~=")
.replaceAll("< =", "<=")
.replaceAll("> =", ">=")
.replaceAll("(?i)topRec\\[\"null\"\\]", "nil");
return statement;
}
String processField(String field) {
for (String recordFieldTransformation : Definitions.recordFieldTransformations) {
field = field.replaceAll("(?i)(" + recordFieldTransformation + ")\\(\\)", "$1\\(" + nameArg1 + "\\)");
}
return field;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy