/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/agpl.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.streamsource.infrastructure.index.elasticsearch; import org.elasticsearch.action.count.CountRequestBuilder; import org.elasticsearch.action.count.CountResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.index.query.AndFilterBuilder; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.OrFilterBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.qi4j.api.Qi4j; import org.qi4j.api.common.Optional; import org.qi4j.api.composite.Composite; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.injection.scope.This; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.query.grammar.*; import org.qi4j.api.specification.Specification; import org.qi4j.api.util.Function; import org.qi4j.api.util.Iterables; import org.qi4j.api.value.ValueComposite; import org.qi4j.spi.Qi4jSPI; import org.qi4j.spi.query.EntityFinder; import org.qi4j.spi.query.EntityFinderException; import org.qi4j.spi.query.NamedEntityFinder; import org.qi4j.spi.query.NamedQueryDescriptor; import org.qi4j.spi.value.ValueDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import static org.elasticsearch.index.query.FilterBuilders.*; import static org.elasticsearch.index.query.QueryBuilders.*; /** * Back ported from Qi4j 2.0 * * courtesy of Paul Merlin */ @Mixins( {ElasticSearchFinder.Mixin.class, ElasticSearchFinder.NamedEntityFinderMixin.class} ) public interface ElasticSearchFinder extends EntityFinder, NamedEntityFinder { class Mixin implements EntityFinder { private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchFinder.class); @This private ElasticSearchSupport support; @Structure Qi4jSPI qi4j; public Iterable<EntityReference> findEntities( Class<?> resultType, BooleanExpression whereClause, OrderBy[] orderBySegments, Integer firstResult, Integer maxResults ) throws EntityFinderException { // Prepare request SearchRequestBuilder request = support.client().prepareSearch( support.index() ); AndFilterBuilder filterBuilder = baseFilters( resultType ); //QueryBuilder queryBuilder = processWhereSpecification( filterBuilder, whereClause ); QueryBuilder queryBuilder = new ElasticSearchQueryParserImpl().getQueryBuilder( filterBuilder, whereClause ); request.setQuery( filteredQuery( queryBuilder, filterBuilder ) ); if ( firstResult != null ) { request.setFrom( firstResult ); } if ( maxResults != null ) { request.setSize( maxResults ); } else { request.setSize( Integer.MAX_VALUE ); // TODO Use scrolls? } if ( orderBySegments != null ) { for ( OrderBy order : orderBySegments ) { FieldSortBuilder sortBuilder = new FieldSortBuilder(order.propertyReference().propertyName()); sortBuilder.order( order.order() == OrderBy.Order.ASCENDING ? SortOrder.ASC : SortOrder.DESC ); sortBuilder.ignoreUnmapped( true ); sortBuilder.missing( "_first" ); request.addSort( sortBuilder ); } } // Log LOGGER.debug( "Will search Entities: {}", request ); // Execute SearchResponse response = request.execute().actionGet(); return Iterables.map(new Function<SearchHit, EntityReference>() { public EntityReference map(SearchHit from) { return EntityReference.parseEntityReference(from.id()); } }, response.getHits()); } public EntityReference findEntity( Class<?> resultType, BooleanExpression whereClause ) throws EntityFinderException { // Prepare request SearchRequestBuilder request = support.client().prepareSearch( support.index() ); AndFilterBuilder filterBuilder = baseFilters( resultType ); //QueryBuilder queryBuilder = processWhereSpecification( filterBuilder, whereClause ); QueryBuilder queryBuilder = new ElasticSearchQueryParserImpl().getQueryBuilder( filterBuilder, whereClause ); request.setQuery( filteredQuery( queryBuilder, filterBuilder ) ); request.setSize( 1 ); // Log LOGGER.debug( "Will search Entity: {}", request ); // Execute SearchResponse response = request.execute().actionGet(); if ( response.getHits().totalHits() == 1 ) { return EntityReference.parseEntityReference( response.getHits().getAt( 0 ).id() ); } return null; } public long countEntities( Class<?> resultType, BooleanExpression whereClause ) throws EntityFinderException { // Prepare request CountRequestBuilder request = support.client().prepareCount( support.index() ); AndFilterBuilder filterBuilder = baseFilters( resultType ); //QueryBuilder queryBuilder = processWhereSpecification( filterBuilder, whereClause ); QueryBuilder queryBuilder = new ElasticSearchQueryParserImpl().getQueryBuilder( filterBuilder, whereClause ); request.setQuery( filteredQuery( queryBuilder, filterBuilder ) ); // Log LOGGER.debug( "Will count Entities: {}", request ); // Execute CountResponse count = request.execute().actionGet(); return count.getCount(); } private static AndFilterBuilder baseFilters( Class<?> resultType ) { return andFilter( termFilter("_types", resultType.getName()) ); } } class NamedEntityFinderMixin implements NamedEntityFinder { private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchFinder.class); @This private ElasticSearchSupport support; public Iterable<EntityReference> findEntities(NamedQueryDescriptor descriptor, String resultType, @Optional Map<String, Object> variables, @Optional OrderBy[] orderBySegments, @Optional Integer firstResult, @Optional Integer maxResults) throws EntityFinderException { return null; } public EntityReference findEntity(NamedQueryDescriptor descriptor, String resultType, Map<String, Object> variables) throws EntityFinderException { SearchRequestBuilder request = support.client().prepareSearch( support.index() ); request.setQuery( descriptor.compose( variables,null,null,null ) ); request.setSize( 1 ); // Log LOGGER.debug( "Will search Entity: {}", request ); // Execute SearchResponse response = request.execute().actionGet(); if ( response.getHits().totalHits() == 1 ) { return EntityReference.parseEntityReference( response.getHits().getAt( 0 ).id() ); } return null; } public long countEntities(NamedQueryDescriptor descriptor, String resultType, @Optional Map<String, Object> variables) throws EntityFinderException { return 0; //To change body of implemented methods use File | Settings | File Templates. } public String showQuery(NamedQueryDescriptor descriptor) { return descriptor.compose(null,null,null,null); } } }