/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.kie.workbench.common.services.refactoring.backend.server.query;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.IntFunction;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.search.TotalHitCountCollector;
import org.drools.workbench.models.datamodel.util.PortablePreconditions;
import org.jboss.errai.bus.server.annotations.Service;
import org.kie.workbench.common.services.refactoring.backend.server.query.response.ResponseBuilder;
import org.kie.workbench.common.services.refactoring.backend.server.query.standard.FindAllChangeImpactQuery;
import org.kie.workbench.common.services.refactoring.model.index.terms.valueterms.ValueBranchNameIndexTerm;
import org.kie.workbench.common.services.refactoring.model.index.terms.valueterms.ValueIndexTerm;
import org.kie.workbench.common.services.refactoring.model.index.terms.valueterms.ValueProjectNameIndexTerm;
import org.kie.workbench.common.services.refactoring.model.index.terms.valueterms.ValueProjectRootPathIndexTerm;
import org.kie.workbench.common.services.refactoring.model.query.RefactoringPageRequest;
import org.kie.workbench.common.services.refactoring.model.query.RefactoringPageRow;
import org.kie.workbench.common.services.refactoring.service.RefactoringQueryService;
import org.kie.workbench.common.services.refactoring.service.impact.QueryOperationRequest;
import org.uberfire.ext.metadata.backend.lucene.LuceneConfig;
import org.uberfire.ext.metadata.backend.lucene.index.LuceneIndexManager;
import org.uberfire.ext.metadata.model.KObject;
import org.uberfire.ext.metadata.search.ClusterSegment;
import org.uberfire.paging.PageResponse;
import static org.uberfire.ext.metadata.backend.lucene.util.KObjectUtil.toKObject;
@Service
@ApplicationScoped
public class RefactoringQueryServiceImpl implements RefactoringQueryService {
private LuceneConfig config;
private NamedQueries namedQueries;
private PageResponse<RefactoringPageRow> emptyResponse;
public RefactoringQueryServiceImpl() {
//Make proxyable
}
@Inject
public RefactoringQueryServiceImpl( @Named("luceneConfig") final LuceneConfig config,
final NamedQueries namedQueries ) {
this.config = PortablePreconditions.checkNotNull( "config", config );
this.namedQueries = PortablePreconditions.checkNotNull( "namedQueries", namedQueries );
}
@PostConstruct
public void init() {
emptyResponse = new PageResponse<RefactoringPageRow>();
emptyResponse.setPageRowList( Collections.<RefactoringPageRow>emptyList() );
emptyResponse.setStartRowIndex( 0 );
emptyResponse.setTotalRowSize( 0 );
emptyResponse.setLastPage( true );
emptyResponse.setTotalRowSizeExact( true );
}
public Set<String> getQueries() {
return namedQueries.getQueries();
}
@Override
public PageResponse<RefactoringPageRow> query( final RefactoringPageRequest request ) {
PortablePreconditions.checkNotNull( "request",
request );
final String queryName = PortablePreconditions.checkNotNull( "queryName",
request.getQueryName() );
NamedQuery namedQuery = namedQueries.findNamedQuery( queryName );
//Validate provided terms against those required for the named query
namedQuery.validateTerms(request.getQueryTerms());
final Query query = namedQuery.toQuery( request.getQueryTerms() );
final int pageSize = request.getPageSize();
final int startIndex = request.getStartRowIndex();
final List<KObject> kObjects
= search(query,
(hits) -> ( (startIndex + 1) * ( pageSize > 0 ? pageSize : 1 ) ), // num docs to get
(collector) -> (collector.topDocs( startIndex ).scoreDocs), // get docs starting at startIndex
(numHits) -> ( numHits > pageSize ? pageSize : numHits ) // num docs to add to response
);
if( ! kObjects.isEmpty() ) {
final ResponseBuilder responseBuilder = namedQuery.getResponseBuilder();
return responseBuilder.buildResponse( pageSize,
startIndex,
kObjects );
} else {
return emptyResponse;
}
}
@Override
public List<RefactoringPageRow> query( final String queryName, final Set<ValueIndexTerm> queryTerms ) {
PortablePreconditions.checkNotNull( "queryName",
queryName );
PortablePreconditions.checkNotNull( "queryTerms",
queryTerms );
NamedQuery namedQuery = namedQueries.findNamedQuery( queryName );
//Validate provided terms against those required for the named query
namedQuery.validateTerms(queryTerms);
final Query query = namedQuery.toQuery( queryTerms );
final List<KObject> kObjects
= search(query,
(hits) -> (hits), // num docs to get
(collector) -> (collector.topDocs().scoreDocs), // get all docs
(numHits) -> (numHits) // num docs to add to response
);
if( ! kObjects.isEmpty() ) {
final ResponseBuilder responseBuilder = namedQuery.getResponseBuilder();
return responseBuilder.buildResponse( kObjects );
} else {
return Collections.emptyList();
}
}
private List<KObject> search(final Query query,
final IntFunction<Integer> totalHitsSupplier,
final Function<TopScoreDocCollector, ScoreDoc[]> getDocsHitFunction,
final IntFunction<Integer> numDocsInResponseFunction,
final ClusterSegment... clusterSegments) {
final LuceneIndexManager indexManager = ( (LuceneIndexManager) config.getIndexManager() );
final IndexSearcher index = indexManager.getIndexSearcher( clusterSegments );
final List<KObject> result = new ArrayList<KObject>();
try {
final TotalHitCountCollector totalHitCountCollector = new TotalHitCountCollector();
index.search( query,
totalHitCountCollector );
int numHits = totalHitCountCollector.getTotalHits();
if( numHits > 0 ) {
final TopScoreDocCollector topScoreDocCollector = TopScoreDocCollector.create( totalHitsSupplier.apply(numHits) );
index.search( query, topScoreDocCollector );
final ScoreDoc[] docsHit = getDocsHitFunction.apply(topScoreDocCollector);
int numDocs = numDocsInResponseFunction.apply(docsHit.length);
for ( int i = 0; i < numDocs; i++ ) {
result.add( toKObject( index.doc( docsHit[ i ].doc ) ) );
}
}
} catch ( final Exception ex ) {
throw new RuntimeException( "Error during Query!",
ex );
} finally {
indexManager.release( index );
}
return result;
}
/* (non-Javadoc)
* @see org.kie.workbench.common.services.refactoring.service.RefactoringQueryService#queryToPageResponse(org.kie.workbench.common.services.refactoring.service.impact.QueryOperationRequest)
*/
@Override
public PageResponse<RefactoringPageRow> queryToPageResponse(QueryOperationRequest queryOpRequest) {
final RefactoringPageRequest request = convertToRefactoringPageRequest(queryOpRequest);
final PageResponse<RefactoringPageRow> response = query( request );
return response;
}
@Override
public List<RefactoringPageRow> queryToList( final QueryOperationRequest queryOpRequest ) {
final RefactoringPageRequest request = convertToRefactoringPageRequest(queryOpRequest);
final List<RefactoringPageRow> response = query( request.getQueryName(), request.getQueryTerms() );
return response;
}
private RefactoringPageRequest convertToRefactoringPageRequest(QueryOperationRequest refOpRequest) {
RefactoringPageRequest request = new RefactoringPageRequest(
FindAllChangeImpactQuery.NAME,
new HashSet<>(),
refOpRequest.getStartRowIndex(),
refOpRequest.getPageSize());
request.getQueryTerms().addAll(refOpRequest.getQueryTerms());
// add project info
String projectName = refOpRequest.getProjectName();
if( projectName != null && projectName != QueryOperationRequest.ALL ) {
ValueProjectNameIndexTerm valueIndexTerm = new ValueProjectNameIndexTerm(projectName);
Set<ValueIndexTerm> queryTerms = new HashSet<ValueIndexTerm>(1);
queryTerms.add(valueIndexTerm);
request.getQueryTerms().addAll(queryTerms);
}
String projectRootPathURI = refOpRequest.getProjectRootPathURI();
if( projectRootPathURI != null && projectRootPathURI != QueryOperationRequest.ALL ) {
ValueProjectRootPathIndexTerm valueIndexTerm = new ValueProjectRootPathIndexTerm( projectRootPathURI );
Set<ValueIndexTerm> queryTerms = new HashSet<ValueIndexTerm>(1);
queryTerms.add(valueIndexTerm);
request.getQueryTerms().addAll(queryTerms);
}
String branchName = refOpRequest.getBranchName();
if( branchName != null && branchName != QueryOperationRequest.ALL ) {
ValueBranchNameIndexTerm valueIndexTerm = new ValueBranchNameIndexTerm(branchName);
Set<ValueIndexTerm> queryTerms = new HashSet<ValueIndexTerm>(1);
queryTerms.add(valueIndexTerm);
request.getQueryTerms().addAll(queryTerms);
}
return request;
}
}