CQL Field Module

Available:

Confluence 5.9 and later

Purpose of this module

The CQL Field Module allows plugins to extend the Confluence Query Language to add additional fields.Field Modules interact with other plugin modules, to produce v2 search queries which map to Lucene queries using a Search Query Mapper Module Lucene Index that has been extracted with an Extractor Module.

CQL Field Modules also provide UI support capabilities, allowing fields added by plugins to be presented to users via the UI in components of the application built on top of CQL, for example in the search screen.

Your plugin must depend on the CQL SPI in order to declare CQL fields or functions. Please see the guide Adding a field to CQL for a detailed walkthrough

CQL Field declaration

Here's an example atlassian-plugin.xml declaring a new CQL field:

<atlassian-plugin name="Sample" key="confluence.cql.sample.field">
    ...
    <cql-query-field fieldName="created"
               key="created-date-field" name="Created Date CQL Field"
               class="com.atlassian.confluence.plugins.cql.fields.CreatedDateFieldHandler">
        <ui-support value-type="date"
                    i18n-key="cql.field.created"
                />
    </cql-query-field>
    ...
</atlassian-plugin>

com.atlassian.querylang.fields.BaseFieldHandler
  • the class attribute defines the field handler implementation class. This class must extend com.atlassian.querylang.fields.BaseFieldHandler and implement one of:

    • EqualityFieldHandler
    • TextFieldHandler
    • DateTimeFieldHandler, or
    • NumericFieldHandler.

  • the ui-support declaration provides information to allow Confluence to render the new field in the UI:
    • The value-type attribute specifies the data type of the new field.
    • The i18n-key attribute specifies a key which will be used to provide translated labels for your field when it is presented in the UI.
    • An optional data-uri attribute to allow the plugin to specify string values which will be used to populate the UI.

Supported UI Value Types

Value Type Example
contentId "With Title" Field
contentType "Of Type" Field
date "Created Date" Field
label "Label" Field
space "In Space" Field
user "Mentioning User" Field
string "Status" Field, used in conjuction with a data-uri, see Adding a field to CQL

CQL Field types

The field types determine the syntax of the value and the operations that can be used in an expression.  Field handlers must implement at least one of four interfaces, however there are some existing helper classes that provide partial implementation.

Field type Supported operations Interface
Text =, !=, IN, NOT IN, ~, !~ TextFieldHandler
Equality =, !=, IN, NOT IN
EqualityFieldHandler
DateRange =, !=, >, >=, <, <= DateTimeFieldHandler
Number =, !=, >, >=, <, <=

NumericFieldHandler

An example Field Handler implementation

Here's an example Field Handler implementation. 

package com.atlassian.confluence.plugins.cql.fields;

import javax.annotation.Nullable;

import com.atlassian.confluence.plugins.cql.spi.v2searchhelpers.V2SearchQueryWrapper;
import com.atlassian.confluence.plugins.cql.spi.v2searchhelpers.V2SearchSortWrapper;
import com.atlassian.confluence.search.lucene.DocumentFieldName;
import com.atlassian.confluence.search.v2.BooleanOperator;
import com.atlassian.confluence.search.v2.SearchQuery;
import com.atlassian.confluence.search.v2.query.TextFieldQuery;
import com.atlassian.confluence.search.v2.sort.TitleSort;
import com.atlassian.querylang.fields.BaseFieldHandler;
import com.atlassian.querylang.fields.EqualityFieldHandler;
import com.atlassian.querylang.fields.TextFieldHandler;
import com.atlassian.querylang.fields.expressiondata.EqualityExpressionData;
import com.atlassian.querylang.fields.expressiondata.SetExpressionData;
import com.atlassian.querylang.fields.expressiondata.TextExpressionData;
import com.atlassian.querylang.query.FieldOrder;
import com.atlassian.querylang.query.OrderDirection;

import static com.atlassian.querylang.fields.expressiondata.SetExpressionData.Operator.IN;
import static com.atlassian.querylang.fields.expressiondata.SetExpressionData.Operator.NOT_IN;
import static com.atlassian.querylang.fields.expressiondata.EqualityExpressionData.Operator.EQUALS;
import static com.atlassian.querylang.fields.expressiondata.EqualityExpressionData.Operator.NOT_EQUALS;
import static com.atlassian.querylang.fields.expressiondata.TextExpressionData.Operator.CONTAINS;
import static com.atlassian.querylang.fields.expressiondata.TextExpressionData.Operator.NOT_CONTAINS;

import com.google.common.base.Function;
import static com.google.common.collect.Sets.newHashSet;

import static com.atlassian.confluence.plugins.cql.spi.v2searchhelpers.V2FieldHandlerHelper.joinSingleValueQueries;
import static com.atlassian.confluence.plugins.cql.spi.v2searchhelpers.V2FieldHandlerHelper.wrapV2Search;

public class TitleTextFieldHandler extends BaseFieldHandler implements TextFieldHandler<V2SearchQueryWrapper>, EqualityFieldHandler<String, V2SearchQueryWrapper>
{
    private static final String FIELD_NAME = "title";

    public TitleTextFieldHandler()
    {
        super(FIELD_NAME, true);
    }

    @Override
    public V2SearchQueryWrapper build(TextExpressionData expressionData, String value)
    {
        validateSupportedOp(expressionData.getOperator(), newHashSet(CONTAINS, NOT_CONTAINS));
        return wrapV2Search(new TextFieldQuery(DocumentFieldName.TITLE, value, BooleanOperator.AND), expressionData);
    }

    @Override
    public FieldOrder buildOrder(OrderDirection direction)
    {
        return new V2SearchSortWrapper(new TitleSort(V2SearchSortWrapper.convertOrder(direction)));
    }

    @Override
    public V2SearchQueryWrapper build(SetExpressionData expressionData, Iterable<String> values)
    {
        validateSupportedOp(expressionData.getOperator(), newHashSet(IN, NOT_IN));
        SearchQuery query = joinSingleValueQueries(values, new Function<String, TextFieldQuery>(){
            @Override
            public TextFieldQuery apply(@Nullable String value)
            {
                return createEqualityQuery(value);
            }
        });
        return wrapV2Search(query, expressionData);
    }

    @Override
    public V2SearchQueryWrapper build(EqualityExpressionData expressionData, String value)
    {
        validateSupportedOp(expressionData.getOperator(), newHashSet(EQUALS, NOT_EQUALS));
        return wrapV2Search(createEqualityQuery(value), expressionData);
    }

    private TextFieldQuery createEqualityQuery(String value)
    {
        return new TextFieldQuery(DocumentFieldName.CONTENT_NAME_UNTOKENIZED, "\""+value+"\"", BooleanOperator.AND);
    }

}

FieldHandler.fieldName Method

Returns the name of the field that this handler handles, this field name will be used in CQL expressions.

FieldHandler.isOrderSupported Method

Returns true if this field supports being part of an ORDER BY clause.  If this is true, the handler must implement the buildOrder method.

FieldHandler.getFieldMetaData Method

Returns a FieldMetaData object describing the CQL field. Metadata includes a flag to indicate if the field is an alias of another field, and if the field has UI support.

FieldHandler.buildOrder Method

Builds and returns a FieldOrder object which is used to sort search results. The FieldOrder object contains the name of the field to which the order applies and an OrderDirection either ascending or descending.  If the field does not support ordering, BaseFieldHandler provides an appropriate default implementation.

BaseFieldHandler Class

The BaseFieldHandler class provides a partial FieldHandler implementation from which others should be extended. It implements both the FieldHandler interface as well as the SubFieldHandlerProvider interface which allows for the definition of CQL subfields for example user.fullname = 'Joe Bloggs', user.userkey = 'jbloggs'

<FieldType>FieldHandler.build Method

The interfaces for each of the field types (Text, Equality, DateTime and Numeric) define a build method which accepts as arguments an ExpressionData object as well as either a single value or Iterable of values. The method returns a V2SearchQueryWrapper object, which as the name suggests, is an object designed to wrap a V2 Search Query and the object and determines which data is queried from the index and returned as CQL search results when a field is used. 

ExpressionData.getOperator Method

Returns the CQL operator used in the query, allowing the FieldHandler to return the appropriate V2 Search Query Wrapped object based on the operator. For example, the CQL query "space = 'MYSPACE'", will yield a result of EQUALS when the EqualityExpressionData.getOperator method is called. 


Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport