com.tangosol.util.QueryHelper Maven / Gradle / Ivy
Show all versions of coherence Show documentation
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.util;
import com.tangosol.coherence.config.ResolvableParameterList;
import com.tangosol.coherence.dslquery.CoherenceQueryLanguage;
import com.tangosol.coherence.dslquery.ExecutionContext;
import com.tangosol.coherence.dslquery.FilterBuilder;
import com.tangosol.coherence.dslquery.Statement;
import com.tangosol.coherence.dsltools.precedence.OPParser;
import com.tangosol.coherence.dsltools.termtrees.Term;
import com.tangosol.coherence.dsltools.termtrees.NodeTerm;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.ConfigurableCacheFactory;
import com.tangosol.net.Session;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* QueryHelper is a utility class that provides a set of factory methods
* used for building instances of {@link Filter} or
* {@link ValueExtractor}.
*
*
* The QueryHelper API accepts a String that specifies the creation of rich
* Filters in a format similar to SQL WHERE
clauses. For example,
* the String "street = 'Main' and state = 'TX'"
will create a
* tree of Filters similar to the following Java code:
*
*
*
* new AndFilter(
* new EqualsFilter("getStreet","Main"),
* new EqualsFilter("getState","TX"));
*
*
*
* The following keywords are currently supported (words between brackets
* are optional):
*
* - Comparison operators:
=, >, >=, <, <=, <> [ NOT ] BETWEEN,
* [ NOT ] LIKE, [ NOT ] IN, IS [ NOT ] NULL , CONTAINS [ ALL | ANY ]
* - Logical operators:
(AND, OR, NOT)
* - Literal numbers, and the constants
true, false
, and
* null
*
*
* Each argument to an operator is converted into a
* {@link com.tangosol.util.extractor.ReflectionExtractor}. Additionally,
* the "."
operator will use
* {@link com.tangosol.util.extractor.ChainedExtractor}. Pseudo functions
* key()
and value()
may be used to specify the use
* of a key as in "key() between 10 and 50"
. The
* value()
pseudo function is a shorthand for
* {@link com.tangosol.util.extractor.IdentityExtractor}.
*
*
* Query bind variables are supported in two forms. One form is a
* "?"
followed by a position number; for example:
* "dept = ?1 and serviceCode in [10,20,30]"
. For this usage,
* supply an Object array of parameters to
* {@link #createFilter(String, Object[])}. Note: this scheme
* treats 1 as the starting index into the array.
*
*
* Additionally, named bind parameters are supported. The above example could
* be: "dept = :deptNum and serviceCode in :myList"
.
* This style requires the use of a Map with
* {@link #createFilter(String, Map)}. Both forms can be used in the
* same specification.
*
*
* The factory methods catch a number of Exceptions from the implementation
* stages and subsequently may throw an unchecked
* {@link FilterBuildingException}.
*
* @author djl 2009.9.3
*/
public final class QueryHelper
{
// ----- constructors ---------------------------------------------------
/**
* No new instances as this is a static helper.
*/
private QueryHelper()
{
}
// ----- FilterBuilder API ----------------------------------------------
/**
* Make a new Filter from the given String.
*
* @param sWhereClause a String in the Coherence Query Language
* representing a Filter
*
* @return the constructed Filter
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static Filter createFilter(String sWhereClause)
{
return createFilter(sWhereClause, new Object[0], new HashMap(), f_language);
}
/**
* Make a new Filter from the given String.
*
* @param sWhereClause a String in the Coherence Query Language
* representing a Filter
* @param aoBindings the array of Objects to use for Bind variables
*
* @return the constructed Filter
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static Filter createFilter(String sWhereClause, Object[] aoBindings)
{
return createFilter(sWhereClause, aoBindings, new HashMap(), f_language);
}
/**
* Make a new Filter from the given String.
*
* @param sWhereClause a String in the Coherence Query Language
* representing a Filter
* @param mapBindings the Map of Objects to use for Bind variables
*
* @return the constructed Filter
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static Filter createFilter(String sWhereClause, Map mapBindings)
{
return createFilter(sWhereClause, new Object[0], mapBindings, f_language);
}
/**
* Make a new Filter from the given String.
*
* @param sWhereClause a String in the Coherence Query Language
* representing a Filter
* @param aBindings the array of Objects to use for Bind variables
* @param mapBindings the Map of Objects to use for Bind variables
*
* @return the constructed Filter
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static Filter createFilter(String sWhereClause, Object[] aBindings, Map mapBindings)
{
return createFilter(sWhereClause, aBindings, mapBindings, f_language);
}
/**
* Make a new Filter from the given String.
*
* @param sWhereClause a String in the Coherence Query Language
* representing a Filter
* @param aBindings the array of Objects to use for indexed Bind variables
* @param mapBindings the Map of Objects to use for named Bind variables
* @param language the CoherenceQueryLanguage instance to use
*
* @return the constructed Filter
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static Filter createFilter(String sWhereClause, Object[] aBindings,
Map mapBindings, CoherenceQueryLanguage language)
{
try
{
FilterBuilder filterBuilder = new FilterBuilder(
aBindings == null ? Collections.emptyList() : Arrays.asList(aBindings),
new ResolvableParameterList(mapBindings), language);
return filterBuilder.makeFilter(parse(sWhereClause));
}
catch (RuntimeException e)
{
throw new FilterBuildingException(e.getMessage(), sWhereClause, e);
}
}
/**
* Make a new ValueExtractor from the given String.
*
* @param s a String in the Coherence Query Language representing
* a ValueExtractor
*
* @return the constructed ValueExtractor
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static ValueExtractor createExtractor(String s)
{
return createExtractor(s, f_language);
}
/**
* Make a new ValueExtractor from the given String.
*
* @param sQuery a String in the Coherence Query Language representing
* a ValueExtractor
* @param language the CoherenceQueryLanguage instance to use
*
* @return the constructed ValueExtractor
*
* @throws FilterBuildingException may be thrown
*/
@SuppressWarnings("rawtypes")
public static ValueExtractor createExtractor(String sQuery, CoherenceQueryLanguage language)
{
try
{
return new FilterBuilder(language).makeExtractor((NodeTerm) parse(sQuery));
}
catch (RuntimeException e)
{
throw new FilterBuildingException(e.getMessage(), sQuery, e);
}
}
/**
* Return the {@link Term} representing the AST produced by
* parsing the specified CohQL query String.
*
* @param sQuery the CohQL query to be parsed
*
* @return the Term representing the AST produced by
* parsing the specified CohQL query String
*/
protected static Term parse(String sQuery)
{
return parse(sQuery, f_language);
}
/**
* Return the {@link Term} representing the AST produced by
* parsing the specified CohQL query String.
* This method takes a CoherenceQueryLanguage parameter which
* allows CohQL customisations to be applied to the CohQL syntax.
*
* @param sQuery the CohQL query to be parsed
* @param language the CoherenceQueryLanguage instance to use
*
* @return the Term representing the AST produced by
* parsing the specified CohQL query String
*/
protected static Term parse(String sQuery, CoherenceQueryLanguage language)
{
OPParser parser = new OPParser(sQuery, language.filtersTokenTable(), language.getOperators());
return parser.parse();
}
/**
* Execute a CohQL statement. This method accepts a complete query
* string as the argument. The type of object returned depends on the query:
*
* Examples on CohQL
*
* Return Type
* Query Description
*
*
* Cache entry value
* If the statement is an INSERT
operation
* the previous entry for the given key will be returned; otherwise null
*
*
* {@link java.lang.Number}
* If the query is an aggregation that returns a single numerical
* value; for example: count()
, avg()
,
* max()
, min()
*
*
* {@link java.util.Map}
* If query has a grouping
*
*
* {@link java.util.Collection}
* For all other SELECT
, DELETE
,
* UPDATE
statements
*
*
* null
* If the query returns no results or for
* create/delete
index or drop/create
cache,
* or backup/restore
*
*
*
* @param sStatement a Coherence Query Language statement
*
* @return the query results
*
* @throws RuntimeException if an invalid query is provided
*/
public static Object executeStatement(String sStatement)
{
PrintWriter out = new PrintWriter(System.out);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
ExecutionContext ctx = createExecutionContext(CacheFactory.getConfigurableCacheFactory());
ctx.setWriter(out);
ctx.setReader(in);
ctx.setCluster(CacheFactory.ensureCluster());
return executeStatement(sStatement, ctx);
}
/**
* Create a new {@link ExecutionContext}. The following properties will be set on the returned context:
*
* - silent mode is enabled
* - sanity checking is enabled
* - cache factory based on provided argument
*
*
* @param factory the {@link ConfigurableCacheFactory} the {@link ExecutionContext} should use
*
* @return a new {@link ExecutionContext}
*
* @throws IllegalArgumentException if {@code factory} is {@code null}
*
* @since 21.06
*/
public static ExecutionContext createExecutionContext(ConfigurableCacheFactory factory)
{
if (factory == null)
{
throw new IllegalArgumentException("must specify a CacheFactory");
}
ExecutionContext ctx = createCommonExecutionContext();
//noinspection deprecation
ctx.setCacheFactory(factory);
return ctx;
}
/**
* Create a new {@link ExecutionContext}. The following properties will be set on the returned context:
*
* - silent mode is enabled
* - sanity checking is enabled
* - session based on provided argument
* - extended language support enabled
*
*
* @param session the {@link Session} the {@link ExecutionContext} should use
*
* @return a new {@link ExecutionContext}
*
* @throws IllegalArgumentException if {@code session} is {@code null}
*
* @since 21.06
*/
public static ExecutionContext createExecutionContext(Session session)
{
if (session == null)
{
throw new IllegalArgumentException("must specify a Session");
}
ExecutionContext ctx = createCommonExecutionContext();
ctx.setSession(session);
return ctx;
}
/**
* Create a new {@link ExecutionContext}. The following properties will be set on the returned context:
*
* - silent mode is enabled
* - sanity checking is enabled
* - extended language support enabled
* - the coherence query language
*
*
* @return a new {@link ExecutionContext}
*
* @throws IllegalArgumentException if {@code session} is {@code null}
*
* @since 21.06
*/
protected static ExecutionContext createCommonExecutionContext()
{
ExecutionContext ctx = new ExecutionContext();
ctx.setSilentMode(true);
ctx.setSanityCheckingEnabled(true);
ctx.setExtendedLanguage(true);
ctx.setCoherenceQueryLanguage(f_language);
return ctx;
}
/**
* Execute a CohQL statement. This method accepts a complete query
* string as the argument. The type of object returned depends on the query:
*
* Query return types
*
* Return Type
* Query Description
*
*
* Cache entry value
* If the statement is an INSERT
operation
* the previous entry for the given key will be returned; otherwise null
*
*
* {@link java.lang.Number}
* If the query is an aggregation that returns a single numerical
* value; for example: count()
, avg()
,
* max()
, min()
*
*
* {@link java.util.Map}
* If query has a grouping
*
*
* {@link java.util.Collection}
* For all other SELECT
, DELETE
,
* UPDATE
statements
*
*
* null
* If the query returns no results or for
* create/delete
index or drop/create
cache,
* or backup/restore
*
*
*
* @param sStatement a Coherence Query Language statement
* @param context the {@link ExecutionContext} to use
*
* @return the query results
*
* @throws RuntimeException if an invalid query is provided
*/
public static Object executeStatement(String sStatement, ExecutionContext context)
{
if (sStatement == null || sStatement.isEmpty())
{
return null;
}
return createStatement(sStatement, context).execute(context).getResult();
}
/**
* Creates a new {@code Coherence Query Language} {@link Statement} from the provided query string.
*
* @param sStatement a Coherence Query Language statement
* @param context the {@link ExecutionContext} to use
*
* @return the parsed {@code Coherence Query Language} statement ready for execution
*
* @throws RuntimeException if an invalid query is provided
*
* @see #createStatement(String, ExecutionContext, Object[])
* @see #createStatement(String, ExecutionContext, Map)
*
* @since 21.06
*/
public static Statement createStatement(String sStatement, ExecutionContext context)
{
return createStatement(sStatement, context, null, null);
}
/**
* Creates a new {@code Coherence Query Language} {@link Statement} from the provided query string.
* This query string may contain zero or more parameters in the format of {@code ?{n}} where {@code {n}}
* is a numeric value indicating the position of the parameter in the provided argument array.
*
* @param sStatement a Coherence Query Language statement
* @param context the {@link ExecutionContext} to use
* @param oaPositionalParams the positional parameter values
*
* @return the parsed {@code Coherence Query Language} statement ready for execution
*
* @throws RuntimeException if an invalid query is provided
*
* @see #createStatement(String, ExecutionContext)
* @see #createStatement(String, ExecutionContext, Map)
*
* @since 21.06
*/
public static Statement createStatement(String sStatement, ExecutionContext context, Object[] oaPositionalParams)
{
return createStatement(sStatement, context, oaPositionalParams, null);
}
/**
* Creates a new {@code Coherence Query Language} {@link Statement} from the provided query string.
* This query string may contain zero or more parameters in the format of {@code :{key}} where {@code {key}}
* is maps to a key in the provided binding parameters.
*
* @param sStatement a Coherence Query Language statement
* @param context the {@link ExecutionContext} to use
* @param mapBindingParams a {@code Map} keyed by the parameter name within the query string mapped
* to the binding value
*
* @return the parsed {@code Coherence Query Language} statement ready for execution
*
* @throws RuntimeException if an invalid query is provided
*
* @see #createStatement(String, ExecutionContext)
* @see #createStatement(String, ExecutionContext, Object[])
*
* @since 21.06
*/
public static Statement createStatement(String sStatement,
ExecutionContext context,
Map mapBindingParams)
{
return createStatement(sStatement, context, null, mapBindingParams);
}
/**
* Creates a new {@code Coherence Query Language} {@link Statement} from the provided query string.
*
* @param sStatement a Coherence Query Language statement
* @param context the {@link ExecutionContext} to use
* @param aoPositionalParams parameters, in order, to be applied to query
* @param mapBindingParams parameters, mapped to binding names
*
* @return the parsed {@code Coherence Query Language} statement ready for execution
*
* @throws RuntimeException if an invalid query is provided
*
* @see #createStatement(String, ExecutionContext)
* @see #createStatement(String, ExecutionContext, Object[])
* @see #createStatement(String, ExecutionContext, Map)
*
* @since 21.06
*/
protected static Statement createStatement(String sStatement,
ExecutionContext context,
Object[] aoPositionalParams,
Map mapBindingParams)
{
if (sStatement == null || sStatement.isEmpty())
{
return null;
}
CoherenceQueryLanguage language = context.getCoherenceQueryLanguage();
OPParser parser = context.instantiateParser(new StringReader(sStatement));
Term term = parser.parse();
List