[fleXive] contents can be queried using a SQL-like dialect called FxSQL. The core idea of FxSQL is to provide a flat virtual table that contains all content instances visible to the current user, with full support for [fleXive] data types and multilingualism.
While it is possible to submit queries in plain FxSQL, there are alternatives for Java and Groovy developers in the form of query builders. They handle proper formatting of values and are the preferred way of formulating queries unless you need some of the more esoteric features of FxSQL.
The overall query layout is similar to SQL. Currently queries are always on the virtual table "content" that provides a flat view on all content instances.
SELECT @pk, caption -- FILTER version=live -- WHERE caption LIKE 'test%' -- ORDER BY caption --
We select the
virtual property
| |
Filters work similar to the SQL
| |
The
| |
To control the sorting of the result set, you can specify one or more columns in the
|
The rest of this section contains an enumeration of FxSQL features along with example queries demonstrating that feature.
When search results are displayed to the user, it is often desirable to let the user choose the
displayed columns. For this purpose, the
ResultPreferencesEngine
provides a simple API for retrieving
and setting the preferred column layout based on the result type (e.g. Articles or Images). To
select these user-defined column layouts, add the
@*
virtual property:
SELECT caption, @*
This is the only virtual property that actually expands to multiple columns, so it should appear at the end of the select clause.
To select all available columns, use the wildcard selector
*
.
The wildcard expands to all properties of the content type returned to the user.
When the result rows contain objects of more than one type, only the properties assigned
to the root type are selected (i.e. properties that are defined for all types).
This example selects all properties of the article type, since only articles
(with a caption) will be returned:
SELECT * WHERE #article/title IS NOT NULL
A main feature of the [fleXive] search engine is full-text indexing. By specifying the "*" property in the where clause, you will search across all indexed properties of a content.
SELECT @pk, caption WHERE * = 'test'
Note that the only supported operator for full-text queries is "=" (equals) and that properties have to be enabled for fulltext indexing (See the section called “Property parameters”).
The exact semantics of the fulltext query depend on the underlying database storage:
MySQL supports wildcards and boolean search expressions as described in the MySQL reference manual.
PostgreSQL and H2 currently do not support wildcards.
When specifying a search property by name, you search across all properties of the given name. If you want to search in specific assignments, i.e. only in the address assignment of your contact data type, you have to either specify the assignment ID (prefixed by a # character) or the XPath prefixed by the type and without cardinalities instead of the property name.
Select the
property
caption
and search in the assignment with ID=25 for main street:
SELECT @pk, caption WHERE #25 like 'main street'
Select the
assignment
ARTICLE/TEASER/TEASER_TITLE
where the assignment
ARTICLE/TITLE
starts with Earthquake:
SELECT @pk, #ARTICLE/TEASER/TEASER_TITLE WHERE #ARTICLE/TITLE LIKE 'Earthquake%'
The
virtual property
@permissions
selects the permissions of the calling user on the content instance:
SELECT @pk, @permissions
The column returns instances of
com.flexive.shared.security.PermissionSet
.
When performing a search on a content collection that supports metadata such as
briefcases,
you can select the metadata of an item using the
virtual property
@metadata
.
SELECT @pk, @metadata FILTER briefcase=21
The
@metadata
column returns instances of type
com.flexive.shared.FxReferenceMetaData<FxPK>
.
To obtain the
FxLock
instance associated to a content, selected the
virtual property
@lock
.
SELECT @pk, @lock
When the content instance is not locked, the
@lock
column returns
null.
[fleXive] also supports searching inside the content tree and returns tree paths of contents linked in the tree.
To select all contents attached to a node or its children, use the
IS CHILD OF
condition and specify
the tree node ID (the root node is always 1).
SELECT @pk WHERE IS CHILD OF 1
To include only contents attached directly to a given tree node, use
IS DIRECT CHILD OF
.
SELECT @pk WHERE IS DIRECT CHILD OF 1
To select all tree paths of a content in the tree, use the
virtual property
@path
. The column then
returns a
FxPaths
object that contains all tree paths
including their content captions. The standard search result table renders these objects as
breadcrumbs.
SELECT @path
Note that this also returns contents not linked in the tree, so you may want to limit your search to children of the root node:
SELECT @path WHERE IS CHILD OF 1
Tree nodes have a position field that explicitly specifies the ordering of
nodes in a folder. You can access the node position in the
SELECT
and
ORDER BY
clauses using the
virtual property
@node_position
.
SELECT @pk, @node_position, caption WHERE IS DIRECT CHILD OF 1 -- select all nodes of the root folder ORDER BY @node_position DESC -- invert tree sort order
FxSQL supports date and time functions similar to MySQL for
FxDate
and
FxDateTime
properties. They can be used to select or query against a partial date value.
For example, this query selects all contents created in March 2008:
SELECT created_at WHERE YEAR(created_at) = 2008 AND MONTH(created_at) = 3
Date and time functions may be used both in the
SELECT
and
WHERE
clauses. When a function is used in a column select, it may also be used
for sorting if you specify the column index instead of the name, for example:
SELECT created_at, SECOND(created_at) ORDER BY 2
Function | Description |
---|---|
YEAR() | Selects the year of a date. |
MONTH() | Selects the month of a date, month 1 is january. |
DAY() | Selects the day of the month of a date. |
HOUR() | Selects the hour. This part is only defined for
FxDateTime
objects.
|
MINUTE() | Selects the minute. This part is only defined for
FxDateTime
objects.
|
SECOND() | Selects the second. This part is only defined for
FxDateTime
objects.
|
To explicitly limit the types returned by a query, you can use explicit type conditions. FxSQL also supports sub- and supertype queries.
Condition | Description |
---|---|
typedef = 'IMAGE' | Returns only instances of the Image type. |
typedef >= 'IMAGE' | Returns instances of the Image type or any type derived from image (inclusive subtype match). |
typedef > 'IMAGE' | Returns instances of any type derived from Image, but not Image itself (exclusive subtype match). |
typedef < 'DERIVED_TYPE' | Returns instances of any supertype (parent type) of Derived_Type (exclusive supertype match). |
typedef <= 'DERIVED_TYPE' | Returns instances of Derived_Type or any of its parent types (inclusive supertype match). |
Selecting properties with data type
SelectOne
returns the complete select item. You can also query individual columns as they
are stored in the table
FXS_SELECTLIST_ITEM
,
which is useful for custom sorts (otherwise the result can only be ordered by the system-internal item ID).
Useful column names include
ID, NAME, DATA, COLOR, POS
(for example:
SELECTPROP.POS
to select the item position).
Of course you can select all system properties, like
acl
or
created_by
,
in your query. Some system properties are linked to their backing table.
For example, the
acl
property is linked to the
FXS_ACL
table, so you can select any column in that table using a field suffix:
acl.description
selects the description column, and
acl.color
returns the color code. The most useful field, however, is the virtual
acl.label
field that returns the ACL label in the calling user's language.
Another useful example is to select the user name that created a content, instead of the account ID:
SELECT @pk, created_by.username
The following table contains all system properties that are linked to their backing table, including the most important fields. For a complete field list, please refer to the database schema.
Property (Table) | Fields |
---|---|
acl (FXS_ACL) |
Selects the content ACL. Additional fields include:
|
created_by, modified_by (FXS_ACCOUNTS) |
Selects the user that has created/lastly modified the content
(
created_by
and
modified_by
both have the same fields). Interesting fields include:
|
mandator (FXS_MANDATOR) |
Selects the mandator of the content instance. To select the mandator name, use
mandator.name .
|
step (FXS_WF_STEPS) |
Selects the workflow step of the content instance. You can select the following fields:
|
typedef (FXS_TYPEDEF) |
Selects the type ID of the content instance.
|
Filters work similar to the SQL having clause by providing a post-processing filter mechanism on the results of the actual query. That is, filters are applied every time the search query is executed, and are not stored in the cached result tables. This means that changing filters usually does not invalidate the query cache, whereas changing the WHERE clause always does.
Filters applicable to the [fleXive] content table:
TYPE=type
Return only contents of the given type. You may specify either the type name (e.g. "ARTICLE") or the type ID (e.g. "2").
VERSION=AUTO|MAX|LIVE|ALL
Restricts the Version, AUTO defaults to MAX. If version=live is selected and an object has no live version, it is omitted.
SEARCH_LANGUAGES=languageId[,languageId]?
Restrict results to languages contained in the comma separated list of language IDs.
General filters:
BRIEFCASE=briefcaseId[,briefcaseId]?
Perform search in one or more briefcases. When exactly one briefcase is specified,
the metadata associated with the items can be selected with the column
@metadata
.
IGNORE_CASE=[true|false]
Query case sensitive?
MAX_RESULTROWS=number
Limit number of rows returned.
To control the sort order of the result set, you can specify one or more
columns in the
ORDER BY clause
.
Usually you specify the name of a column previously selected in the
SELECT
clause, but you can also use the 1-based column index. The direction of the sort
(ascending or descending) is set using the
ASC
and
DESC
modifiers, respectively.
The following two queries order the
result by the properties
priority
and
caption
(i.e. first the result is sorted by priority, then by caption), the first
uses named columns, the second specifies the column indices instead:
SELECT @pk, caption, priority ORDER BY priority DESC, caption
SELECT @pk, caption, priority ORDER BY 3 DESC, 2
As described in
the section called “Select user-defined columns”,
@*
expands to whatever columns the user defined for the result content type.
This causes a problem when the result should be sorted manually by one of these columns,
e.g. because the result table has sortable column headers
(as in the result table in the administration GUI): the FxSQL parser has no way
to expand
@*
(because the search has not been submitted yet and thus the content type is unknown),
but yet it must be possible to sort by one of these columns.
For example, this query does not work:
SELECT @* ORDER BY caption DESC -- caption not found in SELECT clause
As a workaround, it is possible to specify otherwise invalid column indices in the
ORDER BY
clause
if
@*
was selected. Of course this may lead to runtime errors if the user-defined columns
are less than the ORDER BY index. In the most probable use case, on-the-fly
sorting of a result table, this is not a problem, since the user
can only sort by columns that have already been rendered.
For example, the following is a valid FxSQL query but relies on the user having defined at least 3 columns for the result type:
SELECT @* ORDER BY 3 DESC -- order by the third column of @*
For testing and documentation purposes you can embed comments in your query. For a standard
line-based SQL comment use
"--
",
for an inline or multiline comment use C-style comments in the form of
"/*...*/
"
SELECT /* inline comment */ id WHERE id > 0 -- conditions
The following operators are supported by FxSQL. Note that not all data types support all operators.
Operator | Short name | Description | [fleXive] Datatypes |
---|---|---|---|
= | equals | Checks for equality | All except FxBinary |
!= | not equals | Checks for inequality | All except FxBinary |
<= | less than | Compares a property or assignment to a given constant value | All numerical and time types |
> | greater than | Compares a property or assignment to a given constant value | All numerical and time types |
<= | less or equal | Compares a property or assignment to a given constant value | All numerical and time types |
>= | greater or equal | Compares a property or assignment to a given constant value | All numerical and time types |
LIKE | string match with wildcards |
Like string equality, but allows the use of the
%
wildcard which matches any string, including the empty string. For example,
"LIKE 'Test%'"
matches all string values that
begin with "Test".
|
FxString |
Currently all conditions in FxSQL queries involve at least one literal value, e.g.
WHERE title = 'test'
. The following table lists all supported literal
value formats and the [fleXive] types they may be applied to.
Type | Example | Description | [fleXive] Datatypes |
---|---|---|---|
String | 'string value' | A simple string value. Single quotes are escaped like in SQL using double quotes,
e.g. 'te''st' is the representation of the string value
te'st .
|
FxString, FxHTML |
Integer | 21 | Any integer number. The valid range depends on the property datatype, e.g. FxNumber (int) or FxLargeNumber (long). | FxNumber, FxLargeNumber, FxSelectOne, FxSelectMany, internal referential datatypes (e.g. ACL, mandator) |
Date | '2008-03-18' | A date value. Note that in current FxSQL you cannot use a date literal
to compare against a DateTime value - you need to write a manual range
condition for the given day instead, e.g.:
created_at > '2008-03-10 00:00:00.000' AND created_at < '2008-03-11
00:00:00.000'
to find contents created on 2008/03/10.
|
FxDate, FxDateRange |
DateTime | '2008-03-18 15:23:05.159' |
A precise time value with millisecond resolution. Note that millisecond resolution
is currently only supported for the system properties
CREATED_AT
and
MODIFIED_AT . For other properties the millisecond part is always 0.
|
FxDateTime, FxDateTimeRange |
Boolean | true | A boolean value (true or false). | FxBoolean |
To ease the pain of having to learn yet another query language, the
SqlQueryBuilder
provides a thin builder interface for FxSQL queries. The query is created
using chained calls to the builder and results in a FxSQL query.
For example:
new SqlQueryBuilder() // .select("@pk", "created_at", "caption") // .type("article") // .orderBy("created_at", SortDirection.DESCENDING) // .getResult(); //
A new query builder is created. | |
We select three columns, the
virtual property
| |
We want to search only for contents of type
| |
Order the result by creation date, return newest articles first
( | |
The
|
A call to
SqlQueryBuilder#getQuery()
returns the actual FxSQL query, for example, the code snippet above built this FxSQL query:
SELECT @pk, created_at, caption WHERE (typedef = 'article') ORDER BY created_at DESC
The basic query condition specifies
a property or assignment whose value will be compared,
a
PropertyValueComparator
specifying the compare
operator (like equals or greater), and
a constant value to compare the content value against, like "5" or "Test". Comparisons between content values are not supported.
SqlQueryBuilder
offers an overloaded, general-purpose
condition()
method for specifying arbitrary conditions,
and custom condition methods for tree queries and content type constraints:
condition(...)
Add a condition to the query. This method is overloaded to support both
assignment and property queries, for the actual method signatures please refer
to the [fleXive] JavaDoc. If you want to apply a function (e.g.
YEAR(prop)
),
you have to use the generic
condition(String, ...)
methods.
isChild(nodeId)
Adds a tree search condition to the query that limits the search to children (direct and indirect) of the given tree node ID. See the section called “Tree Search”.
isDirectChild(nodeId)
Like
isChild
, but includes only direct
children of the given node.
type(contentType)
Adds a content type constraint to the query, i.e. the expression will match only contents of the given type. Note that this is semantically different from a content type filter, which will be covered in the next section.
Query filters provide filtering capabilities on top of the search result. Currently only the type and briefcase filters are supported.
filterType(contentType)
Applies a content type filter on the result. The query conditions are
not modified by this statement, so it can be called after the query building
has been finished, which is not the case for the
type()
condition.
filterBriefcase(briefcaseId)
Searches only inside the contents of the given briefcase. When no query conditions are specified, the briefcase contents are returned.
The
SqlQueryBuilder
provides support for building nested conditions like
caption LIKE 'Test%' AND (created_by = 21 OR created_by = 22)
.
andSub()
,
orSub()
Creates a new sub-query whose conditions will be joined with the
AND
or
OR
operator respectively. After creating a sub-query, you can
issue condition builder calls exactly like in the top level.
The top-level query is opened implicitly and uses the
AND
operator.
closeSub()
Closes a sub-query. The implicitly opened top-level query does not have to be closed.
Example 6.16. Creating a nested query
new SqlQueryBuilder() .select("@pk") .type("article") .orSub() .condition("caption", PropertyValueComparator.EQ, "Test caption") .condition("caption", PropertyValueComparator.EQ, "Another test caption") .closeSub() .getQuery();Result:
SELECT @pk WHERE (typedef = 'article' AND (caption = 'Test caption' OR caption = 'Another test caption'))
When submitting a query directly with the
SqlQueryBuilder
,
miscellaneous search parameters can be set. Note that these parameters are not
part of the FxSQL query, so if you submit the search by yourself, you cannot use
these methods.
setMaxRows(int)
Sets the maximum number of rows returned by the search query. Useful mostly for paged result tables.
setStartRow(int)
Sets the first row of the search result that should be included in the result set. The first row has index 0.
setViewType(ResultViewType)
Sets the view type (list or thumbnails) when displaying the results to the
user. This is only relevant when you select the
@*
property, which will select the user's predefined result columns
and sort settings based on this view type. The view type as well as the
location can also be specified in the constructor.
For Groovy developers, the
GroovyQueryBuilder
offers an even more convenient way of formulating FxSQL queries. Note that this class
(like the rest of Groovy support classes) is still experimental, so not all features may
work as intended.
Currently the Groovy builder supports defining the column selection, nested
property conditions and briefcase filtering. The query builder actually builds
a query tree similar to the JSF search query editor, so the result object is of type
QueryRootNode
. From this class you can get either the final
FxSQL query from the
sqlQuery
property, or the
SqlQueryBuilder
which has been used for building the query from the
queryBuilder
property.
Example 6.17. Specifying a query with the
GroovyQueryBuilder
new GroovyQueryBuilder().select(["@pk", "caption", "@*"]) { filterBriefcase(21) eq("caption", "test") not_empty("filename") or { gt("id", 0) lt("id", 100) } lt("created_at", new Date()) }.sqlQueryResult:
SELECT @pk, caption, @* FILTER briefcase=21 WHERE (CAPTION = 'test' AND FILENAME IS NOT NULL AND (ID > 0 OR ID < 100) AND CREATED_AT < '2008-04-04 11:17:27.886')
The method names (except
select
and
filterBriefcase
)
are the enumeration values of
PropertyValueComparator
, so any comparison supported by the
SqlQueryBuilder
is also supported by the Groovy builder.
When a FxSQL query is submitted to the database, all rows are fetched at once and
returned in a
FxResultSet
object. It contains all result rows within the user-defined limits
(unlike the cursor-approach of JDBC), including miscellaneous
information like the selected column names, or row count information.
There are two ways of accessing the result rows:
The
FxResultSet#getResultRows()
method returns an iterable object that returns
FxResultRow
objects
for each row in the resultset. Take a look at the example demonstrating accessing
the row columns both by index and column name.
Example 6.18. Iterating over a
FxResultSet
FxResultSet result = new SqlQueryBuilder().select("@pk", "caption") .condition("caption", PropertyValueComparator.LIKE, "test caption%") .getResult(); for (FxResultRow row: result.getResultRows()) { assert row.getPk(1).equals(row.getPk("@pk")); assert row.getFxValue(2).equals(row.getFxValue("caption")); }
The result row table can be accessed with
FxResultSet#getRows()
, which returns a
List<Object[]>. This is best when you need indexed
access in constant time and need only the raw result values (usually
FxValue
objects).
The following code iterates over all rows of a result set:
Example 6.19.
Directly accessing the search results using
getRows()
for (int i = 0; i < result.getRowCount(); i++) { for (int j = 0; j < result.getColumnCount(); j++) { System.out.print(result.getRows().get(i)[j] + ' '); } System.out.println(); }
Sometimes you do not want to work with the full result table, but only need
a list of property values, for example primary keys. The
FxResultSet#collectColumn(int)
method projects a single column of the result set to a linear list.
For example, to collect all primary keys of a [fleXive] type you submit
a query against that type, selecting only the
@pk
column, and then project the first result column using
collectColumn
:
final List<FxPK> folderPks = new SqlQueryBuilder().select("@pk").type("FOLDER").getResult().collectColumn(1);