Interface QueryApi
QueryAPI
Query API is a Groovy API available in all logics to query the data from Pricefx.Its goal is to provide unified access to data in Pricefx and long term wise it should be the only API for querying data.
The main entrance point for use of QueryAPI is SandboxAPI.queryApi()
that returns a QueryApi
interface.
This interface provides access to a set of methods which you will need for querying. Let’s look at this query which
reads a single row with sku = “MB-0001”
from the Product master table:
def qapi = api.queryApi()
def p = qapi.tables().products()
return qapi.source(p, p.sku().equal("MB-0001"))
.stream { it.toList() }
Pipeline based data queries
Query api provides a pipeline based data query approach. Contrary to SQL which provides a declarative language that describe the data query in a single (but sometime complex) statement, pipeline based data queries gives the user the ability describe his query as a sequence of data transformation stages.Stages can be seen as a data transformation node that takes a table as input and provides a table as output.
Like regular database tables, a table is a concrete or virtual (table view) data set structured by named columns and composed of data rows.
There exists two kind of stages
- Source stages: which are the starting stage of every pipelined query, their source is a concrete database table. Their output is a subset of theirs rows and columns provided as a table view
- Transformation stages: which are transforming the output of a previous stage, getting optionally a concrete databases table as additional input and providing a table view as output
Here is an example of representation of a pipelined query with data example:
[ SourceStage: table=Products, columns=[Product.sku, Product.label]] (output) -> ------------------ | sku | label | ------------------ | S1 | foo | -> | S2 | bar | -> | S3 | egg | | S4 | spam | ------------------ -> (input) [ InnerJoinStage: table=Costs, columns=[Costs.sku], criteria=(Costs.sku = input.sku) ] (output) -> ------------------------- | sku | label | Cost | ------------------------- | S1 | foo | 1O | -> | S2 | bar | 15 | -> | S3 | egg | 5 | | S4 | spam | 20 | ------------------------- -> (input) [ FilterStage: filter=input.Cost > 10 ] (output) ->* ------------------------- | sku | label | Cost | ------------------------- -> | S2 | bar | 15 | | S4 | spam | 20 | ------------------------- -> (input) [ SortByStage: order=descending(input.Cost) ] (output) -> ------------------------- | sku | label | Cost | ------------------------- -> | S4 | spam | 20 | | S2 | bar | 15 | -------------------------
As we can see each stage can only refer to its input columns (which is the previous stage output) and if it is
available a concrete source table (used here in SourceStage
and JoinStage
).
The above example is implemented with QueryApi as follows:
def qapi = api.queryApi()
def p = qapi.tables().products()
def c = qapi.tables().productExtensions("Costs")
def query = qapi.source(p, [p.sku(), p.label()])
.innerJoin(c, { input -> [c.Cost] }, { input -> input.sku.equal(c.sku()) })
.filter { input -> input.Cost.greaterThan(10) }
.sortBy { input -> [qapi.orders().descendingNullsLast(input.Cost)] }
As shown in this code snippet, each stage can refer to columns
- of a concrete table when it is belonging to its arguments. This is the case of
- the source stage which uses
p
to refer to the concrete product table columns[...]source(p, [p.sku(), p.label()])
- the join stage which uses
c
to refer to the concrete costs table columns[...]innerJoin(c, { input -> [c.Cost] }, { input -> input.sku.equal(c.sku()) })
- the source stage which uses
- of the table view it gets as input. To refer to these columns the client code should provide a function
getting a
Tables.Columns
object which give access to this input columns. In the above example this is the case for the last three stages of the query pipeline.
Tables.Columns
objects are immutable map associating columns
names to their reference. As groovy supports the .
operator to access the entry of a map, columns can be accessed
directly with it, like {input -> input.MyColumnName}
By default, columns defined from a concrete source table have the same name as the accessor used to get it. For example,
qapi.tables().products().sku()
will lead to a column named "sku"
. This name can be anyway changed
using Expression.as(String)
.
Executing the query and fetching the data
Once a data pipeline has been defined, the user can run the corresponding query and fetch its results usingPipelineStage.stream(Function)
. This method gets a function as argument which will be in charge of consuming
the result stream. This PipelineStage.ResultStream
is an
Iterable
of PipelineStage.ResultStream.ResultRow
which are giving access to the result rows data in the same way columns are provided when adding pipeline stages,
i.e. using an immutable map view indexed by column names.
def qapi = api.queryApi()
def p = qapi.tables().products()
def allSkuSize = 0
return qapi.source(p, [p.sku()])
.stream { it.sum {
row -> row.sku.size()
}}*
- Since:
- 14.0 - Caribou Lou
-
Method Summary
Modifier and TypeMethodDescriptionexprs()
Provides expression building methodsorders()
Provides order statement building methodssource
(Tables.Table table) Main entry point to start writing a query.source
(Tables.Table table, List<? extends Selectable> selectables) Main entry point to start writing a query.source
(Tables.Table table, List<? extends Selectable> selectables, Expression filter) Main entry point to start writing a query.tables()
Provides access to reference of all availableTables.Table
.
-
Method Details
-
source
Main entry point to start writing a query.You must specify
- a source Table
- a list of columns or aliased expressions made up from the source Table
- a filter expression that specify the row of the table to be fetched
The following example fetches the
sku
andcreateDate
columns of each row of the master data Product table having label containing "foo"def qapi = api.queryApi() def p = qapi.tables().products() return qapi.source(p, [p.sku(), p.createDate()], p.label().like("%foo%")) .stream { it.collect { it } }
Note that the result of this method is a
PipelineStage
which can be- either used as input for other data transformation like joins, filter, aggregation, etc. (see all the available methods in
PipelineStage
-
- or used to directly fetch the selected columns or expressions via
PipelineStage.stream(Function)
.
- Parameters:
table
- the table to get the data fromselectables
- anIterable
object containing the columns/expressions to fetch. It must only contain reference to columns coming from the giventable
argument.filter
- a boolean expression that specify the rows to be fetched. It must only contain reference to columns coming from the giventable
argument.- Returns:
- the corresponding pipeline source
- Since:
- 14.0 - Caribou Lou
- See Also:
-
source
Main entry point to start writing a query.Same as
source(Tables.Table, List, Expression)
but without the filter expression. As a result the entire table will be fetched if no subsequentPipelineStage.filter(Function)
stage is applied before the finalPipelineStage.stream(Function)
operation.- Since:
- 14.0 - Caribou Lou
- See Also:
-
source
Main entry point to start writing a query.Same as
source(Tables.Table, List)
but without the specification of the selectable expression. As a result all the available columns of the table will be fetched if no subsequentPipelineStage.retainColumns(Function)
orPipelineStage.removeColumns(Function)
stage is applied before the finalPipelineStage.stream(Function)
operation.- Since:
- 14.0 - Caribou Lou
- See Also:
-
tables
Tables tables()Provides access to reference of all availableTables.Table
.These references are used to query the desired table.
For example:
def tables = api.queryApi().tables() def tProduct = tables.products() def tCosts = tables.productExtension("Costs") def tCountries = tables.companyParameterTable("Countries")
- Returns:
- an object giving access to all the available tables.
- Since:
- 14.0 - Caribou Lou
-
exprs
Exprs exprs()Provides expression building methodsFor example a logic conjuction can be built as follows:
def qapi = api.queryApi() def p = qapi.tables().products() def labelEndsWithFooAndSkuStartsWithBar = qapi.exprs().and(p.label().like("%foo"), p.sku().like("bar%"))
- Returns:
- an object giving access to expression building methods
- Since:
- 14.0 - Caribou Lou
-
orders
Orders orders()Provides order statement building methodsFor example an ascending alphabetical ordering by sku can be built with:
def qapi = api.queryApi() def skuAscendingOrder = api.orders().ascNullsLast(qapi.tables().products().sku())
- Returns:
- an object giving access to order statement building methods
- Since:
- 14.0 - Caribou Lou
-