/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.core.search.query; import java.util.Collection; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilteredQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.facet.FacetBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.enonic.cms.core.content.index.ContentIndexQuery; import com.enonic.cms.core.content.index.ContentIndexQueryExprParser; import com.enonic.cms.core.content.index.queryexpression.CompareExpr; import com.enonic.cms.core.content.index.queryexpression.Expression; import com.enonic.cms.core.content.index.queryexpression.FieldExpr; import com.enonic.cms.core.content.index.queryexpression.LogicalExpr; import com.enonic.cms.core.content.index.queryexpression.NotExpr; import com.enonic.cms.core.content.index.queryexpression.OrderByExpr; import com.enonic.cms.core.content.index.queryexpression.QueryExpr; import com.enonic.cms.core.search.IndexException; import com.enonic.cms.core.search.facet.FacetBuilderFactory; import com.enonic.cms.core.search.query.factory.FilterQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.FullTextQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.InQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.LikeQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.OrderQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.RangeQueryBuilderFactory; import com.enonic.cms.core.search.query.factory.TermQueryBuilderFactory; import com.enonic.cms.core.structure.menuitem.MenuItemKey; import com.enonic.cms.store.dao.ContentTypeDao; @Component public class QueryTranslator { private final FilterQueryBuilderFactory filterQueryBuilderFactory; private final OrderQueryBuilderFactory orderQueryBuilderFactory; private final TermQueryBuilderFactory termQueryBuilderFactory; private final RangeQueryBuilderFactory rangeQueryBuilderFactory; private final LikeQueryBuilderFactory likeQueryBuilderFactory; private final InQueryBuilderFactory inQueryBuilderFactory; private final FullTextQueryBuilderFactory fullTextQueryBuilderFactory; private final FacetBuilderFactory facetBuilderFactory; @Autowired private ContentTypeDao contentTypeDao; public QueryTranslator() { filterQueryBuilderFactory = new FilterQueryBuilderFactory(); orderQueryBuilderFactory = new OrderQueryBuilderFactory(); termQueryBuilderFactory = new TermQueryBuilderFactory(); rangeQueryBuilderFactory = new RangeQueryBuilderFactory(); likeQueryBuilderFactory = new LikeQueryBuilderFactory(); inQueryBuilderFactory = new InQueryBuilderFactory(); fullTextQueryBuilderFactory = new FullTextQueryBuilderFactory(); facetBuilderFactory = new FacetBuilderFactory(); } public SearchSourceBuilder build( final ContentIndexQuery contentIndexQuery ) { return doBuildSearchSource( contentIndexQuery, contentIndexQuery.getCount() ); } public SearchSourceBuilder build( final ContentIndexQuery contentIndexQuery, int count ) { return doBuildSearchSource( contentIndexQuery, count ); } private SearchSourceBuilder doBuildSearchSource( final ContentIndexQuery contentIndexQuery, int count ) { final QueryExpr queryExpr = applyFunctionsAndDateTranslations( contentIndexQuery ); final SearchSourceBuilder builder = new SearchSourceBuilder(); builder.from( contentIndexQuery.getIndex() ); builder.size( count ); final Expression expression = queryExpr.getExpr(); final QueryBuilder builtQuery; builtQuery = buildQuery( expression ); applySorting( builder, contentIndexQuery, queryExpr.getOrderBy() ); doAddFilters( contentIndexQuery, builder, builtQuery ); doAddFacets( contentIndexQuery, builder ); //System.out.println( builder.toString() ); return builder; } private void doAddFilters( final ContentIndexQuery contentIndexQuery, final SearchSourceBuilder builder, final QueryBuilder builtQuery ) { final FilterBuilder filtersToApply = filterQueryBuilderFactory.buildFilter( contentIndexQuery ); FilteredQueryBuilder filterQueryBuilder = new FilteredQueryBuilder( builtQuery, filtersToApply ); builder.query( filterQueryBuilder ); } private void doAddFacets( final ContentIndexQuery contentIndexQuery, final SearchSourceBuilder builder ) { final Collection<FacetBuilder> facetBuilders = facetBuilderFactory.buildFacetBuilder( contentIndexQuery ); for ( FacetBuilder facetBuilder : facetBuilders ) { builder.facet( facetBuilder ); } } private void applySorting( SearchSourceBuilder builder, ContentIndexQuery contentIndexQuery, OrderByExpr orderByExpr ) { final MenuItemKey orderBySection = contentIndexQuery.getOrderBySection(); if ( orderBySection != null ) { orderQueryBuilderFactory.buildOrderBySection( builder, orderBySection ); } else { orderQueryBuilderFactory.buildOrderByExpr( builder, orderByExpr ); } } private QueryExpr applyFunctionsAndDateTranslations( final ContentIndexQuery contentIndexQuery ) { return ContentIndexQueryExprParser.parse( contentIndexQuery, false, contentTypeDao ); } private QueryBuilder buildQuery( final Expression expr ) { if ( expr == null ) { return QueryBuilders.matchAllQuery(); } if ( expr instanceof CompareExpr ) { return buildCompareExpr( (CompareExpr) expr ); } if ( expr instanceof LogicalExpr ) { return buildLogicalExpr( (LogicalExpr) expr ); } if ( expr instanceof NotExpr ) { return buildNotExpr( (NotExpr) expr ); } throw new QueryTranslatorException( expr.getClass().getName() + " expression not supported" ); } private QueryBuilder buildCompareExpr( final CompareExpr expr ) { final String path = QueryFieldNameResolver.resolveQueryFieldName( (FieldExpr) expr.getLeft() ); final QueryField queryField = QueryFieldFactory.resolveQueryField( path ); final QueryValue[] queryValues = QueryValueFactory.resolveQueryValues( expr.getRight() ); final QueryValue querySingleValue = queryValues.length > 0 ? queryValues[0] : null; final QueryFieldAndValue queryFieldAndValue = new QueryFieldAndValue( queryField, querySingleValue ); final int operator = expr.getOperator(); switch ( operator ) { case CompareExpr.EQ: return termQueryBuilderFactory.buildTermQuery( queryFieldAndValue ); case CompareExpr.NEQ: return buildNotQuery( termQueryBuilderFactory.buildTermQuery( queryFieldAndValue ) ); case CompareExpr.GT: return rangeQueryBuilderFactory.buildRangeQuery( queryField, querySingleValue, null, false, true ); case CompareExpr.GTE: return rangeQueryBuilderFactory.buildRangeQuery( queryField, querySingleValue, null, true, true ); case CompareExpr.LT: return rangeQueryBuilderFactory.buildRangeQuery( queryField, null, querySingleValue, true, false ); case CompareExpr.LTE: return rangeQueryBuilderFactory.buildRangeQuery( queryField, null, querySingleValue, true, true ); case CompareExpr.LIKE: return likeQueryBuilderFactory.buildLikeQuery( queryFieldAndValue ); case CompareExpr.NOT_LIKE: return buildNotQuery( likeQueryBuilderFactory.buildLikeQuery( queryFieldAndValue ) ); case CompareExpr.IN: return inQueryBuilderFactory.buildInQuery( queryField, queryValues ); case CompareExpr.NOT_IN: return buildNotQuery( inQueryBuilderFactory.buildInQuery( queryField, queryValues ) ); case CompareExpr.FT: return fullTextQueryBuilderFactory.buildFulltextQuery( path, querySingleValue ); } return null; } private QueryBuilder buildNotExpr( final NotExpr expr ) { final QueryBuilder negated = buildQuery( expr.getExpr() ); return buildNotQuery( negated ); } private QueryBuilder buildLogicalExpr( final LogicalExpr expr ) { final QueryBuilder left = buildQuery( expr.getLeft() ); final QueryBuilder right = buildQuery( expr.getRight() ); if ( expr.getOperator() == LogicalExpr.OR ) { return QueryBuilders.boolQuery().should( left ).should( right ); } else if ( expr.getOperator() == LogicalExpr.AND ) { return QueryBuilders.boolQuery().must( left ).must( right ); } else { throw new IllegalArgumentException( "Operation [" + expr.getToken() + "] not supported" ); } } private QueryBuilder buildNotQuery( final QueryBuilder negated ) { return QueryBuilders.boolQuery().must( QueryBuilders.matchAllQuery() ).mustNot( negated ); } }