/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.search.query.factory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.elasticsearch.index.query.AndFilterBuilder;
import org.elasticsearch.index.query.BoolFilterBuilder;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.MissingFilterBuilder;
import org.elasticsearch.index.query.OrFilterBuilder;
import org.elasticsearch.index.query.RangeFilterBuilder;
import org.elasticsearch.index.query.TermFilterBuilder;
import org.elasticsearch.index.query.TermsFilterBuilder;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.MutableDateTime;
import org.joda.time.ReadableDateTime;
import com.google.common.collect.Sets;
import com.enonic.cms.core.content.ContentKey;
import com.enonic.cms.core.content.category.CategoryAccessType;
import com.enonic.cms.core.content.category.CategoryKey;
import com.enonic.cms.core.content.contenttype.ContentTypeKey;
import com.enonic.cms.core.content.index.ContentIndexQuery;
import com.enonic.cms.core.search.query.AbstractQuery;
import com.enonic.cms.core.search.query.QueryFieldAndMultipleValues;
import com.enonic.cms.core.search.query.QueryFieldAndValue;
import com.enonic.cms.core.search.query.QueryFieldFactory;
import com.enonic.cms.core.search.query.QueryFieldNameResolver;
import com.enonic.cms.core.search.query.QueryValue;
import com.enonic.cms.core.security.group.GroupKey;
import com.enonic.cms.core.structure.menuitem.MenuItemEntity;
public class FilterQueryBuilderFactory
extends BaseQueryBuilderFactory
{
public FilterBuilder buildFilter( final ContentIndexQuery contentIndexQuery )
{
final List<FilterBuilder> filtersToApply = getListOfFiltersToApply( contentIndexQuery );
return createFilter( filtersToApply );
}
public FilterBuilder buildFilter( final AbstractQuery query )
{
final List<FilterBuilder> filtersToApply = getListOfFiltersToApply( query );
return createFilter( filtersToApply );
}
private List<FilterBuilder> getListOfFiltersToApply( final ContentIndexQuery query )
{
List<FilterBuilder> filtersToApply = new ArrayList<FilterBuilder>();
if ( query.hasSectionFilter() )
{
filtersToApply.add( buildSectionFilter( query ) );
}
else if ( query.isSectionFilter() )
{
filtersToApply.add( buildAllSectionsFilter( query ) );
}
if ( query.getContentFilter() != null && !query.getContentFilter().isEmpty() )
{
filtersToApply.add( buildContentFilter( query.getContentFilter() ) );
}
if ( query.hasCategoryFilter() )
{
filtersToApply.add( buildCategoryFilter( query.getCategoryFilter() ) );
}
if ( query.hasContentTypeFilter() )
{
filtersToApply.add( buildContentTypeFilter( query.getContentTypeFilter() ) );
}
if ( query.getContentOnlineAtFilter() != null )
{
filtersToApply.add( buildContentPublishedAtFilter( query.getContentOnlineAtFilter() ) );
}
if ( query.hasContentStatusFilter() )
{
filtersToApply.add( buildContentStatusFilter( query.getContentStatusFilter() ) );
}
if ( query.hasSecurityFilter() )
{
filtersToApply.add( buildSecurityFilter( query.getSecurityFilter() ) );
}
if ( query.getCategoryAccessTypeFilter() != null && !query.getCategoryAccessTypeFilter().isEmpty() )
{
final FilterBuilder categoryAccessFilter =
buildCategoryAccessTypeFilter( query.getCategoryAccessTypeFilter(), query.getCategoryAccessTypeFilterPolicy(),
query.getSecurityFilter() );
if ( categoryAccessFilter != null )
{
filtersToApply.add( categoryAccessFilter );
}
}
return filtersToApply;
}
private List<FilterBuilder> getListOfFiltersToApply( final AbstractQuery query )
{
List<FilterBuilder> filtersToApply = new ArrayList<FilterBuilder>();
if ( query.hasCategoryFilter() )
{
filtersToApply.add( buildCategoryFilter( query.getCategoryFilter() ) );
}
if ( query.hasContentTypeFilter() )
{
filtersToApply.add( buildContentTypeFilter( query.getContentTypeFilter() ) );
}
if ( query.hasSecurityFilter() )
{
final FilterBuilder securityFilter = buildSecurityFilter( query.getSecurityFilter() );
filtersToApply.add( securityFilter );
}
return filtersToApply;
}
private FilterBuilder createFilter( final List<FilterBuilder> filtersToApply )
{
if ( filtersToApply.isEmpty() )
{
return null;
}
if ( filtersToApply.size() == 1 )
{
return filtersToApply.get( 0 );
}
else
{
BoolFilterBuilder boolFilterBuilder = FilterBuilders.boolFilter();
for ( FilterBuilder filter : filtersToApply )
{
boolFilterBuilder.must( filter );
}
return boolFilterBuilder;
}
}
private FilterBuilder buildContentPublishedAtFilter( final DateTime dateTime )
{
final ReadableDateTime dateTimeRoundedDownToNearestMinute = toUTCTimeZone( dateTime.minuteOfHour().roundFloorCopy() );
final RangeFilterBuilder publishFromFilter =
FilterBuilders.rangeFilter( QueryFieldFactory.resolveQueryField( PUBLISH_FROM_FIELDNAME ).getFieldNameForDateQueries() ).lte(
dateTimeRoundedDownToNearestMinute );
final MissingFilterBuilder publishToMissing =
FilterBuilders.missingFilter( QueryFieldFactory.resolveQueryField( PUBLISH_TO_FIELDNAME ).getFieldNameForDateQueries() );
final RangeFilterBuilder publishToGTDate =
FilterBuilders.rangeFilter( QueryFieldFactory.resolveQueryField( PUBLISH_TO_FIELDNAME ).getFieldNameForDateQueries() ).gt(
dateTimeRoundedDownToNearestMinute );
final OrFilterBuilder publishToFilter = FilterBuilders.orFilter( publishToMissing, publishToGTDate );
final AndFilterBuilder filter = FilterBuilders.andFilter( publishFromFilter, publishToFilter );
return filter;
}
private ReadableDateTime toUTCTimeZone( final ReadableDateTime dateTime )
{
if ( DateTimeZone.UTC.equals( dateTime.getZone() ) )
{
return dateTime;
}
final MutableDateTime dateInUTC = dateTime.toMutableDateTime();
dateInUTC.setZone( DateTimeZone.UTC );
return dateInUTC.toDateTime();
}
private FilterBuilder buildSecurityFilter( final Collection<GroupKey> groupKeys )
{
return buildTermsFilterForValues( getKeysAsQueryValues( groupKeys ), CONTENT_ACCESS_READ_FIELDNAME );
}
private TermFilterBuilder buildContentStatusFilter( final Integer contentStatus )
{
QueryFieldAndValue queryFieldAndValue = new QueryFieldAndValue( STATUS_FIELDNAME, contentStatus.toString() );
return new TermFilterBuilder( queryFieldAndValue.getFieldName(), queryFieldAndValue.getValue() );
}
private FilterBuilder buildContentTypeFilter( final Collection<ContentTypeKey> contentTypeFilter )
{
return buildTermsFilterForValues( getKeysAsQueryValues( contentTypeFilter ), CONTENTTYPE_KEY_FIELDNAME );
}
private FilterBuilder buildContentFilter( final Collection<ContentKey> contentKeys )
{
return buildTermsFilterForValues( getKeysAsQueryValues( contentKeys ), CONTENTKEY_FIELDNAME );
}
private FilterBuilder buildCategoryFilter( final Collection<CategoryKey> keys )
{
return buildTermsFilterForValues( getKeysAsQueryValues( keys ), CATEGORY_KEY_FIELDNAME );
}
private FilterBuilder buildCategoryAccessTypeFilter( final Collection<CategoryAccessType> categoryAccessTypeFilter,
final ContentIndexQuery.CategoryAccessTypeFilterPolicy policy,
final Collection<GroupKey> securityFilter )
{
// cannot apply category access type filter without security filter
if ( ( categoryAccessTypeFilter == null ) || ( securityFilter == null ) )
{
return null;
}
final String[] groups = new String[securityFilter.size()];
int i = 0;
for ( GroupKey groupKey : securityFilter )
{
groups[i] = groupKey.toString().toLowerCase();
i++;
}
if ( categoryAccessTypeFilter.size() == 1 )
{
CategoryAccessType type = categoryAccessTypeFilter.iterator().next();
return new TermsFilterBuilder( QueryFieldNameResolver.getCategoryAccessTypeFieldName( type ), groups );
}
final boolean must = policy.equals( ContentIndexQuery.CategoryAccessTypeFilterPolicy.AND );
BoolFilterBuilder boolFilterBuilder = FilterBuilders.boolFilter();
for ( CategoryAccessType type : categoryAccessTypeFilter )
{
final TermsFilterBuilder term = new TermsFilterBuilder( QueryFieldNameResolver.getCategoryAccessTypeFieldName( type ), groups );
if ( must )
{
boolFilterBuilder.must( term );
}
else
{
boolFilterBuilder.should( term );
}
}
return boolFilterBuilder;
}
private FilterBuilder buildSectionFilter( final ContentIndexQuery query )
{
final Set<QueryValue> keysAsQueryValues = getSectionKeysAsList( query.getSectionFilter() );
final boolean buildBothApprovedAndUnapproved = !query.isApprovedSectionContentOnly() && !query.isUnapprovedSectionContentOnly();
if ( buildBothApprovedAndUnapproved )
{
return buildBothApprovedAndUnapprovedSectionFilter( keysAsQueryValues );
}
String fieldName = query.isApprovedSectionContentOnly() ? CONTENTLOCATION_APPROVED_FIELDNAME : CONTENTLOCATION_UNAPPROVED_FIELDNAME;
return buildTermsFilterForValues( keysAsQueryValues, fieldName );
}
private FilterBuilder buildBothApprovedAndUnapprovedSectionFilter( final Set<QueryValue> keysAsQueryValues )
{
BoolFilterBuilder boolFilterBuilder = FilterBuilders.boolFilter();
boolFilterBuilder.should( buildTermsFilterForValues( keysAsQueryValues, CONTENTLOCATION_APPROVED_FIELDNAME ) );
boolFilterBuilder.should( buildTermsFilterForValues( keysAsQueryValues, CONTENTLOCATION_UNAPPROVED_FIELDNAME ) );
return boolFilterBuilder;
}
private FilterBuilder buildTermsFilterForValues( final Set<QueryValue> keysAsQueryValues, final String fieldName )
{
final QueryFieldAndMultipleValues queryFieldAndMultipleValues = new QueryFieldAndMultipleValues( fieldName, keysAsQueryValues );
return new TermsFilterBuilder( queryFieldAndMultipleValues.getFieldName(), queryFieldAndMultipleValues.getValues() );
}
private FilterBuilder buildAllSectionsFilter( final ContentIndexQuery query )
{
if ( query.isApprovedSectionContentOnly() )
{
return FilterBuilders.existsFilter( CONTENTLOCATION_APPROVED_FIELDNAME );
}
else if ( query.isUnapprovedSectionContentOnly() )
{
return FilterBuilders.existsFilter( CONTENTLOCATION_UNAPPROVED_FIELDNAME );
}
else
{
return FilterBuilders.matchAllFilter();
}
}
private <T> Set<QueryValue> getKeysAsQueryValues( final Collection<T> keys )
{
Set<QueryValue> queryValues = Sets.newHashSet();
for ( T key : keys )
{
queryValues.add( new QueryValue( key.toString() ) );
}
return queryValues;
}
private Set<QueryValue> getSectionKeysAsList( final Collection<MenuItemEntity> menuItemEntities )
{
Set<QueryValue> menuItemKeysAsString = Sets.newHashSet();
for ( MenuItemEntity entity : menuItemEntities )
{
menuItemKeysAsString.add( new QueryValue( entity.getKey().toString() ) );
}
return menuItemKeysAsString;
}
}