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

ma.vi.esql.parser.macro.Bin Maven / Gradle / Ivy

/*
 * Copyright (c) 2020 Vikash Madhow
 */

package ma.vi.esql.parser.macro;

import ma.vi.esql.function.Function;
import ma.vi.esql.function.FunctionParameter;
import ma.vi.esql.parser.Context;
import ma.vi.esql.parser.Esql;
import ma.vi.esql.parser.Macro;
import ma.vi.esql.parser.TranslationException;
import ma.vi.esql.parser.expression.*;
import ma.vi.esql.type.Types;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import static java.lang.String.valueOf;
import static org.apache.commons.lang3.StringUtils.leftPad;

/**
 * Given a value and a variadic array of intervals, returns the range
 * where the value fits. E.g.
 * 

* bin(15, 'x', 1,5,12,20,35,67) returns '12 <= x < 19' *

* bin is macro which is expanded to a case statement: E.g. *

 *   bin(age, 'age', 1,2,5) expands to:
 *
 *      '01. age < 1'         if age < 1         else
 *      '02. 1 <= age < 2' if 1 <= age < 2 else
 *      '03. 2 <= age < 5' if 2 <= age < 5 else '04. age >= 5'
 *
 *   which is then translated to a database-specific CASE expression.
 * 
* * The interval values can be expressions but they must be in correct order, * i.e., from smallest to largest. Otherwise the case expansion will be wrong. * * @author Vikash Madhow ([email protected]) */ public class Bin extends Function implements Macro { public Bin() { super("bin", Types.TextType, Arrays.asList(new FunctionParameter("val", Types.TextType), new FunctionParameter("name", Types.TextType), new FunctionParameter("intervals", Types.TextType))); } @Override public boolean expand(String name, Esql esql) { FunctionCall call = (FunctionCall)esql; Context ctx = call.context; List> args = call.arguments(); if (args.size() < 3) { throw new TranslationException("bin requires at least 3 arguments: the value to bin, a human-readable " + "name of the value to bin, and at least 1 value defining the intervals of " + "the bin range. E.g: bin(x, 'age', 1, 5, 10) will produce bins: " + "age < 1, 1 <= age < 5, 5 <= age < 10 and age >= 10" ); } int order = 2; List> cases = new ArrayList<>(); Iterator> i = args.iterator(); Expression binVar = i.next(); Expression varName = i.next(); Expression lower = null; Expression upper = i.next(); while (upper != null) { if (lower == null) { cases.add(new Concatenation(ctx, Arrays.asList( new StringLiteral(ctx, "01. "), (Expression)varName, new StringLiteral(ctx, " < "), new Cast(ctx, upper, Types.StringType)))); cases.add(new LessThan(ctx, (Expression)binVar, (Expression)upper)); } lower = upper; upper = i.hasNext() ? i.next() : null; if (upper == null) { cases.add(new Concatenation(ctx, Arrays.asList( new StringLiteral(ctx, leftPad(valueOf(order), 2, '0') + ". "), (Expression)varName, new StringLiteral(ctx, " >= "), new Cast(ctx, lower, Types.StringType)))); } else { cases.add(new Concatenation(ctx, Arrays.asList( new StringLiteral(ctx, leftPad(valueOf(order), 2, '0') + ". "), new Cast(ctx, lower, Types.StringType), new StringLiteral(ctx," <= "), (Expression)varName, new StringLiteral(ctx," < "), new Cast(ctx, upper, Types.StringType)))); cases.add(new Range(ctx, (Expression)lower, "<=", (Expression)binVar, "<", (Expression)upper)); order += 1; } } call.parent.replaceWith(name, new Case(ctx, cases)); return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy