You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by Apache Wiki <wi...@apache.org> on 2015/10/11 21:16:00 UTC

[Metamodel Wiki] Update of "UserDefinedFunctions" by KasperSorensen

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Metamodel Wiki" for change notification.

The "UserDefinedFunctions" page has been changed by KasperSorensen:
https://wiki.apache.org/metamodel/UserDefinedFunctions

Comment:
Added page about UDFs

New page:
= User Defined Functions (UDFs) =

Apache MetaModel allows for extending the query language using User Defined Functions. In fact, the functions that are built into Apache MetaModel are also "just" built-in UDFs.

Functions in MetaModel come in two forms:

 * Aggregate functions, which implement the org.apache.metamodel.query.AggregateFunction interface.
 * Scalar functions, which implement the org.apache.metamodel.query.ScalarFunction interface.

== Scalar functions ==

A scalar function is a function that provides a result on each row of the dataset that it is applied to. For instance, the built-in function TO_NUMBER will convert each value to a java.lang.Number instead of some other data type.

Normally you can apply the Scalar function TO_NUMBER like this:

{{{#!java
Query q = dataContext.query().from(table).select(FunctionType.TO_NUMBER, "id").toQuery();
}}}

If you dig into FunctionType.TO_NUMBER you will find out that it is simply an object that implements ScalarFunction.
Let's imagine you wanted to implement instead now a hash code function. We can implement it like this:

{{{#!java
public class HashCodeFunction implements ScalarFunction {

    @Override
    public ColumnType getExpectedColumnType(ColumnType type) {
        return ColumnType.INTEGER;
    }

    @Override
    public String getFunctionName() {
        return "HASH_CODE";
    }

    @Override
    public Object evaluate(Row row, SelectItem operandItem) {
        Object value = row.getValue(operandItem);
        return value == null ? null : value.hashCode();
    }
}
}}}

As you can see the implementation part here is pretty easy. We need only to provide a name, a data type and implement the evaluate(...) method. Now to apply the function to our query:

{{{#!java
Query q = dataContext.query().from(table).select(new HashCodeFunction(), "id").toQuery();
}}}

== Aggregate functions ==

Aggregate functions are used to make calculations which span multiple rows of the dataset. Typically used on a complete dataset or in combination with a GROUP BY condition.

In a similar way to scalar functions, you can also implement your own aggregate functions. Let's say we wanted to implement a DISTINCT_COUNT function (ie. a count of distinct/unique values), we could do it like this:

{{{#!java
public class DistinctCountFunction implements AggregateFunction {

    @Override
    public String getFunctionName() {
        return "DISTINCT_COUNT";
    }

    @Override
    public ColumnType getExpectedColumnType(ColumnType type) {
        return ColumnType.INTEGER;
    }

    @Override
    public Object evaluate(Object... values) {
        AggregateBuilder<?> aggregateBuilder = createAggregateBuilder();
        for (Object value : values) {
            aggregateBuilder.add(value);
        }
        return aggregateBuilder.getAggregate();
    }

    @Override
    public AggregateBuilder<?> createAggregateBuilder() {
        return new AggregateBuilder<Integer>() {
            
            private Set<Object> uniqueSet = new HashSet<>();
            
            @Override
            public void add(Object o) {
                uniqueSet.add(o);
            }

            @Override
            public Integer getAggregate() {
                return uniqueSet.size();
            }
        };
    }
}
}}}

(you may choose to extend DefaultAggregateFunction which will save you the effort of implementing evaluate(...))

And again you can apply your function in a query like this:

{{{#!java
Query q = dataContext.query().from(table).select(new DistinctCountFunction(), "type").toQuery();
}}}